Skip to content

Tutorial #2: DCA against Hack.lu 2009 challenge

Philippe Teuwen edited this page Apr 12, 2016 · 7 revisions

Here is the second tutorial of our series.

Specificities of this tutorial:

  • Learning how to trace a Windows executable under Linux
  • Learning how to automate GUI interactions
  • Learning how to use the visual trace to get information useful for the DCA attack

The corresponding materials are available at https://github.com/SideChannelMarvels/Deadpool/tree/master/wbs_aes_hacklu2009

This time we're talking about a challenge made by Jean-Baptiste Bédrune for Hack.lu 2009.

It's a Windows 32-bit graphical crackme performing an AES128 encryption over the user input. If the output is equal to "hack.lu-2009-ctf", one gets a success message.

Intel PIN works also under Windows but TracerPIN was not ported to Windows so let's run the crackme.exe under Linux via Wine. Intel PIN failing on Wine, we'll use TracerGrind. (maybe it's possible to use PIN with Wine with some tuning or memory remappings, to be investigated...)

GUI automation will be provided by xdotool, which can handle keyboard and mouse, but if possible it's always more reliable to use only the keyboard and e.g. if possible use Tab to move along the various features of the graphical interface.

Here is one automated execution where the right input is provided:

wine32 crackme.exe &
sleep 1
xdotool - << EOF
search --sync --limit 1 --name Crackme
sleep 0.1
key Tab Tab
key 1 9 2 shift+E shift+F 9 shift+E 6 1 1 6 4 shift+B shift+D 2 8 9 shift+F 7 7 3 shift+E 6 shift+C 9 1 0 1 shift+B 8 9 shift+C
key Tab space
EOF

First step is to trace the executable with TracerGrind.

But beware! When an executable is launched via Wine, the Wine server is running in the background and we certainly don't want to instrument the whole Wine server!
So, an easy way is to start another process in a separate window and keep it open during the experiments with the crackme.

wine notepad.exe

Now we can trace our executable with an arbitrary input once we've identified the main address range, then convert the trace into a Sqlite DB:

objdump -p crackme.exe |grep Image
ImageBase               00400000
SizeOfImage             00018000

valgrind --trace-children=yes --tool=tracergrind --filter=0x400000-0x401800 --vex-iropt-register-updates=allregs-at-mem-access --output=crackme.trace wine32 crackme.exe &
sleep 1
xdotool - << EOF
search --sync --limit 1 --name Crackme
sleep 0.1
key Tab Tab
key 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
key Tab space
sleep 0.1
windowkill
EOF

sqlitetrace crackme.trace crackme.sqlite

Fire tracegraph, load the sqlite trace and rescale with the Overview zoom.

Full trace

Note that if pixels are too small to get a global idea, you can change their size with keys + and -, e.g.:

Full trace, bigger pixels

We're looking at an AES128 so we see clearly the rounds. Actually we see only about 9 rounds and not 10. That's because in AES, there is no MixColumn step in the last round. One pecularity is that the stack got allocated at a very low address range, even before the code section at 0x400000. Let's zoom on the stack on the AES section:

Stack trace

If we count the blocks seen on the left of the image, we see 10 of them! Reassured? ;)

This binary is still quite small to trace entirely, nevertheless let's see how we can filter addresses to capture only the useful part.

Zoom on the instructions loop of the AES and find the address range to spy on.

Instructions loop

0x401000-0x401243 seems a good one.

https://github.com/SideChannelMarvels/Deadpool/blob/master/wbs_aes_hacklu2009/DCA contains the scripts to perform the acquisition of a few traces.
The range 0x401000-0x401243 is configured in run.sh, a little helper script to handle Wine and xdotool as seen above.

Remember first to have wine notepad.exe running somewhere in the background.

Note that due to the nature of the challenge we've direct access only to the input, not the output. The output is not needed to mount the attack, it's just nice to have to be able to verify if the found key is correct or not. But with this challenge you can test your key to decrypt the string hack.lu-2009-ctf and try the crackme.

Let's acquire a few traces: (obviously refrain from using the keyboard and mouse during the acquisition!)

./Tracer2bin.py
...
00000 D77EAE3D6557FDA6E4E013435B984F50 -> ?
...
00001 A7F1A463EDA8248E357CB14B2DBD74C9 -> ?
...

Convert them for Daredevil:

./bin2daredevil.py

Executing the differential analysis on the converted traces, here on addresses:

daredevil -c addr8_r_20_5888.config|egrep "INFO|Best bit"
[INFO] File LUT/AES_AFTER_SBOX not found, using /usr/local/share/daredevil/LUT/AES_AFTER_SBOX instead.
[INFO] Lookup table specified at LUT/AES_AFTER_SBOX
[INFO] Attack of byte number 0 done in 0.117227 seconds.
Best bit: 0 rank: 0.               1    0x14       130
[INFO] Attack of byte number 1 done in 0.111825 seconds.
Best bit: 0 rank: 0.               1    0x2b       554
[INFO] Attack of byte number 2 done in 0.108856 seconds.
Best bit: 0 rank: 0.               1    0xbe       410
[INFO] Attack of byte number 3 done in 0.119154 seconds.
Best bit: 0 rank: 0.               1    0xe        266
[INFO] Attack of byte number 4 done in 0.109574 seconds.
Best bit: 0 rank: 1.               1    0x2d       258
Best bit: 1 rank: 0.               1    0x2d       259
[INFO] Attack of byte number 5 done in 0.140929 seconds.
Best bit: 0 rank: 0.               1    0x22       170
[INFO] Attack of byte number 6 done in 0.118977 seconds.
Best bit: 0 rank: 1.               1    0xe4       538
Best bit: 2 rank: 0.               1    0xe4       540
[INFO] Attack of byte number 7 done in 0.121391 seconds.
Best bit: 0 rank: 0.               1    0x80       394
[INFO] Attack of byte number 8 done in 0.107881 seconds.
Best bit: 0 rank: 0.               1    0x97       386
[INFO] Attack of byte number 9 done in 0.104676 seconds.
Best bit: 0 rank: 0.               1    0x49       298
[INFO] Attack of byte number 10 done in 0.107286 seconds.
Best bit: 0 rank: 0.               1    0x7d       154
[INFO] Attack of byte number 11 done in 0.107744 seconds.
Best bit: 0 rank: 0.               1    0x5f       522
[INFO] Attack of byte number 12 done in 0.115974 seconds.
Best bit: 0 rank: 0.               1    0xac       514
[INFO] Attack of byte number 13 done in 0.114706 seconds.
Best bit: 0 rank: 0.               1    0x5b       426
[INFO] Attack of byte number 14 done in 0.138724 seconds.
Best bit: 0 rank: 0.               1    0x59       282
[INFO] Attack of byte number 15 done in 0.114543 seconds.
Best bit: 0 rank: 1.               1    0x26       138
Best bit: 1 rank: 0.               1    0x26       139
[INFO] Total attack of file LUT/AES_AFTER_SBOX done in 1.860457 seconds.

Trying on stack writes gives similar results:

daredevil -c stack8_w_20_3008.config|egrep "INFO|Best bit"

Both work equally well on 20 traces with correlations being equal to 1, which means no internal encoding was used in this white-box. It's not so surprising as it was designed as one of the few challenges for a CTF that lasted 48h ;)

As in the first tutorial we actually conducted a known key analysis and observed the right key candidates were ranked first.

To demonstrate an attack without knowing the key, one can edit e.g. the generated addr8_r_20_5888.config file to comment out the correct key and choose which key byte to break (e.g. bytenum=0 to attack the first one). Run again the attack:

daredevil -c addr8_r_20_5888.config
...
[ATTACK] Key byte number 0
[ATTACK] Target bit number 0
[INFO]	1507328 correlations computed in total.
[INFO]	Global top 20 correlations.
               1    0x14       218
               1    0x14       250
               1    0x14       186
               1    0x14       130
       -0.904534    0xf9      3262
        0.904534    0xd5      3405
        0.904534    0xd5      3437
        0.904534    0x7c      2004
        0.904534    0x58       759
       -0.904534    0x50      3263
        0.904534    0xd5      3357
       -0.904534    0x84      3286
        0.904534    0x72      4548
        0.904534    0xd5      3365
        0.902671    0x83      3082
        0.902671    0x83      3090
        0.902671    0x83      3170
        0.902671    0x22      3999
        0.902671    0x9f      3165
        0.902671    0x22      4007
...

First byte best candidate is clearly 0x14. With an AES128 encryption it's easy: the first round key is the AES key and indeed 0x14 is the first byte of the AES key.

Verification:

echo 192EF9E61164BD289F773E6C9101B89C|xxd -r -p|openssl enc -e -aes-128-ecb -nopad -K 142bbe0e2d22e48097497d5fac5b5926
hack.lu-2009-ctf