DRG
DRG Online Challenge December 2013 Solution
2014-02-03

We received only one submission for the December challenge. Vytautaus Krakauskas, the standard bearer for which all DRG challenge players will be judged has turned in a correct submission again. By default, but certainly well deserved, bragging rights this month go to Vytautaus Krakauskas, take it away Vytautaus...

To get an acknowledgment, you need to connect from source port 46368, and provide 75025 as an input.

The form page had a comment with Fibonacci sequence "0,1,1,2,3,5..." so I tried entering variuos forms of the next numbers from the sequence but the form was "silent". Then I paid more attention to the wording of the challenge "use F(n)" so I tried using it as a source port with curl, but still no acknowledgment. Tried using hex and swapping the bytes - no answer. Finally wrote a small bash script to try all values which are within a valid port range (n = [1..25]):

url="http://dragonresearchgroup.org/cgi-bin/201312.cgi"
n0=0
n1=1

for ((i=2; i<=25; i++)); do
    n=$((n0+n1))
    curl -o $i.html --local-port $n1 -F "input=$n" -F "Submit=Submit" -F ".action=check_input" "$url"
    n0=$n1
    n1=$n
done

The last reply had a message "Fibonacci sequence submitted correctly".

Thanks as usual :)

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 December 2013 page. The newest, DRG Online Challenge February 2014 is now available. Visit the DRG Challenges page for information about all current, future and past challenges.

posted at 8:17 pm | permanent link



DRG Online Challenge November 2013 Solution
2013-12-02

We received only one submission for the November challenge. Vytautaus Krakauskas must now be acknowledged as the DRG challenge master as he has turned in a correct submission to all of our online challenges thus far. Björn Zettergren, a regular contributor gets a honorable mention. He came within reaching distance, but didn't quite cross the finish line before his alloted time was exhausted. We may have made this sufficiently difficult to have found a bar for our players. We'll be able to better judge after the next challenge as we think we've lowered it enough that most serious players should be able to complete it. By default, but well deserved, we're giving the bragging rights this month to Vytautaus Krakauskas, take it away Vytautaus...

First I entered all zeros as the password to see plain data in case XOR was used. An Second action was to look at the page source code which contained a peculiar comment with IP, port, date and time of the request. Then I resubmitted the same password for a few times to see if the string would change and it did. I looked at the end of the string which was changing and quickly recognized time from the comment. The trick was that the nibbles of each byte were swapped. Also minutes and seconds were in a slightly different format. The first part of the date resembled numbers in ASCII string so I presumed that everything should be a string. If I would enter another password, the date part would change, so I decided to write a small python script and check if it would reveal an obvious key by XOR'ing the encrypted string with the the comment. And that was it, the output (reformatted) was "Your password was: 0000000000000000\x00\x00\x00\x00"

To confirm I used TOR to get a different IP and picked another password:

Password: 000001715badc0de

The comment in HTML code:

<!-- Logged: 94.242.204.74:47614 @ 20131102 18:35:28 -->

And the encoded string:

06b5b5044124f4143434145405a1346554b0410107012000004000402535442580e56553a32383

Python script:

  1. from binascii import unhexlify
  2. def swap(byte):
  3.      a=(byte & 0xf0) >> 4
  4.      b=byte & 0x0f
  5.      return (b << 4) | a
  6. secret=unhexlify('06b5b5044124f414...2580e56553a32383')
  7. text='94.242.204.74:47614 @ 20131102 18:35:28'
  8. sbytes = [ord(x) for x in secret]
  9. tbytes = [ord(x) for x in text]
  10. i=0
  11. for b in sbytes:
  12.      sb = swap(b)
  13.      x = sb ^ tbytes[i]
  14.      print "%02x %02x(%s) %02x(%s)" % (sb, tbytes[i],text[i], x, chr(x))
  15.      i += 1

The output:

60 39(9) 59(Y)
5b 34(4) 6f(o)
5b 2e(.) 75(u)
40 32(2) 72(r)
14 34(4) 20( )
42 32(2) 70(p)
4f 2e(.) 61(a)
41 32(2) 73(s)
43 30(0) 73(s)
43 34(4) 77(w)
41 2e(.) 6f(o)
45 37(7) 72(r)
50 34(4) 64(d)
1a 3a(:) 20( )
43 34(4) 77(w)
56 37(7) 61(a)
45 36(6) 73(s)
0b 31(1) 3a(:)
14 34(4) 20( )
10 20( ) 30(0)
70 40(@) 30(0)
10 20( ) 30(0)
02 32(2) 30(0)
00 30(0) 30(0)
00 31(1) 31(1)
04 33(3) 37(7)
00 31(1) 31(1)
04 31(1) 35(5)
52 30(0) 62(b)
53 32(2) 61(a)
44 20( ) 64(d)
52 31(1) 63(c)
08 38(8) 30(0)
5e 3a(:) 64(d)
56 33(3) 65(e)
35 35(5) 00()
3a 3a(:) 00()
32 32(2) 00()
38 38(8) 00()

That was fun, thank's as usual! :-)

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 November 2013 page. The newest, DRG Online Challenge December 2013 is now available. Visit the DRG Challenges page for information about all current, future and past challenges.

posted at 8:47 pm | permanent link



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



DRG Online Challenge September 2013 Solution
2013-10-01

We received three submissions to September's challenge. Vytautas Krakauskas, our returning champ again delivered a successful solution first. Shortly thereafter, Björn Zettergren, nipping at Vytautas' heels again came back to us with a solution. Last, but certainly not least, newcomer ZaiRoN (an alias we are using as requested) submitted a solution and a very nice write up. Kudos to all players again this month. Its heartening to have gotten all the positive feedback from those that solved, attempted and learned from the challenges. It was also helpful to have a handful of folks immediately ask us if the challenge was solvable when we initially released an incorrect version. This month we feature Vytautas' write up, take it away Vytautas...

It was late in the evening when I saw the challenge for September. To crack the zip as I tried the fzipcrack with the most popular password list from 10,000 Top Passwords. It didn't work. Then I tried to crack it with an English dictionary generated from aspell I had from another case:

    aspell -d en dump master | aspell -l en expand | tr ' ' '\n' > en_words

That didn't work either. Then I let fcrackzip run to brute-force for the night. The next day, the brute-force was still going. I had various ideas of what to try. I decided to try the English dictionary again and to my surprise it quickly found that the password was "infected". Facepalm! Why it didn't work before, I don't know, it must have been late night bugs.

After that it was the time for the android package. Looking at the strings there were a few candidates, but I knew it wouldn't be that easy. After unpacking the package with apktool, I noticed interesting activity declaration, which contained:

    <action android:name="android.provider.Telephony.SECRET_CODE" />
    <data android:scheme="android_secret_code" android:host="374" />

A quick search revealed that it is activated by entering *#*#374#*#* in the dialer app. So I installed the Android SDK and fired up the emulator, but to my frustration the dialer app was not available. It seems that current Jelly Bean v4.3 (installed by default) is for tablets and does not contain the dialer. After installing v4.1 (which was known to be designed for phones and also the one mentioned in the package's manifest) I was able to enter the secret code, but only a blank screen was displayed. DEX strings contained clues, that something was being logged, but watching the logs did not reveal the hidden message, only reassured that "The right answer is definitely not here there be Dragons!"

I quickly checked the ssh cloud page which is loaded by default when opening the application, but I was not able to find anything of interest and quickly moved on.

Then I analyzed the smali code produced by unpacking with the apktool. Line by line I had reconstructed the functionality as a pseudo code. It became apparent that the javascript blob was essential:

    <script>var data = '';var s1 = '2VjcmV0cw';data =
    interfaces.jsFunction(s1);document.write('getting warm. must be dragons nearby');</script>

It was strange that it wasn't shown after entering the secret code. And the definition for the jsFunction() was nowhere to be found. So I thought that it might be intentionally left out and fixated on the string "2VjcmV0cw". It was quite short and apparently non too random. It wasn't of correct length for base64. Other binary-to-text algorithms didn't fit either and the same goes for common text ciphers, which usually do not include numbers. Then I tried some primitive chained xor and what do you know:

    >>> chr(ord('2')^ord('V'))  
    'd'

I thought it is start of some combination of "dragon", but that wasn't the case. Any further xor'ing sooner or later produced non ascii results.

The string "2VjcmV0cw" looked familiar to url shorteners, but none of the ones i knew had such id. Then I've visited the twitter of Jason Ross (his name was in the apk signing certificate) to see what URL shorteners he did use, but again, none of them contained the id.

I was at a loss and decided to look at all the files again more closely. The idea was to check if there was anything hidden in between the fields. I did some unpacking and repacking of files to check if they would differ, but the closest thing was few bytes of zip's padding (created by a tool called zipalign) and an extra field "0xcafe" (used to denote jar).

During this time i was looking for the tools included in Android SDK package, one line in the documentation gave me the idea that something might be wrong:

"The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names."

One of the classes was named "a" which was similar to how ProGuard obfuscates. Furthermore it was the class which was supposed to contain the jsFunction method. This method was invoked dynamically and could be treated as unused code by the ProGuard's static analysis.

I didn't want to bother the challengers with a hunch and I decided to check it for my self. So I wrote my first android app and indeed, my prediction was valid. The app would work while debugging but it would stop working after exporting if ProGuard was active and wasn't explicitly configured to keep the class intact.

With all this data I was confident enough to ask if the challenge was solvable "as is" and a day later I have received the confirmation that there was a mix up.

Once I had the correct file I have noticed that it was a debug version of the application and it contained a lot more classes. But since I knew what to look for, it was only a matter of minutes to locate the missing function and to figure out what it does.

After entering the secret code the the app would log the concatenated string m9Nb3JlU2VjcmV0cw== which is a base64 encoding of "NoMoreSecrets".

Once again it was a great opportunity to learn something new and I am very grateful for that!

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 September 2013 page. The newest, DRG Online Challenge October 2013 is now available. Visit the DRG Challenges page for information about all current, future and past challenges.

posted at 9:48 pm | permanent link



DRG Challenge at FIRST 2013 Summary Recap and Trivia Solutions
2013-09-25

The Dragon Research Group, for the second year running, hosted the DRG Challenge at FIRST 2013. We undertook some architectural changes to the game and we are pleased to report all went swimmingly. Chalk up another successful and fun-filled event. We would like to acknowledge and thank all the attendees who participated as part of a challenge team, but also all those who simply stopped by or hung out at the challenge headquarters to observe the activities. We would especially like to thank the FIRST 2013 program staff, which includes the program committee, steering committee, secretariat, Cisco on-site network team and CAPS LLC for their unflappable support and assistance. Last, but not least, we are eternally grateful to those who helped provide travel support for DRG volunteers, including those employers who allow their DRG volunteers the flexiblility to participate in these sorts of efforts. Without which none of this would have been possible. A special hats off to CERT.br / NIC.br, RSA Security and Team Cymru Research NFP for helping to underwrite travel costs for one or more DRG volunteers.

This time around we implemented a new, automated web-based challenge framework. This new framework, provided a convenient, easy-to-use interface that managed the roll out of each individual challenge as well as kept track of each team's progress, aggregating progress into a main page scoreboard. As a result, we were able to offer many more challenges, but like last year, the competition was heated until the very end. Ironically, the DRG Challenge at FIRST 2013 Scoreboard shows the team named FIRST Team, so-named because they were the first team to register for the challenge, came out on top, and each participant of that team took home an iPad Mini. There were numerous determined and capable players, but the winning team best demonstrated tenancity and skill to come out on top.

As promised to all those that asked, we will begin releasing the challenges and their solutions in a series of blog posts over the coming months, starting with the trivia challenges today. Like all challenge questions, a varying amount of points were awarded based on the estimated difficulty of the challenge. Without further ado, the DRG Challenge at FIRST 2013 Trivia Questions and Answers:

Question: The size of these attacks keep growing, we saw some of the biggest ones within the past few months. (100 points)
Answer: ddos

Question: What is the number one password tried by SSH scanners? (200 points)
Answer: 12345
The DRG SSH Username and Password Authentication Tag Clouds would provide the answer.

Question: A well known weakness in a class of cryptographic functions that was largely theoretical was recently seen in the wild for the first time. What piece of malware used this weakness? (300 points)
Answer: Flame
The trailofbits MD5 collision analysis blog post is worth a look.

Question: An important algorithm was "in the news" over the past year. The hallmark feature of this algorithm involves a function with two phases that is capable of mapping any size input to any size output. (400 points)
Answer: keccak
The winner of the SHA3 competition utilizes so-called "sponge" functions, making it algorithmically different from existing and commonly used hash functions.

Question: The following assembly instruction can be used as an alternative to what popular sequence of commands used by exploit writers? call dword ptr[esp+8]
Answer: pop pop ret
These commands are commonly used by malware writers to bypass the structure exception handler (SEH) when attempting code injection. call dword ptr[esp+8] effectively moves the stack pointer to the same location in memory as pop pop ret.

Stay tuned for the next installment of the DRG Challenge at FIRST 2013 Summary Recap.

posted at 5:32 pm | permanent link



DRG Online Challenge August 2013 Solution
2013-09-09

We received three submissions to August's challenge. Vytautas Krakauskas for the second month running led the way with the first submission and is now unquestionably the reigning challenge champion. Newcomer, Justin Hildreth provided the second and another returning player, last week's featured blog write-up winner Björn Zettergren submitted another exemplary solution. Kudos to all as Vytautas handily discovered the underlying operational requirements to get the binary to run, while Justin, who admits he is "very new to this" impressed us with his enthusiasm and perseverance, and finally Björn again takes us through his efforts demonstrating vigor and thoroughness. This month we feature Justin's write-up, take it away Justin...

This is an outline of my approach and solution to Dragon Research Group's Online Challenge for August 2013. This was my first foray into anything resembling reverse engineering/program analysis. I had a blast and learned a lot - and I hope to learn a great deal more in this area. Throughout this process, I moved back and forth between Windows 7 and Kali Linux environments, though I don't imagine it's necessary to note when I did so in the write-up.

I downloaded the .zip file, which I found to be password protected. I fired up fcrackzip and ran it against the file - feeling a bit silly when it quickly revealed the password to be 123456.

I extracted the file, which was an unrecognized binary file. When I viewed it in a hex editor and browsed through the file, I saw two helpful clues:

  1. .ELF at the beginning of the file, suggesting this may in fact be an ELF file.
  2. A handy message pointing out that this file had been packed with the UPX executable packer.

I ran the strings tool against the binary file, but that turned up little more information.

I used UPX Packer with upx -d to unpack the file, resulting in the ELF file. Examining this file in my hex editor showed quite a few more strings. I ran the program in the terminal, resulting in message: You have 5 seconds. Factors are: %d and %d.

At this point, I tried many things with no success. What immediately occurred to me was to multiply the two factors, and enter the result. When this did not work, I thought perhaps the factors were multiplied together more than just once, and tried multiplying by each factor multiple times, hoping to randomly encounter the solution. I discarded this approach when one of the times I ran the program, one of the factors was 0. When I entered 0, without success, it became apparent that the desired input was not simply some multiple of the given factors. I did find it interesting, that the timer (at least seemed to) reset every time I entered a response. It seemed I could enter responses indefinitely, as long as I never paused longer than the duration of the timer (presumably 5 seconds).

I used objdump to get at the assembly code of the file, and spent a while digging around in there. This however, did not get me very far.

As I stated earlier, this has been my first adventure into anything of this nature. I dusted off the copy of OllyDbg refused to run the file. I was able to open the file in Evan's Debugger (edb), but it didn't seem to run correctly. I could not get it to simply run through the program, and when I tried to step-through the program, it usually got stuck in what seemed like an infinite loop (which I believe I may have seen later in my static analysis). In any event, I was never able to get it to the point in the program where the prompt exists, in order to examine the contents of the stack and registers at that point.

I had been hesitant to use IDA, as I didn't want to feel like I was running a trial, I wanted to stick to tools I could grow into. I'm glad I finally gave in, the free version of IDA will be sufficient for me to grow into for a good, long time I think.

IDA broke things down really nicely, and was a blast to use. However, my knowledge of assembly and C is so limited, that I found myself lost for the most part. I explored as best I could, with IDA up on one screen, Google up on the other, and the Malware Analysis book open on my desk. I managed to limp along enough to find the portion of code responsible for printing "The factors are..." (0804E95D).

Despite knowing the general area in which to root around now, I still was unable to parse what the program was looking for as input here. However, just a little further down, I found something very interesting - a string of sections of code that printed a bunch of single characters.

From here, it was a simple (albeit tedious) task to go through all the code, put together the string of characters listed in hex, and convert them to ASCII. This produced lines that began to reveal the mythic creature I was seeking. I did have some issues at first, as I (foolishly) ignored the loops that created space characters in some places for spacing. Once I went back and added those in, the image became very clear!

So, I never did get the active program to display the dragon image for me, but I was able to retrieve the target information from the code. This has really sparked an interest for me, and I'm excited to learn more about reverse engineering and malware analysis. Thank you to Dragon Research Group for putting on this challenge, I had a great time working through it!

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 August 2013 page. The newest, DRG Online Challenge September 2013 is now available. Visit the DRG Challenges page for information about all current, future and past challenges.

posted at 7:01 pm | permanent link



DRG Online Challenge July 2013 Solution
2013-08-01

A new monthly series kicked off last month with the inaugural DRG Online Challenge July 2013 and we're off to a promising start. We received three correct submissions. In the order we received them, those who correctly solved the challenge were Vytautas Krakauskas, Björn Zettergren and Thomas Kjærlund Petersen. All three provided a solution within a couple of days after the challenge was published. While Vytautas was the quickest to tell us he found a solution, Björn provided a rich and lively chronicle of his efforts and Thomas discovered a clue we had inadvertently left behind, which he took advantage of. As promised, we are going to feature one solution write-up. Take it away Björn...

A challenge

This text describes how I thought and worked while solving the monthly challenge that Dragon Research Group set up. I really enjoy these kinds of exercises; It feels great when you can deduce small clues in the riddle, that eventually lead to finding the answer. Usually, I learn a lot. This was no exception.

If you would like to have a go at the challenge yourself, stop reading now and go to the challenge webpage! You can always come back afterwards.

The mission and the steps taken

Find a hidden message in a PGP message block at DRG Online Challenge July 2013. I saved it to my computer as drg-challenge.txt, and try running it through gpg (the GNU Privacy Guard, an OpenPGP implementation).

    $ gpg -d drg-challenge.txt
    gpg: packet(7) with unknown version 12
    gpg: no valid OpenPGP data found.

Of course it wouldn't be that easy, the comment did say "something is not right" after all. I spent a few moments messing around with various ideas; Maybe the block text is an ASCII-image if viewed at a different line width and small fonts - Fruitless. Maybe the block text contains some pattern of characters not contained in the base64-table, try removing all base64-chars - That left me an empty console.

Time to stop the random messing around, and look at the code-block more thoroughly from a format point of view. After a while I notice that the base64-encoding looked weird. Padding goes at the end of the sequence in Base64 (strictly speaking though, PGP uses Radix64 for "ASCII Armor", which is Base64 with a 24-bit checksum appended to it). Also, the checksums equal-sign always goes at beginning of the last row:

    -----BEGIN PGP MESSAGE-----
    Comment: something is not right
    nT9ADKOc0/VRR5Thwliz2vw7IkRwNX54Vm2opoo321eygdX+FITQnIDjCMwAE0Aj
    Imwa+dXL4Myo8CxSnG3rNJwYl7UBh+wjcZ4kfZm0NE4WyTnL1MRiK5APRgzLmJa4
    [...lots of more data...]
    iYrPdWow+n/tVnuFvhAV4ug6ZlsG+GCmWtEHmPCg2d0UIdUz2ZDF4s1nUJHp/KBn
    =4y+KEgjV9P2exoG
    tLAn=
    -----END PGP MESSAGE-----

I tried moving the equal-signs to the "correct" place, but still got the same errors. I began to suppose that the whole block might have been reversed, line by line, except the comment and PGP-boundary markers. I also found that according to the specification, the first few bytes of the ASCII-armour is actually the PGP-header, eg. jA0EAwM is the header for symmetrical CAST5 data, jA0EBwM the header for AES data, and so on. Although the beginning of the first line of the encrypted block did not adhere to this, i did notice that the header existed in reverse at the end of the line. So, let's try reversing the Radix64 block!:

    $ (head -n 2 drg-challenge.txt; sed -r '/^(-----.*PGP.*-----|Comment.*)/d' drg-challenge.txt| rev ;
    tail -n1 drg-challenge.txt ) > drg-challenge-reversed.txt

Now it looks something like this:

    -----BEGIN PGP MESSAGE-----
    Comment: something is not right
    jA0EAwMCjDInQTIF+Xdgye123oopo2mV45XNwRkI7wv2zilwhT5RRV/0cOKDA9Tn
    4aJmLzgRPA5KiRM1LnTyW4EN0mZfk4Zcjw+hBU7lYwJNr3GnSxC8oyM4LXd+awmI
    [...lots of more data...]
    nBK/pHJUn1s4FDZ2zUdIU0d2gCPmHEtWmCG+GslZ6gu4VAhvFunVt/n+woWdPrYi
    Goxe2P9VjgEK+y4=
    =nALt
    -----END PGP MESSAGE-----

This seems to have done the trick. Gpg will now recognize the data as valid PGP-block. However, I need a passphrase to decrypt it!

    $ gpg -d --no-use-agent drg-challenge-reversed.txt
    gpg: CAST5 encrypted data
    Enter passphrase:

I randomly try the few obvious options; A blank passphrase, drg, dragonresearchgroup, dragon, research, challenge, passphrase in various cases and combinations. None are correct. I tried different case-combinations and variations of the comment "something is not right". I tried using the passphrases left and wrong (since that something is not right either).

All of these attempts where futile, no award for me. I considered if there were any attacks against CAST5 (although I find it highly unlikely). So, I did a little reading on the viability of brute forcing it. Found a good explanation of why that would take a long time, for any reasonably long passphrase. I conclude that the best approach for now, is to go back to looking at what potential clues the challenge provides. Primarily, I have at my disposal the PGP-block and the original instructions at the challenge web-page. I looked through the HTML-source for clues and find nothing. Viewing the web-server response-headers yields no additional hints either.

I started to think about the comment again - "something is not right". Perhaps I was over-eager to preserve the comment when reversing the rest of the block? I tried the reversed comment as passphrase, with GREAT success! It's the correct passphrase! Cue adrenaline surge, heartbeat at unhealthy frequencies, a smile on my face! But back in the console window, signs of chaos!

    $ gpg -d --no-use-agent --passphrase "thgir ton si gnihtemos" drg-challenge-reversed.txt
    gpg: CAST5 encrypted data
    gpg: encrypted with 1 passphrase
    gpg: WARNING: message was not integrity protected
    end
    `
    110FV%9D`````245.1*Y"8((`
    MX@1^N.84SJ&,9/-YW1H0?MZ#4:RRKH^DNSV'<@[E?*1KW)A/P7RD;?P/TG35
    M;I!=R.;:.9?&J-WM@J[W?H5AZ(9NZ+H>SJ*80SG[-BB(KG[?`7D6;[P=A#$\
    MIFG:Y>,@QF+XN@.74NHX?;MK$D&EWI>=0A@=4[.(`&8:A9S(9Y6H&
    M[0XK\W*$$+)DZ(;`C$M@6)3:=K?;[;(@JFJ&$$*6R,RS>'VA%(LB5+*$+&F:
    [...lost of more data...]
    M24U%!]T'`0PA!9T'!BP````E=$58=$-O;6UE;G0`&V1R87=R;V8@=V5N(&5H
    MZ0````9B2T=$`/\`_P#_H+VGDP````EP2%ES```+$P``"Q,!`)J<&`````=T
    MB5!.1PT*&@H````-24A$4@```-P```#="`(```!?9(MS`````7-21T(`KLX&
    begin 644 dne

When I settle down a bit after the first feel-good-moment of finding the passphrase, having spent a few minutes staring at the output for a while, I recognise that it looked like uuencoded data. The last rows that say "begin 644 dne" seem to indicate that this is actually inverted uuencoded data. That part of uuencoding says that after decoding it, it'll write a file called dne with file-permissions 644. I construct a command to reverse the order and decode it (tac prints files from bottom and up):

    $ gpg -d --no-use-agent --passphrase "thgir ton si gnihtemos" drg-challenge-reversed.txt | tac | uudecode

So what is in the file?

    $ file dne
    dne: PNG image data, 220 x 221, 8-bit/color RGB, non-interlaced

When viewed in an image-viewer, the displayed image is that of an inverted Dragon Research Group beer label logo, with no obvious sign of a hidden message within the image itself. I decided to check the comment field using ImageMagicks "identify"-component - it felt like the second place to look after the image itself. Immediate success:

    $ identify -verbose dne | grep -i comment
        Comment: sdrawrof wen eht si sdrawkcab

Well, except that it seems a little backwards...

    $ identify -verbose dne | grep -i comment | rev
    backwards is the new forwards :tnemmoC

That did indeed seem like the final message, rather than a clue to continue digging. The filename is dne, which backwards becomes end after all. I considered further possible interpretations: Could there be additional strings within the raw image data? Could the author have been so cruel to stoop to steganography? After brief experimentation with both of these possibilities, I decided to accept that "backwards is the new forwards" is the intended hidden message, and mailed that to the challenge email-address.

Thank you Senghan Bright at Basefarm, for editing this text. Thank you DRG, for providing the challenge, it was lots of fun! If you have any questions or comments, please let me know.

Best Regards

Bjorn Zettergren
Basefarm SIRT
https://www.basefarm.com/Global/pgpkeys.asc

Links

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 July 2013 page. The newest, DRG Online Challenge August 2013 is now available. Visit the DRG Challenges page for information about all current, future and past challenges.

posted at 4:40 pm | permanent link



DRG FIRST 2013 Challenge
2013-06-17

The Dragon Research Group (DRG) is pleased to present, for the second year in a row, the DRG Challenge at the FIRST 2013 conference in Bangkok, Thailand. This year many more challenges have been added under an entirely new challenge framework. The competition was heated last year and promises to be again this time. While only the winning team will walk away with new iPad minis, all are sure to have fun. Thats to our generous sponsors for helping making this year's challenge possible:

Please stop by the Geek Lounge to say hi and join in if you're attending this year's FIRST Conference.

posted at 4:53 am | permanent link



Introducing the DRG Challenges!
2012-06-11

The Dragon Research Group (DRG) is pleased to kick off another major milestone in the community services we provide. The DRG Challenges page details our foray into the world of information security challenges. We currently have two projects underway. At this year's FIRST 2012 conference, the DRG is sponsoring a live, in-person challenge, the DRG FIRST 2012 Challenge. Check out what the winning team will be awarded for their efforts. It is sure to be heated competition. For those not attending the conference, fret not, we are also sponsoring the HotCRP Challenge aimed to help enhance a widely used conference management software package. We invite you to join the DRG challenges mailing list to discuss these and future DRG challenges.

posted at 5:31 pm | permanent link



DRG is looking to expand again, join us?!
2012-05-11

The Dragon Research Group (DRG) is in need of talented and trustworthy individuals who can donate at least 20 hours per month of their spare time to help us make a difference in addressing Internet security issues. We are specifically looking for volunteers who posses the following capabilities:

FreeBSD and GNU/Linux system administration
We are seeking volunteers who are proficient in managing FreeBSD and GNU/Linux systems. Ideally the candidate will be familiar and comfortable setting up and using common monitoring tools, log management applications, backup/restore solutions, database administration, system auditing processes, configuration management and light programming duties using shell or interpreted scripting languages. The ideal candidate should have extensive, remote distributed Unix system administration experience.
Software development
We are seeking volunteers who are proficient in developing various network and security tools. Competency with C/C++ a must. Ability to maintain, debug and learn other languages is desired. The ideal candidate should be capable of socket client and server programming with an emphasis on secure coding practices suitable for widespread public usage and release.
Malware analysis
We are seeking volunteers who are proficient in advanced static and dynamic malware analysis. Competency with common tools such as disassemblers and debuggers as well as x86 assembly instructions and programming a must. The ideal candidate should be capable of developing associated tools suitable for public use and security community release.
Digital media development
We are seeking volunteers who are proficient in creating original digital content including images and video suitable for publication online and in printed materials. An ideal candidate should have experience performing web development in a Unix environment.

If you can help fulfill one or more of the needs above, we want to hear from you. Please visit our Apply to DRG page and submit your application today!

posted at 4:18 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: