RHME3 Quals - Whitebox

September 01 2017

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:

  method a) ./whitebox [16 bytes plaintext]
  method b) ./whitebox --stdin

So we pass in 16 bytes of plaintext to see the result:

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


	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 files:		 1
	Traces type:		 i
	Transpose traces:	 True
	Total number samples:	 42784
	1. mem_addr1_rw1_2275_42784.trace	 [2275x42784]

	Guesses files:		 1
	Guesses type:		 u
	Transpose guesses:	 True
	Total columns guesses:	 16
		1. mem_addr1_rw1_2275_42784.input	 [2275x16]


[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!