4

The Eindblazen CTF Teaser has just started. I will be attempting a few of the challenges over the course of today. If anyone wants to help out, head over to the Sec.SE CTF team chat room.

See the CTF team announcement for more information.

4

BIN100: Dice Game

What at first glance seemed to be a simple reversing challenge had a sting in its tail which led to me spending far more time than I should have figuring it out.

Running the executable yields a dice game interface, where the user presses return to throw the dice. The value rolled is randomly generated 1-6. To win the game, you have to roll 5 specific numbers in order; 3-1-3-3-7, if at any point you throw an incorrect number the program terminates.

If your first thought at this stage, like mine, is that this space is easily brute forced - why not just let the random rolls hit it eventually - it's worth noting that we might be waiting a while to roll a 7.

So the first stage was just a case of firing up Immunity and pausing the execution at the first dice roll. Stepping through the program quite quickly showed the call to rand() that was being used to generate the rolls.

Immunity with freehand circles

A carefully placed breakpoint at the moment the rand() result was loaded into eax meant that I could replace it with whatever value I wanted.

Command Prompt with freehand circles

Unfortunately, due to what I thought at the time was loop-unrolling, the 5 calls to rand() appear separately, so I had to repeat the process above for each roll offset.

So now we can run the program, and at each dice roll edit the result so that we get 3-1-3-3-7. Too easy, right? Unfortunately this is not enough. Despite rolling the required numbers, the program terminates with a message

[!] It seems you did something wrong :( No flag for you.

And this is where I started wasting time. I assumed that I had not 'synthesized' the dice rolls in exactly the way that was required. I was led to this assumption by the fact that on the final roll my prompt always drew the dice-roll for 3, even though the text underneath read that I had rolled a 7.

The first thing I did was track down the branch that was taking me to the 'something wrong' message, and patched it to reverse its decision.

This resulted in me getting the success message, but a garbled flag. So the 'something wrong' message was right - somehow the flag generation hadn't happened as it should.

Anyway, much head-scratching later, I revisited something I had noticed earlier while browsing the overview in IDA. There were lots of calls to time(). This program was making use of system time somehow.

Closer inspection showed that it was saving a timestamp each time a roll was made and the timestamp for the current roll was being compared to the timestamp for the previous roll.

enter image description here

Essentially, if the time taken between any two consecutive dice rolls was ever greater than 2 seconds, a certain code fragment was executed. This code fragment stomped on the flag generation address, garbling it.

So I patched each of these cmp [var], 2 instructions to read cmp [var], 0x7f. Annddd...

[!] It seems you did something wrong :( No flag for you.

There was one last, glaringly obvious thing I missed:

enter image description here

Again, patching this branch to reverse its decision in Immunity and the flag was presented, ungarbled.

3

The Sec.SE team did a poor but oddly appropriate showing at #69.

CRY100: Espionage

We get two encrypted files, a decryption program, and the password for one of the files. The plaintext for the file we can decrypt is formatted like an email, which suggests that the other may be too. It's likely that the second file begins with From: Vlugge Japie <vl.japie@yahoo.com> or with From: Baron van Neemweggen <b.neemweggen@zmail.com>.

The encryption algorithm is a stream cipher. The keystream is derived from the password as follows:

  • Apply some function h to the password 20000 times to get the first 16 bytes of keystream.
  • Given a chunk k of keystream, the next chunk is h(k || s) where s is some constant string (the length of the message in decimal).

Oh, look: each chunk of keystream only depends on the previous chunk! So if we can guess one chunk, we can decrypt the message starting at that chunk. Earlier we saw that we had a good guess for the first chunk, so let's plug it in. We can reuse the supplied Python code, just remove the initial key generation step from the crypt function.

#!/usr/bin/python2
import hashlib, string, sys
def xor(a, b):
    l = min(len(a), len(b))
    return ''.join([chr(ord(x) ^ ord(y)) for x, y in zip(a[:l], b[:l])])
def h(x):
    x = hashlib.sha256(x).digest()
    x = xor(x[:16], x[16:])
    return x
def crypt0(msg, k):
    out = ''
    for i in xrange(0, len(msg), 16):
        out += xor(msg[i:i+16], k)
        k = h(k + str(len(msg)))
    return out

ciphertext = open('msg002.enc').read().decode('base64')
print crypt0(ciphertext, xor(ciphertext[:16], 'From: Vlugge Jap'))
print crypt0(ciphertext, xor(ciphertext[:16], 'From: Baron van '))

The message is:

From: Vlugge Japie <vl.japie@yahoo.com>
To: Baron van Neemweggen <b.neemweggen@zmail.com>
Subj: Found it!

Boss,

I found some strange code on one of the documents.
Is this what you're looking for?

ebCTF{21bbc4f404fa2057cde2adbf864b5481}

Vlugge Japie

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .