LabyREnth Challenge One

Synopsis - Why you so wrong at this?

This is a write up on how I solved the first Windows LabyREnth challenge. At the time of doing these challenges I was not thinking about posting my solutions so that's why some of the screenshots may show dates after the challenge had ended.

At first pass this is what it looks like when executed:
Why you so wrong at this?

Well, looks like 123 was not it ;-). Let's take a deeper look and dig into the binary.

Analysis

PE Analysis

Let's start with some basic file analysis. Using CFF Explorer we notice that it appears to be packed with UPX, though the section names say ups versus upx.

  1. File Info
  • CFF Explorer - File Info
  • Section Headers
  • CFF Explorer

Follow Along Note: Set "DLL can move" to false using CFF Explorer to get the memory addresses to match.

  1. Select Optional Header
  • Click Click here to the far right of DllCharacteristics
  • Uncheck DLL can move
    • CFF Explorer DLL Can Move
  • Click OK and then save the changes.
Unpacking

Let's take a shot at unpacking this manually. Load the binary into x64dbg and set a break point on the last instruction, a jmp in this case (this jmp will transfer execution of the code from the packer to the original progam).
x64-Dbg UPX Jmp
X64Dbg - At the JMP instruction

After breaking at the jmp, single step one more instruction so that the EIP is pointing to the OEP (original entry point). Next, using Scylla, dump the program and rebuild the Import Address Table (IAT).

Scylla
Syclla - With the IAT Rebuilt

This method should work for UPX in general.

Slightly more detail for those interested

Dump File

  1. Once the program is paused at the OEP, launch Syclla
  • The instruction the jmp jumps to
  • In Syclla, make sure the AntiD.exe file is selected at the top
  • Set the OEP to the value of EIP
  • Click Dump

Rebuild the Import Address Table (IAT)

  1. Click IAT Autosearch
  • or
  1. Locate starting position of the IAT and enter that into VA
    • IAT Rebuilding Tutorial
      • Note: According to this, as long as the size is large enough (but not too large), it will work.
  • Calculate the size
    • x64Dbg has a built-in calculator to help with this
  • Click Get Imports
  • Fix dump by clicking Fix Dump and selecting the prior dumped file.

The IAT starting position and the length in the screenshot were manually entered in, thus the values may differ some if using IAT Autosearch.

Next, confirm that the executable works by running it. Assuming it does, we are ready to continue the hunt!


Key Hunting

Locating the Key Comparison Check

So now we have a running unpacked executable. Let's load it into IDA Pro and look at the main function.

Main Function in IDA Pro
Main Function in IDA - To view larger image, right click on the image and select Open image in new tab or the equivalent depending on the browser.

Looking at line 4013CA we notice what appears to be the message displayed upon entering the correct key. Let's work backwards from here. The jump that determines if this block of code is executed is at 4013C8 whose Zero Flag is set by the test instruction in the line prior. The test instruction is checking the value EDX and setting the Zero Flag accordingly. The EDX register is set from the value of AL (movzx will zero fill the remaining bits of the register). Most functions store their return value in EAX, so AL is more than likely set in the function sub_4011B0. Upon opening that function, it looks like the value of AL is set to either a 0 or a 1 prior to the function returning. This also appears to be where the key check algorithm is. Let's take a closer look.

Key Check Algorithm

The full disassembled function sub_4011B0 with comments is located in the appendix.

Key Check Algorithm Summary

The key check algorithm contains a loop that iterates over the values in the string and performs a series of XOR, AND, ADD, and SUB operations on them and then compares it to the "decoded" hard coded values in the function. At various stages throughout the loop, the value is saved off and debugging checks are done.

Debug Checks

  1. @ address 4012B4 : A function call that calls CheckRemoteDebuggerPresent
  • @ address 4012D6 : A function call that calls FindWindow looking for OLLYDBG
  • @ adddress 4012F5 : A function call that calls IsDebuggerPresent
  • @ adddress 401313 : A function call that makes two calls to rdtsc and looks at the difference to see if the program was paused during execution and thus might be running in a debugger

First, let's make use of Python and replicate the binary logic.

def hash(var_38, test_char):
  return ((var_38 & 0xff) ^ (((((((ord(test_char) ^ 0x33) & 0xFF) + 0x44) & 0xFF) ^ 0x55) & 0xFF) - 0x66) & 0xFF) & 0xFF

And the values to compare against can be extracted like so:

keys = ['0x8c', '0xf1', '0x53', '0xa3', '0x8', '0xd7', '0xdc', '0x48', '0xdb', '0xc', '0x3a', '0xee', '0x15', '0x22', '0xc4', '0xe5', '0xc9', '0xa0', '0xa5', '0xc', '0xd3', '0xdc', '0x51', '0xc7', '0x39', '0xfd', '0xd0', '0xf8', '0x3b', '0xe8', '0xcc', '0x3', '0x6', '0x43', '0xf7', '0xda', '0x7e', '0x65', '0xae', '0x80']

Using a built-in python constant (string.printable), iterate over the possible values to derive the key.

#!/usr/bin/env python

import string

secret = []

keys = ['0x8c', '0xf1', '0x53', '0xa3', '0x8', '0xd7', '0xdc', '0x48', '0xdb', '0xc', '0x3a', '0xee', '0x15', '0x22', '0xc4', '0xe5', '0xc9', '0xa0', '0xa5', '0xc', '0xd3', '0xdc', '0x51', '0xc7', '0x39', '0xfd', '0xd0', '0xf8', '0x3b', '0xe8', '0xcc', '0x3', '0x6', '0x43', '0xf7', '0xda', '0x7e', '0x65', '0xae', '0x80']

def hash(var_38, test_char):
  return ((var_38 & 0xff) ^ (((((((ord(test_char) ^ 0x33) & 0xFF) + 0x44) & 0xFF) ^ 0x55) & 0xFF) - 0x66) & 0xFF) & 0xFF


var_38 = 0

for key in keys:
  print 'key: ' + key + '    var_38: ' + hex(var_38)
  s = None
  for var in string.printable:
    if hex(hash(var_38, var)) == key:     
      var_38 = var_38 + hash(var_38, var)
      print 'Found answer for: ' + key + ' : ' + var
      s = var
      secret.append(s)
      break
  if s is None:
    print 'Not found for: ' + key

print secret
print ''.join(secret)

Results upon running the script:

$python keyfinder.py 
key: 0x8c    var_38: 0x0
Found answer for: 0x8c : P
key: 0xf1    var_38: 0x8c
Found answer for: 0xf1 : A
key: 0x53    var_38: 0x17d
Found answer for: 0x53 : N
key: 0xa3    var_38: 0x1d0
Found answer for: 0xa3 : {
key: 0x8    var_38: 0x273
Found answer for: 0x8 : C
key: 0xd7    var_38: 0x27b
Found answer for: 0xd7 : 0
key: 0xdc    var_38: 0x352
Found answer for: 0xdc : n
key: 0x48    var_38: 0x42e
Found answer for: 0x48 : f
key: 0xdb    var_38: 0x476
Found answer for: 0xdb : 1
key: 0xc    var_38: 0x551
Found answer for: 0xc : a
key: 0x3a    var_38: 0x55d
Found answer for: 0x3a : g
key: 0xee    var_38: 0x597
Found answer for: 0xee : u
key: 0x15    var_38: 0x685
Found answer for: 0x15 : l
key: 0x22    var_38: 0x69a
Found answer for: 0x22 : 4
key: 0xc4    var_38: 0x6bc
Found answer for: 0xc4 : t
key: 0xe5    var_38: 0x780
Found answer for: 0xe5 : i
key: 0xc9    var_38: 0x865
Found answer for: 0xc9 : 0
key: 0xa0    var_38: 0x92e
Found answer for: 0xa0 : n
key: 0xa5    var_38: 0x9ce
Found answer for: 0xa5 : s
key: 0xc    var_38: 0xa73
Found answer for: 0xc : _
key: 0xd3    var_38: 0xa7f
Found answer for: 0xd3 : 0
key: 0xdc    var_38: 0xb52
Found answer for: 0xdc : n
key: 0x51    var_38: 0xc2e
Found answer for: 0x51 : _
key: 0xc7    var_38: 0xc7f
Found answer for: 0xc7 : 4
key: 0x39    var_38: 0xd46
Found answer for: 0x39 : _
key: 0xfd    var_38: 0xd7f
Found answer for: 0xfd : J
key: 0xd0    var_38: 0xe7c
Found answer for: 0xd0 : 0
key: 0xf8    var_38: 0xf4c
Found answer for: 0xf8 : 8
key: 0x3b    var_38: 0x1044
Found answer for: 0x3b : _
key: 0xe8    var_38: 0x107f
Found answer for: 0xe8 : W
key: 0xcc    var_38: 0x1167
Found answer for: 0xcc : 3
key: 0x3    var_38: 0x1233
Found answer for: 0x3 : L
key: 0x6    var_38: 0x1236
Found answer for: 0x6 : L
key: 0x43    var_38: 0x123c
Found answer for: 0x43 : _
key: 0xf7    var_38: 0x127f
Found answer for: 0xf7 : D
key: 0xda    var_38: 0x1376
Found answer for: 0xda : 0
key: 0x7e    var_38: 0x1450
Found answer for: 0x7e : N
key: 0x65    var_38: 0x14ce
Found answer for: 0x65 : 3
key: 0xae    var_38: 0x1533
Found answer for: 0xae : !
key: 0x80    var_38: 0x15e1
Found answer for: 0x80 : }
['P', 'A', 'N', '{', 'C', '0', 'n', 'f', '1', 'a', 'g', 'u', 'l', '4', 't', 'i', '0', 'n', 's', '_', '0', 'n', '_', '4', '_', 'J', '0', '8', '_', 'W', '3', 'L', 'L', '_', 'D', '0', 'N', '3', '!', '}']
PAN{C0nf1agul4ti0ns_0n_4_J08_W3LL_D0N3!}

And the key is: PAN{C0nf1agul4ti0ns_0n_4_J08_W3LL_D0N3!} !!

And that is all. Until next time, Happy Reversing!


Appendix
Key Check Algorithm Disassembled
ups0:004011B0
ups0:004011B0 ; =============== S U B R O U T I N E =======================================
ups0:004011B0
ups0:004011B0 ; Attributes: bp-based frame
ups0:004011B0
ups0:004011B0 ; int __cdecl sub_4011B0(char *Str)
ups0:004011B0 sub_4011B0      proc near               ; CODE XREF: _main+3Bp
ups0:004011B0
ups0:004011B0 var_38          = dword ptr -38h
ups0:004011B0 counter         = dword ptr -34h
ups0:004011B0 var_30          = dword ptr -30h
ups0:004011B0 var_2C          = byte ptr -2Ch
ups0:004011B0 var_2B          = byte ptr -2Bh
ups0:004011B0 var_2A          = byte ptr -2Ah
ups0:004011B0 var_29          = byte ptr -29h
ups0:004011B0 var_28          = byte ptr -28h
ups0:004011B0 var_27          = byte ptr -27h
ups0:004011B0 var_26          = byte ptr -26h
ups0:004011B0 var_25          = byte ptr -25h
ups0:004011B0 var_24          = byte ptr -24h
ups0:004011B0 var_23          = byte ptr -23h
ups0:004011B0 var_22          = byte ptr -22h
ups0:004011B0 var_21          = byte ptr -21h
ups0:004011B0 var_20          = byte ptr -20h
ups0:004011B0 var_1F          = byte ptr -1Fh
ups0:004011B0 var_1E          = byte ptr -1Eh
ups0:004011B0 var_1D          = byte ptr -1Dh
ups0:004011B0 var_1C          = byte ptr -1Ch
ups0:004011B0 var_1B          = byte ptr -1Bh
ups0:004011B0 var_1A          = byte ptr -1Ah
ups0:004011B0 var_19          = byte ptr -19h
ups0:004011B0 var_18          = byte ptr -18h
ups0:004011B0 var_17          = byte ptr -17h
ups0:004011B0 var_16          = byte ptr -16h
ups0:004011B0 var_15          = byte ptr -15h
ups0:004011B0 var_14          = byte ptr -14h
ups0:004011B0 var_13          = byte ptr -13h
ups0:004011B0 var_12          = byte ptr -12h
ups0:004011B0 var_11          = byte ptr -11h
ups0:004011B0 var_10          = byte ptr -10h
ups0:004011B0 var_F           = byte ptr -0Fh
ups0:004011B0 var_E           = byte ptr -0Eh
ups0:004011B0 var_D           = byte ptr -0Dh
ups0:004011B0 var_C           = byte ptr -0Ch
ups0:004011B0 var_B           = byte ptr -0Bh
ups0:004011B0 var_A           = byte ptr -0Ah
ups0:004011B0 var_9           = byte ptr -9
ups0:004011B0 var_8           = byte ptr -8
ups0:004011B0 var_7           = byte ptr -7
ups0:004011B0 var_6           = byte ptr -6
ups0:004011B0 var_5           = byte ptr -5
ups0:004011B0 var_4           = dword ptr -4
ups0:004011B0 Str             = dword ptr  8
ups0:004011B0
ups0:004011B0                 push    ebp
ups0:004011B1                 mov     ebp, esp
ups0:004011B3                 sub     esp, 38h
ups0:004011B6                 mov     eax, ___security_cookie
ups0:004011BB                 xor     eax, ebp
ups0:004011BD                 mov     [ebp+var_4], eax
ups0:004011C0                 mov     [ebp+var_2C], 8Ch
ups0:004011C4                 mov     [ebp+var_2B], 0F1h
ups0:004011C8                 mov     [ebp+var_2A], 53h
ups0:004011CC                 mov     [ebp+var_29], 0A3h
ups0:004011D0                 mov     [ebp+var_28], 8
ups0:004011D4                 mov     [ebp+var_27], 0D7h
ups0:004011D8                 mov     [ebp+var_26], 0DCh
ups0:004011DC                 mov     [ebp+var_25], 48h
ups0:004011E0                 mov     [ebp+var_24], 0DBh
ups0:004011E4                 mov     [ebp+var_23], 0Ch
ups0:004011E8                 mov     [ebp+var_22], 3Ah
ups0:004011EC                 mov     [ebp+var_21], 0EEh
ups0:004011F0                 mov     [ebp+var_20], 15h
ups0:004011F4                 mov     [ebp+var_1F], 22h
ups0:004011F8                 mov     [ebp+var_1E], 0C4h
ups0:004011FC                 mov     [ebp+var_1D], 0E5h
ups0:00401200                 mov     [ebp+var_1C], 0C9h
ups0:00401204                 mov     [ebp+var_1B], 0A0h
ups0:00401208                 mov     [ebp+var_1A], 0A5h
ups0:0040120C                 mov     [ebp+var_19], 0Ch
ups0:00401210                 mov     [ebp+var_18], 0D3h
ups0:00401214                 mov     [ebp+var_17], 0DCh
ups0:00401218                 mov     [ebp+var_16], 51h
ups0:0040121C                 mov     [ebp+var_15], 0C7h
ups0:00401220                 mov     [ebp+var_14], 39h
ups0:00401224                 mov     [ebp+var_13], 0FDh
ups0:00401228                 mov     [ebp+var_12], 0D0h
ups0:0040122C                 mov     [ebp+var_11], 0F8h
ups0:00401230                 mov     [ebp+var_10], 3Bh
ups0:00401234                 mov     [ebp+var_F], 0E8h
ups0:00401238                 mov     [ebp+var_E], 0CCh
ups0:0040123C                 mov     [ebp+var_D], 3
ups0:00401240                 mov     [ebp+var_C], 6
ups0:00401244                 mov     [ebp+var_B], 43h
ups0:00401248                 mov     [ebp+var_A], 0F7h
ups0:0040124C                 mov     [ebp+var_9], 0DAh
ups0:00401250                 mov     [ebp+var_8], 7Eh
ups0:00401254                 mov     [ebp+var_7], 65h
ups0:00401258                 mov     [ebp+var_6], 0AEh
ups0:0040125C                 mov     [ebp+var_5], 80h
ups0:00401260                 mov     eax, [ebp+Str]
ups0:00401263                 push    eax             ; Str
ups0:00401264                 call    strlen
ups0:0040126A                 add     esp, 4
ups0:0040126D                 cmp     eax, 10h
ups0:00401270                 jnz     loc_401361
ups0:00401276                 mov     [ebp+var_38], 0
ups0:0040127D                 mov     [ebp+counter], 0
ups0:00401284                 jmp     short loc_40128F
ups0:00401286 ; ---------------------------------------------------------------------------
ups0:00401286
ups0:00401286 loc_401286:                             ; CODE XREF: sub_4011B0+1A8j
ups0:00401286                 mov     ecx, [ebp+counter]
ups0:00401289                 add     ecx, 1
ups0:0040128C                 mov     [ebp+counter], ecx
ups0:0040128F
ups0:0040128F loc_40128F:                             ; CODE XREF: sub_4011B0+D4j
ups0:0040128F                 cmp     [ebp+counter], 28h
ups0:00401293                 jge     loc_40135D
ups0:00401299                 mov     [ebp+var_30], 0
ups0:004012A0                 mov     edx, [ebp+Str]  ; Set edx to the first position of the string.
ups0:004012A3                 add     edx, [ebp+counter] ; Use the counter variable to set the value of EDX to the character at the counter (index) position of the string.
ups0:004012A6                 movsx   eax, byte ptr [edx] ; Set EAX to the value at that byte.
ups0:004012A9                 xor     eax, 33h        ; XOR value with 0x33
ups0:004012AC                 and     eax, 0FFh       ; AND 0xFF
ups0:004012B1                 mov     [ebp+var_30], eax ; Store the value in VAR_30
ups0:004012B4                 call    check_CheckRemoteDebuggerPresent
ups0:004012B9                 movzx   ecx, al
ups0:004012BC                 test    ecx, ecx        ; Check result of debugger check. If zero, continue (take the jz jump); else jmp to the end.
ups0:004012BE                 jz      short loc_4012C7
ups0:004012C0                 xor     al, al
ups0:004012C2                 jmp     loc_401363
ups0:004012C7 ; ---------------------------------------------------------------------------
ups0:004012C7
ups0:004012C7 loc_4012C7:                             ; CODE XREF: sub_4011B0+10Ej
ups0:004012C7                 mov     edx, [ebp+var_30] ; Restore the value from var_30 into EDX
ups0:004012CA                 add     edx, 44h        ; Add 0x44
ups0:004012CD                 and     edx, 0FFh       ; AND 0xff
ups0:004012D3                 mov     [ebp+var_30], edx ; Store the value back into var_30
ups0:004012D6                 call    check_for_ollydbg
ups0:004012DB                 movzx   eax, al
ups0:004012DE                 test    eax, eax        ; Check the return value of the check_for_ollydbg.  If false (0 in the ZF), continue (take the jz jump); else jmp to the end.
ups0:004012E0                 jz      short loc_4012E6
ups0:004012E2                 xor     al, al
ups0:004012E4                 jmp     short loc_401363
ups0:004012E6 ; ---------------------------------------------------------------------------
ups0:004012E6
ups0:004012E6 loc_4012E6:                             ; CODE XREF: sub_4011B0+130j
ups0:004012E6                 mov     ecx, [ebp+var_30] ; Restore value from var_30 into ECX
ups0:004012E9                 xor     ecx, 55h        ; XOR ECX with 0x55
ups0:004012EC                 and     ecx, 0FFh       ; AND ECX with 0xff
ups0:004012F2                 mov     [ebp+var_30], ecx ; Store the value of ECX into var_30
ups0:004012F5                 call    check_IsDebuggerPresent
ups0:004012FA                 movzx   edx, al
ups0:004012FD                 test    edx, edx        ; Check the return value of check_isDebuggerPresent.  If false (0 in the ZF), continue (take the jz jump); else jmp to the end.
ups0:004012FF                 jz      short loc_401305
ups0:00401301                 xor     al, al
ups0:00401303                 jmp     short loc_401363
ups0:00401305 ; ---------------------------------------------------------------------------
ups0:00401305
ups0:00401305 loc_401305:                             ; CODE XREF: sub_4011B0+14Fj
ups0:00401305                 mov     eax, [ebp+var_30] ; Restore the value of var_30 into EAX.
ups0:00401308                 sub     eax, 66h        ; SUB 0x33 from EAX
ups0:0040130B                 and     eax, 0FFh       ; AND EAX with 0xff
ups0:00401310                 mov     [ebp+var_30], eax ; Store the value of EAX into var_30
ups0:00401313                 call    check_rdtsc_diff
ups0:00401318                 movzx   ecx, al
ups0:0040131B                 test    ecx, ecx        ; Check the return value from the rdtsc diff check.  If false (0 in the ZF), continue (take the jz jump); else jmp to the end.
ups0:0040131D                 jz      short loc_401323
ups0:0040131F                 xor     al, al
ups0:00401321                 jmp     short loc_401363
ups0:00401323 ; ---------------------------------------------------------------------------
ups0:00401323
ups0:00401323 loc_401323:                             ; CODE XREF: sub_4011B0+16Dj
ups0:00401323                 mov     edx, [ebp+var_38] ; Load the value of var_38 into EDX
ups0:00401326                 and     edx, 0FFh       ; AND EDX with 0xff
ups0:0040132C                 xor     edx, [ebp+var_30] ; XOR EDX with the value from var_30
ups0:0040132F                 and     edx, 0FFh       ; AND EDX with 0ff
ups0:00401335                 mov     [ebp+var_30], edx ; Store EDX into var_30
ups0:00401338                 mov     eax, [ebp+counter] ; Load value of the counter into EAX
ups0:0040133B                 movsx   ecx, [ebp+eax+var_2C] ; Start at the location of var_2c, add the value of the counter (EAX) to get the value hard coded into the fuction to work this comparison against.
ups0:00401340                 and     ecx, 0FFh       ; AND ECX with 0xff
ups0:00401346                 cmp     [ebp+var_30], ecx ; Check to see if var_30 is equal to ECX.  If false (0 in the ZF), continue (take the jz jump); else jmp to the end (aka. exit loop).
ups0:00401349                 jz      short loc_40134F
ups0:0040134B                 xor     al, al
ups0:0040134D                 jmp     short loc_401363
ups0:0040134F ; ---------------------------------------------------------------------------
ups0:0040134F
ups0:0040134F loc_40134F:                             ; CODE XREF: sub_4011B0+199j
ups0:0040134F                 mov     edx, [ebp+var_38] ; Move the value of var_38 into EDX (inital value is 0)
ups0:00401352                 add     edx, [ebp+var_30] ; Add var_30 to EDX (var_38)
ups0:00401355                 mov     [ebp+var_38], edx ; Move the value of EDX into var_38
ups0:00401358                 jmp     loc_401286      ; Repeat loop
ups0:0040135D ; ---------------------------------------------------------------------------
ups0:0040135D
ups0:0040135D loc_40135D:                             ; CODE XREF: sub_4011B0+E3j
ups0:0040135D                 mov     al, 1
ups0:0040135F                 jmp     short loc_401363
ups0:00401361 ; ---------------------------------------------------------------------------
ups0:00401361
ups0:00401361 loc_401361:                             ; CODE XREF: sub_4011B0+C0j
ups0:00401361                 xor     al, al
ups0:00401363
ups0:00401363 loc_401363:                             ; CODE XREF: sub_4011B0+112j
ups0:00401363                                         ; sub_4011B0+134j ...
ups0:00401363                 mov     ecx, [ebp+var_4]
ups0:00401366                 xor     ecx, ebp
ups0:00401368                 call    @[email protected] ; __security_check_cookie(x)
ups0:0040136D                 mov     esp, ebp
ups0:0040136F                 pop     ebp
ups0:00401370                 retn
ups0:00401370 sub_4011B0      endp
ups0:00401370
ups0:00401370 ; ---------------------------------------------------------------------------
comments powered by Disqus