So we are given a binary that encrypts whatever we pass it on STDIN with what I assume is AES-128.
If you run the binary with no arguments it prints out the help text:
usage: method a) ./whitebox [16 bytes plaintext] method b) ./whitebox --stdin
So we pass in 16 bytes of plaintext to see the result:
$ ./whitebox ABCDEFGHIJKLMNOP 65 49 38 33 73 6c c1 ca 3d 61 0f 88 c6 ad c1 a1
The output is the ciphertext. Let's think, how can we attack this whitebox crypto binary?
- Reverse it? nah too hard
- Bruteforce? You're dreaming...
- DCA? Yes!
DCA stands for Differentical Computational Analysis, and is the software version of DPA (Differential Power Analysis) - which is effectivley a side channel attack use to crack crypto by watching the amount of power used over the time of the crypto operations. DCA involves analyzing reads and writes to specific regions of memory during run time of the encryption function, over many many executions.
There is an awesome suite of tools written by some really smart guys that I will leverage to crack the key of the AES cipher.
The script I used is the following:
How this works, is that we use
TracerPIN to trace the execution of the binary and all of it's reads and writes to memory. We provide
TracerPIN a function (
processinput) that it uses to mangle the desired (
16) amount of bytes into a form that the binary (
../shared/rhme/whitebox) will accept, and a function (
processoutput) that will mangle the output of the binary into a form that
Daredevil will be able to understand.
My processinput function uses stdin with the binary rather than cli arguments, as I was having issues running the script with using the default argument option, as nullbytes and spaces in the plaintext was causing the binary to print the help text instead of encrypting the text (fair enough).
We then run 2000 traces, which I found way overkill for what was needed, as it took quite a while to run through all of these. You could probably get away with a hundred or so.
The output of TracePIN should be converted into a format that Daredevil will understand, so we use
bin2daredevil to create two output files. Daredevil has two approaches at cracking the key, and each of these output files can be used to attempt to crack in two different ways.
Letting this script run will create an output such as the following:
00160 0E63E586567952994CB37021741D6F42 -> AD7DFA7AAAA78203CAC8DF967297DA2A 00161 46DE5D2BB2D38B0ADEF884FDFB16902A -> 3D97B02AD1E684A05AB5F08B48BD4875 00162 D68EF5D4B14848C28BEADA72B86D2970 -> 64E2D00DE3F4A4E000F4FF28350C212E 00163 2B77D02E5FBA1AB2A5CCD0630E884C88 -> 90DD5735923533DA2DC0E7688DACC404 00164 3009F101799A6A938FC3297876EE7BEE -> 713E58B74D8F9841302DCDE4DDF2FD95
Showing the current trace number, the plaintext and the ciphertext.
After the desired amount of traces are allowed to run, we run the output file for the
attack_sbox approach through daredevil to attempt to crack the key byte by byte, bit by bit!
The file we want to run with daredevil was
mem_addr1_rw1_*_*.attack_sbox.config, which was the file that the DCA script created with the sbox attack in mind for daredevil.
daredevil -c mem_addr1_rw1_2275_42784.attack_sbox.config [CONFIGURATION] [GENERAL] Number of threads: 8 Index first sample: 0 Number of samples: 42784 Total number traces: 2275 Target number traces: 2275 Total number keys: 256 Attack order: 1 Return Type: d Window size: 0 Algorithm: AES Round: 0 Bytenum: all Target all bits individually. Secret Key: Unspecified Memory: 4.00GB Keep track of: 20 Separator : STANDARD [TRACES] Traces files: 1 Traces type: i Transpose traces: True Total number samples: 42784 Traces: 1. mem_addr1_rw1_2275_42784.trace [2275x42784] [GUESSES] Guesses files: 1 Guesses type: u Transpose guesses: True Total columns guesses: 16 Guesses: 1. mem_addr1_rw1_2275_42784.input [2275x16] [/CONFIGURATION] [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 [ATTACK] Computing 1-order correlations... [ATTACK] Key byte number 0 [ATTACK] Target bit number 0 [ATTACK] Target bit number 1 [ATTACK] Target bit number 2 [ATTACK] Target bit number 3 [ATTACK] Target bit number 4 [ATTACK] Target bit number 5 [ATTACK] Target bit number 6 [ATTACK] Target bit number 7 Best 10 candidates for key byte #0 according to sum(abs(bit_correlations)): 1: 0x61 sum: 6.25271 2: 0x0c sum: 1.11944 3: 0x6d sum: 1.10948 4: 0xdd sum: 1.10619 5: 0xfc sum: 1.06602 6: 0x1f sum: 1.06513 7: 0x6b sum: 1.06294 8: 0x15 sum: 1.0557 9: 0xe3 sum: 1.04775 10: 0xcf sum: 1.0463 Best 10 candidates for key byte #0 according to highest abs(bit_correlations): 1: 0x61 peak: 0.891726 2: 0x8c peak: 0.183911 3: 0x25 peak: 0.180419 4: 0xfc peak: 0.178889 5: 0x06 peak: 0.178065 6: 0xb1 peak: 0.177028 7: 0x12 peak: 0.174891 8: 0x26 peak: 0.174883 9: 0xef peak: 0.173886 10: 0x1f peak: 0.170436 [INFO] Attack of byte number 0 done in 253.929359 seconds. [ATTACK] Key byte number 1 ...
We can see we have 2275 traces, which is a fair amount! This amount of traces may be necessary for DPA on actual hardware with a large amount of noise, but this whitebox approach on software (DCA) has zero noise. You can see the result of the number of traces I did on the confidence that daredevil has for the first byte.. that's very confident!
After letting this run for a while (about an hour for me with my excessive amount of traces..) we get the most probable key out!
Most probable key sum(abs): 1: 106.732: 61316c5f7434623133355f525f6f5235 2: 101.599: 0c316c5f7434623133355f525f6f5235 3: 101.589: 6d316c5f7434623133355f525f6f5235 4: 101.585: dd316c5f7434623133355f525f6f5235 5: 101.584: 61316c5f743462310b355f525f6f5235 6: 101.558: 61316c5f74346231b2355f525f6f5235 7: 96.4507: 0c316c5f743462310b355f525f6f5235 8: 96.4408: 6d316c5f743462310b355f525f6f5235 9: 96.4375: dd316c5f743462310b355f525f6f5235 10: 96.4247: 0c316c5f74346231b2355f525f6f5235 Most probable key max(abs): 1: 14.3015: 61316c5f7434623133355f525f6f5235 2: 13.6037: 61316c5f7434623133435f525f6f5235 3: 13.6026: 61316c5f3d34623133355f525f6f5235 4: 13.5978: 61316c5f7434623145355f525f6f5235 5: 13.5936: 8c316c5f7434623133355f525f6f5235 6: 12.9048: 61316c5f3d34623133435f525f6f5235 7: 12.9: 61316c5f7434623145435f525f6f5235 8: 12.8989: 61316c5f3d34623145355f525f6f5235 9: 12.8959: 8c316c5f7434623133435f525f6f5235 10: 12.8947: 8c316c5f3d34623133355f525f6f5235 [INFO] Total attack of file LUT/AES_AFTER_SBOX done in 4173.009927 seconds.
Doing some encryption/decryption tests with the output of
Trace during the initial trace gathering I am confident that option 1 is the correct key.
And converting the most probable key from hex to ascii (this tripped me up while trying to submit for quite a while...) we get the flag!