Last active
August 29, 2015 14:17
-
-
Save stephenR/dc30c1f08a9f860b9df9 to your computer and use it in GitHub Desktop.
ICBM writeup
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From tsuro for Stratum Auhuur | |
==Stage 1== | |
ICBM was a two stage challenge. The first stage was quite easy, there was a | |
network service running, the sample_collector that read a filename from you in a | |
loop (you have to confirm), a number of "samples" n, and then n integers that | |
would be written onto the stack. The number n would be bounds checked that it | |
can't be too big too overflow the buffer. There were two easy vulnerabilities | |
in here, the filename would be passed as the first argument to printf and the | |
buffer could by overflown by giving a negative value. We can use the format | |
string vulnerability to leak memory, first from the stack with %p, then from an | |
absolute address with %s. With that, we leak the base address of the libc via | |
the got.plt and can calculate any other function address since we know the libc | |
version. From there it's an easy buffer overflow exploit. Overwrite the saved | |
instruction pointer with the address of a pop rdi; ret gadget, followed by a | |
pointer to the string "/bin/sh" and then the address of system. | |
==Stage 2== | |
Stage 2 is a service running on the same host and you have access to the binary | |
and test input. The binary includes an AES key + IV, that we're replaced in the | |
version that we get. If you pass the test input to the real binary (listening | |
on a network socket) it will print some ascii art for you. However that ascii | |
art is not part of the original binary. If you reverse it, you'll find out that | |
it mmaps a block of writable and executable memory, reads your binary and AES | |
CBC decrypts it into that block. CBC has the weakness that if you xor a value | |
in one ciphertext block, it will be xored at the same offset in the cleartext of | |
the next block. However, this will corrupt the whole block you did the xor on. | |
Knowing that, we xor the IV byte by byte and check dmesg for an int 3 interrupt | |
(luckily we're on the same machine and have access to dmesg). If this interrupt | |
is triggered, we'll know that we hit 0xcc. So, we can nop it out and try the | |
next byte. We did this manually :), for the first 5 bytes since that's enough | |
for our shellcode. Our shellcode will consist of instructions up to 3 bytes, | |
followed by a jump to the block after the next (skipping one, which will get | |
corrupted by our xor). We can thus control the first 5 bytes of a block and by | |
chaining IV, 1st block, IV, 1st block, ... together, we control enough to | |
execute arbitrary code. So putting in some shellcode will give us the privileges | |
of the service. But: the flag is encrypted. Since we're running out of time, | |
we're panicking a little bit at this point :). But with our new shell, we dump | |
the key from the original binary and replace it in ours. Next: fire it up in the | |
debugger, pass the encrypted flag on stdin and set a breakpoint to the address, | |
where the decrypted code will be executed. Examine that memory and you'll find | |
the encrypted flag, much easier than decrypting it yourself :). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment