DRG
DRG Online Challenge October 2013 Solution
2013-11-01

We received six submissions to October's challenge. Björn Zettergren overtakes Vytautas Krakauskas this month for turning in a solution in the shortest amount of time, but just barely. We then received submissions from three new players: Benoit Roussille, David Lesperson and Hugh Millard respectively. Rounding out the submissions was another returning player, ZaiRon. With the increased number of submissions, it is difficult to feature just one on the blog, but this week we're giving the honor to Hugh Millard, take it away Hugh...

After extracting the 201310.tar.gz archive I was presented with a folder containing 2 files, found_in_memory and found_on_disk. As is sually the case with unknown files the first thing I tend to do is open them in a hex editor to get a general idea with what type of files I'm dealing with.

I started with found_in_memory for no reason other than it being listed first and Nautilus had identified the file type as a plain text document. Opening this file I was presented with a hexadecimal string inside a b'...'. This string seemed to give no extra info as to what it contained so I decided to take a look at the next file rather than waste any time trying to decrypt the contents of this file.

Up on opening the next file found_on_disk I found it to be a binary file with a few plain text strings towards the end. Among these strings were references to Blowfish suggesting that the first file may be encrypted. More importantly however, towards the end of the file I found the string snake.py. My first thought was that this was compiled python application but having never tried to reverse a python application before I was not entirely sure where to go next.

As I was reversing this on a virtual machine I threw caution to the wind and renamed the file found_on_disk.pyc and ran it with python only to be presented with the message:

RuntimeError: Bad magic number in .pyc file

My initial thought was that maybe it wasn't a pyc file after all but a quick search for the error message showed that the magic number was contained in the first 4 bytes of the file and that this number was changed for each python release. And that a pyc file would only run in the same version of python for which it had been compiled. I tried searching for a list of magic numbers used by python but could not find the number used for this file in any of these list however most seemed out of date.

Time to try something new...

Reassured that the file I was looking at was in fact a pyc file my next thought was "Can I decompile a pyc file?". A quick search later led me to find Decompyle++ a Python byte-code disassembler/decompiler. A quick download and make later I ran the found_on_disk.pyc file through pycdc and was presented with the following code:

==========================================================================

# Source Generated with Decompyle++
# File: found_on_disk.pyc (Python 3.3)

from Crypto.Cipher import Blowfish
from Crypto import Random
from struct import pack
import binascii
bs = Blowfish.block_size
r = b'byJVnn48lJE9gp8sPtDTvTj1dw'
s = []
for x in r:
    s.append(str(x).swapcase())

k = r
u = s
key = r
iv = Random.new().read(bs)
cipher = Blowfish.new(key, Blowfish.MODE_CBC, iv)
plaintext = b''
plen = bs - divmod(len(plaintext), bs)[1]
padding = [
    plen] * plen
padding = pack('b' * plen, *padding)
msg = iv + cipher.encrypt(plaintext + padding)
print(msg)
print(binascii.hexlify(msg))
dmsg = cipher.decrypt(msg)
print(dmsg[len(iv):])

==========================================================================

Having the python code meant that I was no longer tied to the version of python originally used to compile the pyc. A quick glance at the code highlighted a couple of things:

  1. The code is using the Blowfish cipher and the key is included in the code.
  2. The Initialisation Vector (iv) was being filled with random data.

The second point was more troubling. To decrypt anything using Blowfish I needed to know the iv for the encrypted data. Without thinking too much about it at this stage I ran the application and was shown this output:

b'\xdb\x03&\x8b\xfd.! \x0b\xdc\x1e\x1c\xa2\x13\xf9\xe4'
b'db03268bfd2e21200bdc1e1ca213f9e4'
b'\x08\x08\x08\x08\x08\x08\x08\x08'

First thing I noticed about this output was the second line was formatted similar to the text in the found_in_memory file. So I focused my effort on working out how this line was produced which lead me to this section of code:

msg = iv + cipher.encrypt(plaintext + padding)
print(msg)
print(binascii.hexlify(msg))
dmsg = cipher.decrypt(msg)
print(dmsg[len(iv):])

The third line in this block is where the line I was looking for was output. More interesting though is the first line in this block showing how msg is formed:

msg = iv + cipher.encrypt(plaintext + padding)

The plain text is encrypted using Blowfish and appended to the iv used initialise the Blowfish cipher. Assuming that the contents of found_in_memory follows the same format we can recover the iv and decrypt the data.

Now how to decode the message in found_in_memory?

One thing I had noticed was that the last 2 lines also decrypt msg and output the decrypted plain text, so I decided to modify the code to output the hidden message. The modified code is below, changed code is left in comments.

[Editor's note: unhexlify routine split at 60 character boundary to minimize horizontal scrolling.]
==========================================================================

# Source Generated with Decompyle++
# File: found_on_disk.pyc (Python 3.3)

from Crypto.Cipher import Blowfish
from Crypto import Random
from struct import pack
import binascii

# found_in_memory added
found_in_memory =
binascii.unhexlify(b'9f064a541356c9bb6f9f14d1bb1978b554df3cd\
8ace92cfac442992a6db527506bd0d45122687e8e097fd4ca456d839ac37\
670e10a17be566ff5014ab3e9f10ab75ed2d5aa523ff539dd134da957d55\
d3a6df1fe7b0181c63e868830ed1a71d10e4ff689ea43fbd6b91e54f0753\
ef09f6edb61ab55e843312edc5579b3757bb83ff9b337c523b141192e95a\
7c9846ea90eade878f9ae7fc2a1cf502bec094712b4bfbeb0c644bd9c3fc\
62b27f8ae96082c3b0fbdc9d045075cf9ffad5a43a141508f437d211ecda\
107540eecb55612d336cd0517bd2cbb159dcf2aaa0e38b6c3a919d3b1b4a\
d6ab0c0ef3070a9e078e4f0a0e47b2abfbbdf')

bs = Blowfish.block_size
r = b'byJVnn48lJE9gp8sPtDTvTj1dw'
s = []
for x in r:
    s.append(str(x).swapcase())

k = r
u = s
key = r
iv = found_in_memory[:bs] #Random.new().read(bs)
cipher = Blowfish.new(key, Blowfish.MODE_CBC, iv)
plaintext = b''
plen = bs - divmod(len(plaintext), bs)[1]
padding = [
    plen] * plen
padding = pack('b' * plen, *padding)
msg = iv + found_in_memory[bs:] #+ cipher.encrypt(plaintext + padding)
print(msg)
print(binascii.hexlify(msg))
dmsg = cipher.decrypt(msg)
print(dmsg[len(iv):])

==========================================================================

Running this and the hidden message:

The work of Carl Friedrich Gauss who in 1809 used the normal density to predict the locations of astronomical objects brought awareness of the density to a wide scientific audience; in consequence it is also called the Gaussian density.

Thanks to DRG for the challenge. It's always fun to try something new.

For further details on how the challenge was constructed along with notes and write-ups from all challenge players who solved solved the challenge, please see the updated DRG Online Challenge October 2013 page. The newest, DRG Online Challenge November 2013 is now available. Visit the DRG Challenges page for information about all current, future and past challenges.

posted at 7:24 pm | permanent link



About DRG

Apply to DRG

Host a DRG Distro Pod

Insight & Analysis

Tools

Weekend Reads

Challenges

Security Innovation Grant

Mailing lists

DRG PGP public key

Follow us on Twitter Follow DragonResearch on Twitter


Feedback: dragon@dragonresearchgroup.org

Archives: