castle: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked (uses shared libs), for FreeBSD 8.2, stripped
This service listens in IPv6 on port 7629. When connecting, the service drops its privileges and forks to call the function at offset 0x08049340. This function begins reading a buffer sent to the socket, ending by "EOF\n". The read buffer is then written in a temporary file, created by a call to the mkstemp() function with the "/tmp/castleXXXXXXXX" template. Finally, castle redirects stdin, stdout and stderr to the socket, using the dup2() function and the "/usr/local/bin/sandy" binary is called with "-o <our ipv6> -d -s " and the temporary file as parameters.
In order for it to work properly, castle has to be able to write in the /tmp/ folder. This may be an idea from ddtek to compel the teams to leave at least one writable directory on the filesystem. On our server, we changed rights of the /tmp/ folder so that it belonged to the castle user, the only user able to write in it. That's all for the "castle" binary.
We shall now focus on the "sandy" binary (which was placed in the wrong /usr/local/sbin/ folder on first day of the CTF...) which really contains the exploitable code.
sandy: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked (uses shared libs), for FreeBSD 8.2, stripped
This binary is larger than the previous one: its size is 120Kb while castle's one is 9Kb. sandy begins by scheduling an alarm signal using the alarm() function with 5 seconds as parameter. It then retrieves the passed arguments and checks on them:
- if a file is passed, it is opened, read and stored in a buffer
- if argument "d" is passed, the previous file is deleted
- if argument "o" is passed, the IPv6 socket address is checked
When all arguments are correct, the buffer read from the file is sent to the function at 0x8057030, in charge of parsing it. The parser understands a kind of simplified C language, using the following keywords, operands and functions:
- for, while, switch, case, if, else, return, do
- int, str, length, isnull, type
- fopen, fclose, fprintf, fread, fwrite, fork, socket, listen, accept, fgets, fputs
Polling
The polling sent by ddtek is:
The polling reads a string sent on the socket and, in return, writes what has been read concatenated to the " goodbye\n" string.
Exploit
What first comes to mind is to read and write the token using a combination of the fopen/fread/fwrite/fclose functions. But, this is not all so simple and we note that the fopen() function is limited on purpose and can not be used. However, the fprintf() and fclose() functions do really call their libc counterparts.
The exploit consists in sending to the service a code with a format string vulnerability and to use it. We decided to replace the fclose() pointer in the GOT by an address from the stack on which our shellcode, preceded by a big nopsled, is stored.
- The first two "#" are there to align the format string.
- The shellcode must fork to bypass the signal scheduled by the alarm() function.
Patch
The quick-and-dirty patch consists in replacing the fprintf word in the sandy parser by another random word, or in replacing the fprintf handler by a null function.
Sometimes after having patched sandy, we saw it was still possible to exploit us. In fact, ddtek regularly replaced the sandy binary on the filesystem by the original one, without warning the teams... Could the script in charge of correcting the wrong sandy binary location still have been running?
The castle binary also had to be patched for it to call the corrected sandi binary instead of the original sandy, regularly overwritten.
Assessing the number of valid tokens stolen
Team IPv6 address Stolen flags Period --------------------------------------------------------------------------------------------------- Shellphish dc19:c7f:2011:1::2 33 From 2011-08-06 10:08:09 to 2011-08-06 13:59:44 PLUS@Postech dc19:c7f:2011:2::2 107 From 2011-08-06 10:01:42 to 2011-08-07 13:08:11 Routards dc19:c7f:2011:3::2 - - Hates Irony dc19:c7f:2011:4::2 64 From 2011-08-06 10:03:27 to 2011-08-07 11:53:11 lollersk8ers dc19:c7f:2011:5::2 0 - PPP dc19:c7f:2011:6::2 0 - IV dc19:c7f:2011:7::2 8 From 2011-08-06 15:26:15 to 2011-08-07 12:41:44 VelociROPtors dc19:c7f:2011:8::2 154 From 2011-08-06 10:05:23 to 2011-08-07 13:28:07 European Nopsled dc19:c7f:2011:9::2 132 From 2011-08-06 10:05:42 to 2011-08-07 13:03:18 sutegoma2 dc19:c7f:2011:a::2 17 From 2011-08-06 15:55:56 to 2011-08-06 20:23:54 int3pids dc19:c7f:2011:b::2 118 From 2011-08-06 10:05:52 to 2011-08-06 21:00:44 ACME Pharm dc19:c7f:2011:c::2 119 From 2011-08-06 10:05:48 to 2011-08-07 12:58:05 -------------------------------------------------------------------------------------------------- TOTAL - 752 From 2011-08-06 10:01:42 to 2011-08-07 13:28:07
This is not the real score as the tokens submission system failed several times and we had to submit tokens by hand without using our framework. Next year, our framework will be modified to handle all ddtek system failures ;)
As a consequence, this table sums up the minimum number of validated tokens for this service.
The 8 IV tokens have certainly not been stolen from them as they had redirected services to a virtual machine (not quite fairplay, isn't it? But if ddtek doesn't do anything about it two years in a row, why not take advantage of it?). However, we used this virtual machine as a proxy to attack other teams from the IV network, in order to bypass blacklisting.
Why was blacklisting an issue? I thought someone said that the "from" IPs were anonymized. Were they imperfectly anonymized?
ReplyDelete