From: Jessica <jessica@megacortek.com>
To: w3pwnz <w3pwnz@megacortek.com>
Subject: New email from our contact
Attachments : executable2.ndh
Thank you again for your help, our technical staff has a pretty good overview
of the new device designed by Sciteek. Your account will be credited with $500.

You did work hard enough to impress me, your help is still more than welcome,
you will get nice rewards. Our anonymous guy managed to get access to another
bunch of files. Here is one of his emails:

---
Hi there, see attached file for more information. It was found on
http://sci.nuitduhack.com/EgZ8sv12.
---

Maybe you can get further than him by exploiting this website.
We also need to get
as much information as possible about the file itself. If you succeed, you will
be rewarded with $2500 for the ndh file and $1000 for the website. Please use
"Sciteek shortener" and "strange binary file #2" titles.

Regards,
Jessica.


This challenge was another crackme. I called it "VMception", and it was a little harder than the other one =)

First we noticed the string "password is wrong:" (0x8737), and another a little stranger "Unhandled exception occured during execution. Exiting" (0x874b) and search for their references :

0x85f6: movl r0, #0x874b		; <-- unhandled exception
0x85fb: call 0xfb7a
0x85ff: end
------
0x8672: movl r0, #0x8737		; <-- password is wrong
0x8677: call 0xfafe
0x867b: end

The entry point of the crackme is a "jmpl 0x0624", corresponding to the following routine :

0x8627: movl r0, #0x867c		; <-- source = 0x867c
0x862c: movl r1, #0x86e8
0x8631: sub r1, r0			; <-- 0x86e8 - 0x867c = 0x6C = 108
0x8635: call 0xfc64			; --> 0x829d (memcpy_to_0x000a)
0x8639: movl r0, #0x0000	
0x863e: movl r1, #0x0035
0x8643: movl r2, #0x0000
0x8648: movl r3, #0x0000
0x864d: call 0xfc67
0x8651: call 0xffab			; --> 0x8600 (ask password)
0x8655: call 0xfed8			; --> 0x8531 (VMception)
0x8659: movl r0, #0x0000		; <-- 0x0000 = buffer password
0x865e: call 0xfca9	
0x8662: test r0, r0
0x8665: jz 0x000a			; --> goto password_is_wrong if r0 = 0
0x8668: movl r0, #0x872a
0x866d: call 0xfb90			; --> pwned \o/ 
0x8671: end

The first action is to copy 0x6c bytes of data from 0x867c to 0x000a :

niklos@box:~/ndh/jessica4# vmndh -file executable2.ndh -debug
[Console]#> x/x 0x867c:0x6c
0x867c: 0a 06 06 00 00 0b 02 07 4d 06 00 07 02 07 78 07
0x868c: 00 07 09 02 02 07 61 06 01 07 02 07 02 07 01 07
0x869c: 09 02 02 07 72 06 02 07 02 07 43 07 02 07 09 02
0x86ac: 02 07 31 06 03 07 02 07 45 07 03 07 09 02 02 07
0x86bc: 30 06 04 07 02 07 03 07 04 07 09 02 02 07 4c 06
0x86cc: 05 07 02 07 7f 07 05 07 09 02 02 07 64 06 06 07
0x86dc: 02 07 0f 07 06 07 09 02 02 00 01 0b

So far, we've got no idea of what the hell it could be, except that's probably vicious... We need to go deeper :)
Then, a 8bytes password is read, and stored in 0x0000 (which is quite surprising when you forget that you're in a very simple virtual machine). Next is a quite big routine, but quite easily understandable :

0x8531: push r2
0x8534: call 0xfdf9			; --> 0x8331 (code = get_next_ element)
0x8538: cmpb r0, #11		; --> code == 0xb
0x853c: jnz 0x0003
0x853f: jmpl 0x008f 			; --> goto 0x85d1 (BREAK)
0x8542: cmpb r0, #01		; --> code == 0x01		
0x8546: jnz 0x0007
0x8549: call 0xfeec
0x854d: jmpl 0xffe4
0x8550: cmpb r0, #02		; --> code == 0x2
0x8554: jnz 0x0007
0x8557: call 0xff0e
0x855b: jmpl 0xffd6			; --> loop
 
[...]
 
0x85c0: cmpb r0, #0a			; --> code == 0xa
0x85c4: jnz 0x0007
0x85c7: call 0xff5d
0x85cb: jmpl 0xff66			; --> loop
0x85ce: jmpl 0x0025			; --> 0x85f6 (unhandled_exception)
 
.label BREAK:
[...]
0x85f5: ret

First, this routine get an opcode from the buffer we previously copied in 0x000a, by calling 0x8331. Then, there's a switch/case test on this code, with a registered action for each from 0x00 to 0x0a, and 0x0b is the code signifying the end of the routine.

We now have a better understanding of the crackme : VMception : a VM inside a VM, and the buffer in 0x000a contains our program (we already see that most bytes are in [0x0 - 0xa] and it ends by 0xb.

Now comes the fun part when you trace the program opcode by opcode to understand the corresponding subroutines...
Here is what you can say then :

  • The password is actually 7bytes long, since the 8th byte is overitten (by a value used to check a byte of the password btw)
  • The password is checked one char at the time, and a single check use several opcodes
  • Opcodes used to do a single check are : 0x2 (routine 0x8469) 0x6 (routine 0x83a4), 0x7 (routine 0x8495), and 0x9 (routine 0x84fe)
  • The program-counter is stored at 0x9 (just before the program itself)

Let's check the subroutines of opcodes 0x02, 0x6, 0x7 and 0x9, starting with 0x2 :

[...]
0x8472: call 0xfebb		; --> 0x8331  (get_next_element = always 7)
0x8476: mov r1, r0	
0x847a:  call 0xfeb3		; --> 0x8331 (get_next_element = X, a value outside [0,a], like 0x4d, 0x78, ...)
0x847e: mov r2, r0
0x8482: mov r0, r1
0x8486: mov r1, r2
0x848a: call 0xfe90		; --> 0x831e (write_r0_at_r1 : writes the )
[...]
0x8494: ret

So this routine retrieves a value that is not an opcode, and stores it at 0x7.
Now 0x6 :

[...]
0x83b0: call 0xff7d		; --> 0x8331 (get_next_element = index i in the password of the char to be tested)
0x83b4: mov r3, r0
0x83b8: call 0xff75		; --> 0x8331 (get_next_ element = always 7)
0x83bc: call 0xff4b		; --> 0x830b (return *R0 == get [0x07])
0x83c0: mov r2, r0
0x83c4: mov r0, r3
0x83c8: call 0xff3f		; --> 0x830b (return *R0 = get password[i], the value of the char to be tested)
0x83cc: xor r0, r2		; --> XOR
0x83d0: mov r1, r0		
0x83d4: mov r0, r3		
0x83d8: call 0xff42		; --> 0x831e (write_r1_at_r0 = write the xored value at password[i])
[...]
0x83e4: ret

This one takes the previous value at 0x7 and XOR the current byte of the password with it.
Now 0x7 :

[...]
0x849e: call 0xfe8f		; --> 0x8331 (get_next_element = index i in the password of the char to be tested)
0x84a2: call 0xfe65		; --> 0x830b (return *R0 = get password[i], the value of the char to be tested)
0x84a6: mov r1, r0		
0x84aa: call 0xfe83		; --> 0x8331 (get_next_ element = always 7)
0x84ae: call 0xfe59		; --> 0x830b (return *R0 == get [0x07])
0x84b2: mov r2, r0	
0x84b6: cmp r1, r2		; --> CMP password[i], [0x7]
0x84ba: movl r1, #0x00	; --> R1 = 0
0x84bf: jnz 0x0002
0x84c2: inc r1			; --> R1 = 1
0x84c4: movl r0, #0x08	; --> R0 = 8
0x84c9: call 0xfe51		; --> 0x831e (write_r0_at_r1 = writes the result of CMP at 0x8)
[...]
0x84d3: ret

This routine compares the current byte of the password with some value stored in 0x7.
And finally 0x9 :

[...]
0x8504: call 0xfe29		; --> 0x8331 (get_next_element = always 0x2)
0x8508: mov r1, r0		
0x850c: movl r0, #0x0008	; --> R0 = 8
0x8511: call 0xfdf6		; --> 0x830b (return *R0 = get [0x08])
0x8515: test r0, r0
0x8518: jnz 0x0008
0x851b: mov r0, r1
0x851f: call 0xfe30		; --> IF [0x08] == 0 THEN : 0x8353 (change_program_counter by 0x2)
[...]
0x8527: ret

This one changes the program counter by 0x2 if the previous comparison fails.

We now know how one byte of the password is checked :

  • 0x02 : store next element at 0x7
  • 0x06 : XOR password[i] and [0x7]
  • 0x02 : store next element at 0x7
  • 0x07 : compare xored value and [0x7] and write the result at 0x8
  • 0x09 : f*ck up program counter if [0x8] is 0

So with 3 breakpoints in routines of opcodes 0x6, 0x7 and 0x9, we can get the xor keys and the xored values :

niklos@box:~/ndh/jessica4# vmndh -file executable2.ndh -debug
[Console]#> bp 0x83c0
Breakpoint set in 0x83c0
[Console]#> bp 0x84b6
Breakpoint set in 0x84b6
[Console]#> bp 0x8515
Breakpoint set in 0x8515
[Console]#> run
[...]
[SYSCALL output]: Please enter Sciteek admin password:
AAAAAAA
[...]
[BreakPoint 1 - 0x83c0]
0x83c0 > mov r2, r0
[Console]#> info reg
[r0]: 004d		; --> first xor key is 0x4d
[...]
[Console]#> run
[BreakPoint 2 - 0x84b6]
0x84b6 > cmp r1, r2
[Console]#> info reg
[r0]: 0078		; --> first xored value is 0x78
[Console]#> run
[...]
[BreakPoint 3 - 0x8515]
0x8515 > test r0, r0
[Console]#> set r1=1	; --> set the result of the comparison to 1
r1 = 0x0001
[Console]#> run

[...]

And so on for the 7 bytes of the password :)

Now we xor all this and get the password :

>>> a = [0x4d, 0x61, 0x72, 0x31, 0x30, 0x4c, 0x64]
>>> b = [0x78, 0x02, 0x43, 0x45, 0x03, 0x7f, 0x0f]
>>> print ''.join([ chr(a[i] ^ b[i]) for i in range(len(a)) ])
5c1t33k

Bazinga \o/