Skip to content

Instantly share code, notes, and snippets.

@stephenR
Last active August 29, 2015 14:17
Show Gist options
  • Save stephenR/dc30c1f08a9f860b9df9 to your computer and use it in GitHub Desktop.
Save stephenR/dc30c1f08a9f860b9df9 to your computer and use it in GitHub Desktop.
ICBM writeup
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