NDH 2018 / AutoCrackIt ... (150 pts).

NDH 2018 - AutoCrackIt

Event Challenge Category Points Solves
Nuit du hack 16 AutoCrackIt Reverse 150 ??


This is a window for a license key validator written in AutoIt. You can find the associated file here : AutoCrackIt.exe


I started to decompile the executable with AutoIt3 decompiler. Then I looked for the piece of code which manages window events. The user input is manipulated by three functions and the result is compared with an encrypted license key. By executing the functions I guessed what they were doing and I wrote a script to reverse the encrypted licence key.

Find the interesting piece of code

I began by running the executable. It’s a simple window with two buttons and an edit text.


As indicated by his name, this program may be written in AutoIt. So I tried to decompile it with AutoIt3 decompiler. I looked for the string “Validate” in the decompiled source and I found the code which checks the license key.

	$licence = "9677BD05BC969C4C8602A43163FC5BA313A04A8EC59F0A14A8F3457C..."
	While 1
		$nmsg = GUIGetMsg()
		Switch $nmsg
			Case $gui_event_close
			Case $cancel
			Case $validate
				If (cryyypt(cryypt(crypt(GUICtrlRead($edit1)))) == $licence) Then
					MsgBox(64, "Confirmed verification", "Congratz here's the flag : " & @CRLF & GUICtrlRead($edit1))
					MsgBox(16, "Failed verification", "The Licence Key is incorrect")

Reverse the algorithm

As you can see above the user input ($edit1) is manipulated by three functions crypt,cryypt and cryyypt. The result must be equals to $licence variable.


I watched the first function crypt.

Func crypt($data, $linebreak = 76)
	Local $opcode = "0x5589E5FF7514535657E8410000004142434445464748494A4B4C4D4E4F50515253545556..."
	Local $codebuffer = DllStructCreate("byte[" & BinaryLen($opcode) & "]")
	DllStructSetData($codebuffer, 1, $opcode)
	$data = Binary($data)
	Local $input = DllStructCreate("byte[" & BinaryLen($data) & "]")
	DllStructSetData($input, 1, $data)
	$linebreak = Floor($linebreak / 4) * 4
	Local $oputputsize = Ceiling(BinaryLen($data) * 4 / 3)
	$oputputsize = $oputputsize + Ceiling($oputputsize / $linebreak) * 2 + 4
	Local $ouput = DllStructCreate("char[" & $oputputsize & "]")
	DllCall("user32.dll", "none", "CallWindowProc", "ptr", DllStructGetPtr($codebuffer), "ptr", DllStructGetPtr($input), "int", BinaryLen($data), "ptr", DllStructGetPtr($ouput), "uint", $linebreak)
	Return DllStructGetData($ouput, 1)

As you can see the function call native function CallWindowProc with a pointer to $opcode as lpPrevWndFunc. This will indirectly execute the content of $opcode variable. This is a trick often used by malware to call native code. The $data variable is stored into $input which is passed as hWnd of CallWindowProc function and consequently as hWnd of lpPrevWndFunc. So the native code ($opcode) may manipulate our input. I saved it into a file and I disassembled it with IDA.


As you can see, there is a strange string “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/” which reminded us of base64 algorithm. To confirm my hypothesis I ran the crypt function with “Hello world” as parameters.

ConsoleWrite(crypt("Hello world"))

The above code will give the following output SGVsbG8gd29ybGQ= which confirm our hypothesis that crypt function encodes the input in base64.


As you can see the following function add or subtract 13 on each character of $data, it’s maybe a ROT-13 encryption algorithm. By running it I confirmed my hypothesis.

Func cryypt($data)
	If $data == "" Then
		Return $data
	Local $aarray = StringToASCIIArray($data)
	For $i = 0 To UBound($aarray) - 1
		If ($aarray[$i] >= 65 AND $aarray[$i] <= 77) OR ($aarray[$i] >= 97 AND $aarray[$i] <= 109) Then
			$aarray[$i] += 13
		ElseIf ($aarray[$i] >= 78 AND $aarray[$i] <= 90) OR ($aarray[$i] >= 110 AND $aarray[$i] <= 122) Then
			$aarray[$i] -= 13
	Return StringFromASCIIArray($aarray)


The cryyypt function is a little bit more complicated to guess X).

Func cryyypt($data)
	$i = 0
	$crypteddata = ""
	$adata = StringSplit($data, "", 2)
	For $char In $adata
		If (Mod($i, 2) == 0) Then
			$crypteddata = $crypteddata & StringTrimLeft(_crypt_hashdata($char, 32781), 2)
			$crypteddata = $crypteddata & $char
		$i += 1
	Return STRINGREVERSE($crypteddata)

_crypt_hashdata will hash the char with the selected algorithm. 32781 is the id of the SHA384 algorithm. As you can see there is a test with the counter variable $i

Mod($i, 2) == 0

This mean that one character out of two will be hash by SHA384 algorithm. At the end, the result string is reversed.

Putting all the pieces together

So the license key is just a base64 encoded string which is ciphered with ROT-13 algorithm which is partially hashed. We can easily write a script to decode it (except at 4 am x) ).

First, I created a dictionary to associate characters to they corresponding reversed sha384 hash (because licence key is reversed cf cryyypt).

def sha384(s):
	h = hashlib.sha384()
	return h.hexdigest()[::-1].upper()
correspondance = {}
for i in range(0,256):
	correspondance[sha384(chr(i))] = chr(i)

Then we will attack the license key block by block.

A block is composed of one character and a sha384 hash. So the blockSize is 48 (size of a sha384 hash) * 2 (because the result is in hexadecimal, so 2 digits for 1 byte) + 1 (for the one character). For each block, we will find the corresponding characters from the reversed hash.

blockSize = 48*2+1
for i in range(0,len(licence),blockSize):
	c = licence[i:i+blockSize][0]
	h = licence[i:i+blockSize][1:]
	ch = correspondance[h]
	s = ch + c + s

After retrieved the string I decrypted it with ROT-13 and decoded it with base64 algorithm.


The result is: ndh16_{{e8724c7b3596bee26cedfa0e89ea09aa6b12a5ab066950591c73514feab6e7d7eb99dff0954dc5a29ade4da9b5a811181d2c6b6b418e4279aa2612458e309f0c}}