Search This Blog

Saturday, August 24, 2019

Some Interesting ROP Techniques

Besides some heap pwning, I've also been playing around with many interesting ROP techniques I have seen from the many new stack problems I have tried.  Previously, I usually relied on syscalls or a ret2libc for the majority of my rop attacks, but now as I encounter more difficult challenges, I have discovered some cool techniques.  I will be discussing some of them here.

1. ret2csu

Sometimes, you can't seem to find any gadgets at all as arguments for a certain function you want to make in the program on a 64 bit pwn problem.  In this scenario, ret2csu comes in handy.  I only encountered this in the final problem of ROP Emporium.  Anyways, ELF binaries can have this: __libc_csu_init.  All the 3 main gadgets for calling conventions (rdi, rsi, rdx) are supposed to be there, although misalignment might be necessary sometimes.  However, returns aren't exactly there immediately afterwards... some program logic will run through with other registers so those need to be set accordingly for the exploit to work.  Most importantly, this instruction probably is there: call QWORD PTR [r12+rbx*8].  If not controlled correctly, a segfault will occur.  It also must not disturb the state of the other registers we have to control.  A call to __init or __fini should be fine in this scenario and your exploit should work.

2. ret2mprotect

We use ROP chains in stack smashing to beat NX.  However, what if allow regions of non-exectuable memory to become executable again?  That way, we can shellcode once again!  My first encounter of this was from HackTheBox's Calamity Box.  Basically, we just need to control three arguments of mprotect: the address of mapped memory, the size, and the flag.  0x7 for the flag makes that region of memory executable.  I usually set that to the entire region of the stack/heap depending which one I am going for.  Then, have the vulnerable program return to shellcode and it will execute!

3. ret2dlresolve

Basically, use this technique when you have no output and no idea about the libc.  This technique was quite difficult for me to learn and I might not provide the best explanation (I learned from these links: link 1, link 2, link 3).  __dl_runtime_resolve is called to resolve addresses of a function when it is first called in a dynamic binary while also updating GOT.  When calling it, the binary passes an argument of the index in the relocation table and a pointer to something called link_map, which is a linked list of loaded libraries.  The entry in the relocation table leads to an index in the symbol table, which leads to an index in the string table regarding the name.  Some additional information before continuing: JMPREL is stores the relocation table, in which each entry corresponds with a symbol, STRTAB stores the strings for the symbols, and SYMTAB holds information for symbols.
JMPREL has this structure:
typedef uint32_t Elf32_Addr ;
 typedef uint32_t Elf32_Word ;
 typedef struct {
 Elf32_Addr r_offset ; /* Address */
 Elf32_Word r_info ; /* Relocation type and symbol index */
 } Elf32_Rel ; //this is the type of entry in this segment
 #define ELF32_R_SYM(val) ((val) >> 8)
 #define ELF32_R_TYPE(val) ((val) & 0xff)

Only relevant field in SYMTAB is the first thing defined in the struct: st_name.  This tells the program the offset of the string in STRTAB.  We can probably craft fake structures in regions of memory where we control (fake entries of JMPREL and SYM structures).  Using dl resolve, which takes the link map and an offset to Elf32_Rel in JMPREL, we can pwn this binary.  No bounds checking is done on the offset; this will allow us to redirect it to our fake structures.  Using this idea, we can force it to give us a nice system(), or any other function; this function must be set up as an argument as the resolver calls this afterwards.  Our fake structures should be like this:
1. make r_offset somewhere writable (normally somewhere in GOT)
2. make the high 24 bits of r_info point to the fake structure for sym elsewhere
3. make the last 8 bits of r_info equal to 0x7 (for this to work, this is necessary)
4. address of the system string (add st_name to STRTAB)

And there you have it... system pwned!

No comments:

Post a Comment