Solving software challenges - Synopsys Pesti Challenge 2016

I like mysteries and puzzles. They provide solid, exciting and interesting base for movies, books and recreational activities. And, at least in the software industry, they can be used in recruitment prescreening and/or candidate selection. This was also the case in Synopsys Pesti Challenge 2016, which I stumbled upon when reading backlogs Geek Collision's IRC channel. The challenge was part of the Pesti Career Day, a networking event aimed at the students of University of Oulu.

Spoiler alert!

Detailed solutions ahead!

Mission 1

The zip archive contains three files:

instructions.txt
mission2.zip
traffic.pcap

The instructions tells you to find a password from traffic.pcap and use it to open the encrypted mission2.zip archive. Sounds quite straightforward.

So, pcap files are network packet capture files, recorded for example by tcpdump. I used tshark (a network analyzer with a command line interface, member of the Wireshark suite) to decode the file as plain text:

tshark -V -r traffic.pcap | less

And because I was looking for a password, I decided to give a try and searched for words "password" and "Password". Luckily, the latter gave a match:

X-Shadows: Password for mission2.zip is same as for this webpage.\n

That line was a HTTP header field, part of a response message (in frame 251) to a previously sent HTTP request. The "mission1" was starting to look really easy and almost solved; all captured HTTP traffic was unencrypted, so the password was there, just waiting for me to read it. The original HTTP request was couple of frames before (frame number 247) and it contained the basic auth header in plain text, alongside with its base64 decoded payload:

Authorization: Basic TXJKb2huc29uOlJ1bm5pbmdJblRoZVNoYWRvd3M=\r\n
    Credentials: MrJohnson:RunningInTheShadows

Mission accomplished, that was easy.

Mission 2

With the correct password, mission2.zip revealed following secrets:

instructions.txt
logo.bmp
mission3.zip

Now, the mission was once again, to find a correct password for mission3.zip. Instructions hinted that the password was somewhere inside logo.bmp. I decided to have look at it with my preferred image viewer, sxiv. Unfortunately, the image format was unsupported, which was not a big surprise, since BMP is a bitmap file format specificed by Microsoft. Firefox was my second choice, and it revealed the secrets of the image right away; it was a blend of a very crude logo and a QR code:

logo.bmp

Poor man's steganography, that is.

So, the password was probably encoded as a QR code which in turn was blended with another image to make it hard to decipher. Well, because QR codes are binary images, one bit per pixel is enough for conveying the code. Perhaps it was the least significant bit?

I just had to extract the QR code from the image and make it actually machine-readable for a barcode scanner app. I had experience on mangling bitmaps with Python and PIL (Pillow actually), so writing a short Python script for the task was the most natural thing to do for me. I guess I could have extracted the bardcode from the image with Gimp or such, but mouse-surfing through menus, dialogs and buttons is not my cup of tea. Hacking with my keyboard is:

import PIL.Image

img1        = PIL.Image.open("logo.bmp")
img1_bytes  = img1.tobytes()
img2        = PIL.Image.new("RGB", img1.size)
img2_pixels = img2.load()

for i in range(img1.size[0]):
    for j in range(img1.size[1]):
        byte = ord(img1_bytes[i + j * img1.size[0]])
        img2_pixels[i, j] = ((byte & 1) * 255,) * 3

img2.save("logo_unmasked.png")

And out came the password:

logo_unmasked.png

440e167fdec

Mission 3

The final archive, mission3.zip, contains the following files:

final.txt.gpg
strange.txt
task.txt

The mission, as described in task.txt, is to parse the password for the symmetrically encrypted final.txt.pgp file from strange.txt with the following rules:

  • Begin from the start until the first occurrence of '/' (inclusive).
  • Take all occurences of 'h' followed by 4 uppercase letters, 3 numbers and 2 next characters.
  • Take everything between 'e' and '/' (inclusive).
  • Take all occurences of 'i' and any 5 characters.
  • Take 'SYN' and 'ACK' and everything between.
  • Take 'RST' and 15 next characters.
  • All found matches regardless of match type must be in the order they are found in strange.txt.

I must say that I find the parsing rules somewhat ambiguous, probably intentionally, it is part of the "challenge" afterall, right?

After I read those rules couple of times, I came up with the following short Python script:

import re

with open("strange.txt") as f:
    strange = f.read()

parts = []

patterns = (
    r'^.*?/',
    r'h[A-Z]{4}[0-9]{3}..',
    r'e.*?/',
    r'i.{5}',
    r'SYN.*?ACK',
    r'RST.{15}',
)

for pattern in patterns:
    for match in re.finditer(pattern, strange):
        parts.append((match.start(), match.group()))

print(''.join(list(zip(*sorted(parts)))[1]), end='')

So the first six rules translate directly to the six regular expression patterns. Nothing special there. But the trick of this mission was to join all matched substrings in the right order. The seventh rule states that all matches must be concatenated in the order they are found. So, to concatenate matches in the right order (not in the order the patterns were matched, but in the order the password substrings are located in the original string), I had to sort the results before joining them together. Hence I used the start offset of each match to sort all substrings.

And then again, out came the password:

DCWdszB5rVbamS6S8pLVBx2KzSktPxU2CsbbMdFLqLc2w78Tt0YTyD3PaubkmDHr1ZcbD0XD3I/SYNj4xqSU7ACKe8yUz4wB46FsoLJIIRJAMuVoCeFbqb2Ij5KLsel2NSjKCudMEtj8vIONhQIYZewUt7mqjkIuz9zD5U/iE2J6UhGKON356QDiOJ7WUiap7y1hCDUE295qfi03sk2eMs5orW9nr2UAkWWUuTgAAxT/iwg8R7hHJDH026COelPC/e3yNlz5y5L/eVTZzaMaE1ZW/SYNuqoOPnGEHuOVKgQrJ7dr4duJfWRZfKmo74pP7sUvPRgtNY8j30Yp5APPgWwf4DQqDgb9YszYPt5K77VcaI3WVKgAJ1YNdY9urAMn07NIT1R4z4UZg8OzMc9yWIKfcntt1DlLACKiQX95bhDSIX661jkeu1HLxFpqJLz2CTkfYpxJJ6ooWJzEmkNqXpjf0wKmPtHH7/SYNtzLBcSaoKjP0m8BA+ull2JaJld26Kf8Ol92+KJQggBrTglzRd+36GVCoTnNSsACKegPwJ2kj7dQmlc/hULOR911nriY0vRUhKYPM777rheFPo7mCrp+uRWpFHHw+bERn65ZkXKv3f8FuHdD8Ocos/iWgUqaiproB3ej5YV/ecpqNwsm2LmAFvLzwrsj/RSTTKcTPVwIjLKdTgl

Decrypted final.txt welcomes the reader to visit Synopsys's Pesti Career Day booth to claim a prize. The challenge was Truly Covered, yay. Not much of a challenge though, but still a nice snack.

Few months ago I solved a reverse engineering challenge, named Cyber Challenge 2015. It was a somewhat more challenging task than this and proved to bring more joy. I'll write a post about it the next time I find some time.

Comments

Comments powered by Disqus