Friday, December 30, 2011

Can you crack it ? A quite detailed solution (Part 3)

This is Part 3/3. You can find the previous parts here : Part 1, Part 2

Prerequisites

If you don't want to get lost you need to know about calling conventions. Through this part arguments are pushed on the stack in the reverse order. There is one additional trick : arguments are not really pushed on the stack. Actually the necessary space for function arguments is already reserved and they are moved directly to the stack to the correct offset relative to esp.

Analysis of the executable file

On part 2 we found a link to this file : www.canyoucrackit.co.uk/da75370fe15c4148bd4ceec861fbdaa5.exe
(I made a local copy in case the original file is not available anymore. I just changed the extension to .bin : da75370fe15c4148bd4ceec861fbdaa5.bin)

Let's give it a nice name and examine it first.
$ mv da75370fe15c4148bd4ceec861fbdaa5.exe keygen.exe

$ objdump -x keygen.exe
keygen.exe:     file format pei-i386
keygen.exe
architecture: i386, flags 0x00000102:
EXEC_P, D_PAGED
start address 0x00401000

Characteristics 0x30f
        relocations stripped
        executable
        line numbers stripped
        symbols stripped
        32 bit words
        debugging information removed

Time/Date               Fri Aug  5 15:29:54 2011
Magic                   010b    (PE32)
MajorLinkerVersion      2
MinorLinkerVersion      20
SizeOfCode              00000c00
SizeOfInitializedData   00000600
SizeOfUninitializedData 00000200
AddressOfEntryPoint     00001000
BaseOfCode              00001000
BaseOfData              00002000
ImageBase               00400000
SectionAlignment        00001000
FileAlignment           00000200
MajorOSystemVersion     4
MinorOSystemVersion     0
MajorImageVersion       1
MinorImageVersion       0
MajorSubsystemVersion   4
MinorSubsystemVersion   0
Win32Version            00000000
SizeOfImage             00005000
SizeOfHeaders           00000400
CheckSum                00005050
Subsystem               00000003        (Windows CUI)
DllCharacteristics      00000000
SizeOfStackReserve      00200000
SizeOfStackCommit       00001000
SizeOfHeapReserve       00100000
SizeOfHeapCommit        00001000
LoaderFlags             00000000
NumberOfRvaAndSizes     00000010

The Data Directory
Entry 0 00000000 00000000 Export Directory [.edata (or where ever we found it)]
Entry 1 00004000 00000338 Import Directory [parts of .idata]
Entry 2 00000000 00000000 Resource Directory [.rsrc]
Entry 3 00000000 00000000 Exception Directory [.pdata]
Entry 4 00000000 00000000 Security Directory
Entry 5 00000000 00000000 Base Relocation Directory [.reloc]
Entry 6 00000000 00000000 Debug Directory
Entry 7 00000000 00000000 Description Directory
Entry 8 00000000 00000000 Special Directory
Entry 9 00000000 00000000 Thread Storage Directory [.tls]
Entry a 00000000 00000000 Load Configuration Directory
Entry b 00000000 00000000 Bound Import Directory
Entry c 00000000 00000000 Import Address Table Directory
Entry d 00000000 00000000 Delay Import Directory
Entry e 00000000 00000000 CLR Runtime Header
Entry f 00000000 00000000 Reserved

There is an import table in .idata at 0x404000

The Import Tables (interpreted .idata section contents)
 vma:            Hint    Time      Forward  DLL       First
                 Table   Stamp     Chain    Name      Thunk
 00004000       00004054 00000000 00000000 000042a4 000040d0

        DLL Name: cygcrypt-0.dll
        vma:  Hint/Ord Member-Name Bound-To
        4148        0  crypt

 00004014       0000405c 00000000 00000000 00004318 000040d8

        DLL Name: cygwin1.dll
        vma:  Hint/Ord Member-Name Bound-To
        4150       59  __main
        415c      182  _dll_crt0@0
        416c      275  _fopen64
        4178      367  _impure_ptr
        4188      741  calloc
        4194      788  connect
        41a0      839  cygwin_detach_dll
        41b4      841  cygwin_internal
        41c8      862  dll_dllcrt0
        41d8      929  fclose
        41e4      999  free
        41ec     1006  fscanf
        41f8     1073  gethostbyname
        4208     1147  htons
        4210     1277  malloc
        421c     1295  memcpy
        4228     1299  memset
        4234     1385  printf
        4240     1493  realloc
        424c     1496  recv
        4254     1575  send
        425c     1663  socket
        4268     1673  sprintf
        4274     1691  strcmp
        4280     1702  strlen

 00004028       000040c4 00000000 00000000 00004328 00004140

        DLL Name: KERNEL32.dll
        vma:  Hint/Ord Member-Name Bound-To
        428c      529  GetModuleHandleA

 0000403c       00000000 00000000 00000000 00000000 00000000

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         00000a50  00401000  00401000  00000400  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         000001a0  00402000  00402000  00001000  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000120  00403000  00403000  00000000  2**3
                  ALLOC
  3 .idata        00000338  00404000  00404000  00001200  2**2
                  CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
no symbols
This file is in Portable Executable format and it imports functions from cygwin DLLs.

We need to boot under Windows and use cygwin to run it. But first let's use a good Win32 disassembler to see exactly what it is doing. Personally I used W32dasm which is quite good.
I won't kill you with a full listing here since it is more than 1000 lines long. I will only dump the full listing of the main function. In the comments I explicitly reconstructed function calls.
:004010BA C745F400000000          mov [ebp-0C], 00000000  ;Sets a flag to 0
:004010C1 C704244E204000          mov dword ptr [esp], 0040204E

* Reference To: cygwin1.printf, Ord:0569h
                                  |
:004010C8 E88B040000              Call 00401558
; printf("keygen.exe")
:004010CD 837D0802                cmp dword ptr [ebp+08], 00000002
; if (argc != 2) {...
:004010D1 7418                    je 004010EB

* Possible StringData Ref from Data Obj ->"usage: keygen.exe hostname"
                                  |
:004010D3 C704245C204000          mov dword ptr [esp], 0040205C

* Reference To: cygwin1.printf, Ord:0569h
                                  |
:004010DA E879040000              Call 00401558
; printf("usage: keygen.exe hostname")
:004010DF C745B0FFFFFFFF          mov [ebp-50], FFFFFFFF
:004010E6 E919010000              jmp 00401204
; jump to exit ... }

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010D1(C)
|

* Possible StringData Ref from Data Obj ->"r"
                                  |
:004010EB C744240478204000        mov [esp+04], 00402078

* Possible StringData Ref from Data Obj ->"license.txt"
                                  |
:004010F3 C704247A204000          mov dword ptr [esp], 0040207A

* Reference To: cygwin1._fopen64, Ord:0113h
                                  |
:004010FA E851040000              Call 00401550
; fopen("license.txt","r")
:004010FF 8945B4                  mov dword ptr [ebp-4C], eax  
; The result is stored in [ebp-4C], this is a file descriptor
:00401102 837DB400                cmp dword ptr [ebp-4C], 00000000  
; if ([ebp-4c] == 0) { ...
:00401106 7518                    jne 00401120

* Possible StringData Ref from Data Obj ->"error: license.txt not found"
                                  |
:00401108 C7042486204000          mov dword ptr [esp], 00402086

* Reference To: cygwin1.printf, Ord:0569h
                                  |
:0040110F E844040000              Call 00401558
; printf("error: license.txt not found")
:00401114 C745B0FFFFFFFF          mov [ebp-50], FFFFFFFF
:0040111B E9E4000000              jmp 00401204
; jump to exit ... }

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401106(C)
|
:00401120 C744240818000000        mov [esp+08], 00000018
:00401128 C744240400000000        mov [esp+04], 00000000
:00401130 8D45C8                  lea eax, dword ptr [ebp-38]
:00401133 890424                  mov dword ptr [esp], eax

* Reference To: cygwin1.memset, Ord:0513h
                                  |
:00401136 E80D040000              Call 00401548
; memset([ebp-38],0,0x18)  //[ebp-38] = buffer of size 0x18=24
:0040113B 8D45C8                  lea eax, dword ptr [ebp-38]
:0040113E 89442408                mov dword ptr [esp+08], eax

* Possible StringData Ref from Data Obj ->"%s"
                                  |
:00401142 C7442404A4204000        mov [esp+04], 004020A4
:0040114A 8B45B4                  mov eax, dword ptr [ebp-4C]
:0040114D 890424                  mov dword ptr [esp], eax

* Reference To: cygwin1.fscanf, Ord:03EEh
                                  |
:00401150 E8EB030000              Call 00401540
; fscanf([ebp-4C], %s, &[ebp-38])  //[ebp-4c] = file descriptor
:00401155 8B45B4                  mov eax, dword ptr [ebp-4C]
:00401158 890424                  mov dword ptr [esp], eax

* Reference To: cygwin1.fclose, Ord:03A1h
                                  |
:0040115B E8D8030000              Call 00401538
; fclose([ebp-4C]);
:00401160 C745B400000000          mov [ebp-4C], 00000000
:00401167 817DC867636871          cmp dword ptr [ebp-38], 71686367
; if ([ebp-38] != 0x71686367) goto error; //it is 'gchq'
:0040116E 755F                    jne 004011CF

* Possible StringData Ref from Data Obj ->"hqDTK7b8K2rvw"
                                  |
:00401170 A100204000              mov eax, dword ptr [00402000]
:00401175 89442404                mov dword ptr [esp+04], eax
:00401179 8D45C8                  lea eax, dword ptr [ebp-38]
:0040117C 83C004                  add eax, 00000004
:0040117F 890424                  mov dword ptr [esp], eax

* Reference To: cygcrypt-0.crypt, Ord:0000h
                                  |
:00401182 E8A5020000              Call 0040142C
; crypt(&[ebp-38]+4,"hqDTK7b8K2rvw")
:00401187 89C2                    mov edx, eax

* Possible StringData Ref from Data Obj ->"hqDTK7b8K2rvw"
                                  |
:00401189 A100204000              mov eax, dword ptr [00402000]
:0040118E 89442404                mov dword ptr [esp+04], eax
:00401192 891424                  mov dword ptr [esp], edx

* Reference To: cygwin1.strcmp, Ord:069Bh
                                  |
:00401195 E896030000              Call 00401530
; strcmp(result,"hqDTK7b8K2rvw") //Compare crypt result with hqDTK7b8K2rvw
:0040119A 85C0                    test eax, eax
:0040119C 7507                    jne 004011A5
:0040119E C745F401000000          mov [ebp-0C], 00000001
; Set [ebp-0C] to 1 if crypt's result was ok. 

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040119C(C)
|

* Possible StringData Ref from Data Obj ->"loading stage1 license key(s)..."
                                  |
:004011A5 C70424A8204000          mov dword ptr [esp], 004020A8

* Reference To: cygwin1.printf, Ord:0569h
                                  |
:004011AC E8A7030000              Call 00401558
; printf("loading stage1 license key(s)...");
:004011B1 8B45D4                  mov eax, dword ptr [ebp-2C]
:004011B4 8945B8                  mov dword ptr [ebp-48], eax
; [ebp-48] = [ebp-2C]. 4 bytes are copied from the fscanfed buffer

* Possible StringData Ref from Data Obj ->"loading stage2 license key(s)..."
                                  |
:004011B7 C70424CC204000          mov dword ptr [esp], 004020CC

* Reference To: cygwin1.printf, Ord:0569h
                                  |
:004011BE E895030000              Call 00401558
; printf("loading stage2 license key(s)...");
:004011C3 8B45D8                  mov eax, dword ptr [ebp-28]
:004011C6 8945BC                  mov dword ptr [ebp-44], eax
:004011C9 8B45DC                  mov eax, dword ptr [ebp-24]
:004011CC 8945C0                  mov dword ptr [ebp-40], eax
; [ebp-40]=[ebp-24] and [ebp-44]=[ebp-28]. 8 bytes are copied for stage 2

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040116E(C)
|
:004011CF 837DF400                cmp dword ptr [ebp-0C], 00000000  
; if ([ebp-0C] != 0) { ... //Checks if crypt result was 0
:004011D3 7515                    jne 004011EA

* Possible StringData Ref from Data Obj ->"error: license.txt invalid" 
                                  |
:004011D5 C70424EF204000          mov dword ptr [esp], 004020EF

* Reference To: cygwin1.printf, Ord:0569h
                                  |
:004011DC E877030000              Call 00401558
; printf("error: license.txt invalid")
:004011E1 C745B0FFFFFFFF          mov [ebp-50], FFFFFFFF
:004011E8 EB1A                    jmp 00401204
; jump to exit ... }

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004011D3(C)
|
:004011EA 8D45B8                  lea eax, dword ptr [ebp-48]
:004011ED 89442404                mov dword ptr [esp+04], eax
:004011F1 8B450C                  mov eax, dword ptr [ebp+0C]
:004011F4 83C004                  add eax, 00000004
:004011F7 8B00                    mov eax, dword ptr [eax]
:004011F9 890424                  mov dword ptr [esp], eax
:004011FC E808000000              call 00401209  
; Calls a procedure with params (&[ebp+0C]+4, &[ebp-48]) 
; It is something like build_request(argv[1], &stage1_license)

Basically it reads a file license.txt and performs some checks before calling a function that I named build_request. I will spare you the details about this function. All it does is build a HTTP GET request with the parameters passed to it and send it. The request and reply are printed to the terminal.

First thing we notice is that the program should be called with 1 argument. The hostname to which the request will be sent. Obviously we will try with www.canyoucrackit.co.uk.

Bypassing the password check

Second thing is that there is a kind of password check. The program reads it from the file and runs it through the libc function crypt. It is a crypt(password, salt) in DES mode so only the first 8 bytes of the password and the 2 first bytes of the salt are used.
There are two ways to pass the password check :
  1. Find the actual password with a password cracker. We should find a password that resolves to hqDTK7b8K2rvw when crypted with hq as a salt. This can take some time.
  2. Or since the original password is used nowhere else than for this check we can change the x86 assembler opcode to inverse the test and continue the normal execution path if the password is incorrect.
We are going to choose the second option. This is the fastest way. We need to inverse the test done in the highlighted line in the code above. The original test is jne which opcode is 0x75 and the binary for the inverse test je is 0x74. We are going to change
:0040119C 7507                    jne 004011A5
into
:0040119C 7407                    je 004011A5

We can change it with our disassembler if it can do so or simply with the shell.
$ xxd -p keygen.exe | sed s/7507/7407/ | xxd -p -r > keygen.exe

Moreover with this first analysis we can draw a draft of the stack for the main() function.
ebp-50 0000 0000  ; Return code variable
ebp-4C 0000 0000  ; filedescriptor for fopen("license.txt")
ebp-48 ???? ????  ; Contains stage1 license key gotten from [ebp-2C]
ebp-44 ???? ????  ; Contains stage2 license key part1 gotten from [ebp-28]
ebp-40 ???? ????  ; Contains stage2 license key part2 gotten from [ebp-24]
ebp-3C ???? ???? 
ebp-38 6763 6871  ; buffer of size 0x18 = 24, first 2 bytes : magic number
ebp-34 ???? ????  ; password for which crypt(password,"hq") should be "hqDTK7b8K2rvw"
ebp-30 ???? ????  ; next 4 bytes of password
ebp-2C 0000 0000  ; Stage1 license key, 4 bytes
ebp-28 0000 0000  ; Stage2 license key first 4 bytes
ebp-24 0000 0000  ; Stage2 license key last 4 bytes
ebp-20 ???? ???? 
ebp-1C ???? ????
ebp-18 ???? ????
ebp-14 ???? ???? 
ebp-10 ???? ???? 
ebp-C  ???? ???? ; Variable that contains 1 is the password is correct
ebp-8  ???? ???? 
ebp-4  ???? ???? 
ebp    xxxx xxxx ; Saved EBP 
ebp+4  xxxx xxxx ; Saved EIP
ebp+8  0000 0000 ; argc
ebp+C  0000 0000 ; argv
ebp+10 0000 0000 ; argv[1]

Forging the file license.txt

The next step is to create the file license.txt. With our analysis we know that the file should contain the following.
<4 bytes Magic Number><8 bytes password><4 bytes Stage1 license key><8 bytes Stage2 license key>

Magic Number

We know that it needs to begin with gchq.
$ printf "gchq" > license.txt

Password

It is followed by 8 bytes of password for which we can put anything we want since we have bypassed the check.
$ printf "PASSWORD" >> license.txt

Stage 1 License Key

For that step the analysis didn't give us the value we should use. But remember that on part 2 of the challenge there was 4 bytes that were jumped over just at the beginning of the program.
00000000  EB04              jmp short 0x6
00000002  AF                scasd          ;Unused byte
00000003  C2BFA3            ret 0xa3bf     ;Unused bytes
[...]
Those 4 bytes are AFC2 BFA3. Let's use them for stage 1 license key.
$ echo "AFC2 BFA3" | xxd -p -r >> license.txt

Stage 2 License Key

On part 2 there was 8 bytes of data in VM.cpu.firmware that we could not make sense of.
firmware: [0xd2ab1f05, 0xda13f110]
Let's not forget about the endianness. It gives us stage 2 license key : 051F ABD2 10F1 13DA.
$ echo "051F ABD2 10F1 13DA" | xxd -p -r >> license.txt

Running the program

Time to run the program
$ ./keygen.exe www.canyoucrackit.co.uk
keygen.exe

loading stage1 license key(s)...
loading stage2 license key(s)...

request:

GET /hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt HTTP/1.0

response:

HTTP/1.1 404 Not Found
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Fri, 30 Dec 2011 14:48:44 GMT
Connection: close
Content-Length: 315

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Not Found</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Not Found</h2>
<hr><p>HTTP Error 404. The requested resource is not found.</p>
</BODY></HTML>


We get a 404 response. We could think that our license file is wrong but it is not. Strangely the HTTP GET request sent by the program misses a very important part : the Host header.
In order for it to work it should be
GET /hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt HTTP/1.0
Host: www.canyoucrackit.co.uk


Let's try it away with netcat (install the package for cygwin if you don't have it).
$ printf "GET /hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt HTTP/1.0\r\n\
Host: www.canyoucrackit.co.uk\r\n\r\n" | nc www.canyoucrackit.co.uk 80
HTTP/1.1 200 OK
Content-Type: text/plain
Last-Modified: Wed, 26 Oct 2011 08:40:14 GMT
Accept-Ranges: bytes
ETag: "bc46bae1ba93cc1:0"
Server: Microsoft-IIS/7.5
Date: Fri, 30 Dec 2011 15:06:15 GMT
Connection: close
Content-Length: 37

Pr0t3ct!on#cyber_security@12*12.2011+
Here is the password ! (We could also have gotten it by visiting  : http://www.canyoucrackit.co.uk/hqDTK7b8K2rvw/a3bfc2af/d2ab1f05/da13f110/key.txt)
Finally we just have to enter it to the front page of the website canyoucrackit. It leads us to this page http://www.canyoucrackit.co.uk/soyoudidit.asp#code

The button register your interest just sends us to gchq-careers.co.uk on a page where we can apply for a position of Cyber Specialist. This page is publicly accessible through their search engine. A bit disappointing.

Final Note

I didn't crack the password myself but for anyone interested the crypted password is cyberwin. It is easy to check.
$ crypt hq cyberwin
hqDTK7b8K2rvw

Monday, December 26, 2011

Can you crack it ? A quite detailed solution (Part 2)

This is Part 2/3. You can find the previous parts here :  Part 1

Prerequisites

There are no real requirements other than a little bit of knowledge about how a CPU works. Nevertheless it is good to know about Bitwise operators and Segmented memory to fully understand this part of the challenge.

Writing our virtual machine

On part 1 the code we decoded led us to this URL : www.canyoucrackit.co.uk/15b436de1f9107f3778aad525e5d0b20.js
(I made a copy in case the original link doesn't work anymore 15b436de1f9107f3778aad525e5d0b20.js)
//--------------------------------------------------------------------------------------------------
//
// stage 2 of 3
//
// challenge:
//   reveal the solution within VM.mem
//
// disclaimer:
//   tested in ie 9, firefox 6, chrome 14 and v8 shell (http://code.google.com/apis/v8/build.html),
//   other javascript implementations may or may not work.
//
//--------------------------------------------------------------------------------------------------

var VM = {
  
  cpu: {
    ip: 0x00,
    
    r0: 0x00,
    r1: 0x00,
    r2: 0x00,
    r3: 0x00,
    
    cs: 0x00,
    ds: 0x10,
    
    fl: 0x00,
    
    firmware: [0xd2ab1f05, 0xda13f110]
  },
  
  mem: [
    0x31, 0x04, 0x33, 0xaa, 0x40, 0x02, 0x80, 0x03, 0x52, 0x00, 0x72, 0x01, 0x73, 0x01, 0xb2, 0x50,
    0x30, 0x14, 0xc0, 0x01, 0x80, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    
    0x98, 0xab, 0xd9, 0xa1, 0x9f, 0xa7, 0x83, 0x83, 0xf2, 0xb1, 0x34, 0xb6, 0xe4, 0xb7, 0xca, 0xb8,
    0xc9, 0xb8, 0x0e, 0xbd, 0x7d, 0x0f, 0xc0, 0xf1, 0xd9, 0x03, 0xc5, 0x3a, 0xc6, 0xc7, 0xc8, 0xc9,
    0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
    0xda, 0xdb, 0xa9, 0xcd, 0xdf, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
    0x26, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
    0x7d, 0x1f, 0x15, 0x60, 0x4d, 0x4d, 0x52, 0x7d, 0x0e, 0x27, 0x6d, 0x10, 0x6d, 0x5a, 0x06, 0x56,
    0x47, 0x14, 0x42, 0x0e, 0xb6, 0xb2, 0xb2, 0xe6, 0xeb, 0xb4, 0x83, 0x8e, 0xd7, 0xe5, 0xd4, 0xd9,
    0xc3, 0xf0, 0x80, 0x95, 0xf1, 0x82, 0x82, 0x9a, 0xbd, 0x95, 0xa4, 0x8d, 0x9a, 0x2b, 0x30, 0x69,
    0x4a, 0x69, 0x65, 0x55, 0x1c, 0x7b, 0x69, 0x1c, 0x6e, 0x04, 0x74, 0x35, 0x21, 0x26, 0x2f, 0x60,
    0x03, 0x4e, 0x37, 0x1e, 0x33, 0x54, 0x39, 0xe6, 0xba, 0xb4, 0xa2, 0xad, 0xa4, 0xc5, 0x95, 0xc8,
    0xc1, 0xe4, 0x8a, 0xec, 0xe7, 0x92, 0x8b, 0xe8, 0x81, 0xf0, 0xad, 0x98, 0xa4, 0xd0, 0xc0, 0x8d,
    0xac, 0x22, 0x52, 0x65, 0x7e, 0x27, 0x2b, 0x5a, 0x12, 0x61, 0x0a, 0x01, 0x7a, 0x6b, 0x1d, 0x67,
    0x75, 0x70, 0x6c, 0x1b, 0x11, 0x25, 0x25, 0x70, 0x7f, 0x7e, 0x67, 0x63, 0x30, 0x3c, 0x6d, 0x6a,
    0x01, 0x51, 0x59, 0x5f, 0x56, 0x13, 0x10, 0x43, 0x19, 0x18, 0xe5, 0xe0, 0xbe, 0xbf, 0xbd, 0xe9,
    0xf0, 0xf1, 0xf9, 0xfa, 0xab, 0x8f, 0xc1, 0xdf, 0xcf, 0x8d, 0xf8, 0xe7, 0xe2, 0xe9, 0x93, 0x8e,
    0xec, 0xf5, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    
    0x37, 0x7a, 0x07, 0x11, 0x1f, 0x1d, 0x68, 0x25, 0x32, 0x77, 0x1e, 0x62, 0x23, 0x5b, 0x47, 0x55,
    0x53, 0x30, 0x11, 0x42, 0xf6, 0xf1, 0xb1, 0xe6, 0xc3, 0xcc, 0xf8, 0xc5, 0xe4, 0xcc, 0xc0, 0xd3,
    0x85, 0xfd, 0x9a, 0xe3, 0xe6, 0x81, 0xb5, 0xbb, 0xd7, 0xcd, 0x87, 0xa3, 0xd3, 0x6b, 0x36, 0x6f,
    0x6f, 0x66, 0x55, 0x30, 0x16, 0x45, 0x5e, 0x09, 0x74, 0x5c, 0x3f, 0x29, 0x2b, 0x66, 0x3d, 0x0d,
    0x02, 0x30, 0x28, 0x35, 0x15, 0x09, 0x15, 0xdd, 0xec, 0xb8, 0xe2, 0xfb, 0xd8, 0xcb, 0xd8, 0xd1,
    0x8b, 0xd5, 0x82, 0xd9, 0x9a, 0xf1, 0x92, 0xab, 0xe8, 0xa6, 0xd6, 0xd0, 0x8c, 0xaa, 0xd2, 0x94,
    0xcf, 0x45, 0x46, 0x67, 0x20, 0x7d, 0x44, 0x14, 0x6b, 0x45, 0x6d, 0x54, 0x03, 0x17, 0x60, 0x62,
    0x55, 0x5a, 0x4a, 0x66, 0x61, 0x11, 0x57, 0x68, 0x75, 0x05, 0x62, 0x36, 0x7d, 0x02, 0x10, 0x4b,
    0x08, 0x22, 0x42, 0x32, 0xba, 0xe2, 0xb9, 0xe2, 0xd6, 0xb9, 0xff, 0xc3, 0xe9, 0x8a, 0x8f, 0xc1,
    0x8f, 0xe1, 0xb8, 0xa4, 0x96, 0xf1, 0x8f, 0x81, 0xb1, 0x8d, 0x89, 0xcc, 0xd4, 0x78, 0x76, 0x61,
    0x72, 0x3e, 0x37, 0x23, 0x56, 0x73, 0x71, 0x79, 0x63, 0x7c, 0x08, 0x11, 0x20, 0x69, 0x7a, 0x14,
    0x68, 0x05, 0x21, 0x1e, 0x32, 0x27, 0x59, 0xb7, 0xcf, 0xab, 0xdd, 0xd5, 0xcc, 0x97, 0x93, 0xf2,
    0xe7, 0xc0, 0xeb, 0xff, 0xe9, 0xa3, 0xbf, 0xa1, 0xab, 0x8b, 0xbb, 0x9e, 0x9e, 0x8c, 0xa0, 0xc1,
    0x9b, 0x5a, 0x2f, 0x2f, 0x4e, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  ],
  
  exec: function()
  {
    // virtual machine architecture
    // ++++++++++++++++++++++++++++
    //
    // segmented memory model with 16-byte segment size (notation seg:offset)
    //
    // 4 general-purpose registers (r0-r3)
    // 2 segment registers (cs, ds equiv. to r4, r5)
    // 1 flags register (fl)
    //
    // instruction encoding
    // ++++++++++++++++++++
    //
    //           byte 1               byte 2 (optional)
    // bits      [ 7 6 5 4 3 2 1 0 ]  [ 7 6 5 4 3 2 1 0 ]
    // opcode      - - -             
    // mod               -           
    // operand1            - - - -
    // operand2                         - - - - - - - -
    //
    // operand1 is always a register index
    // operand2 is optional, depending upon the instruction set specified below
    // the value of mod alters the meaning of any operand2
    //   0: operand2 = reg ix
    //   1: operand2 = fixed immediate value or target segment (depending on instruction)
    //
    // instruction set
    // +++++++++++++++
    // 
    // Notes:
    //   * r1, r2 => operand 1 is register 1, operand 2 is register 2
    //   * movr r1, r2 => move contents of register r2 into register r1
    // 
    // opcode | instruction | operands (mod 0) | operands (mod 1)
    // -------+-------------+------------------+-----------------
    // 0x00   | jmp         | r1               | r2:r1
    // 0x01   | movr        | r1, r2           | rx,   imm 
    // 0x02   | movm        | r1, [ds:r2]      | [ds:r1], r2
    // 0x03   | add         | r1, r2           | r1,   imm
    // 0x04   | xor         | r1, r2           | r1,   imm 
    // 0x05   | cmp         | r1, r2           | r1,   imm 
    // 0x06   | jmpe        | r1               | r2:r1
    // 0x07   | hlt         | N/A              | N/A
    //
    // flags
    // +++++
    // 
    // cmp r1, r2 instruction results in:
    //   r1 == r2 => fl = 0
    //   r1 < r2  => fl = 0xff
    //   r1 > r2  => fl = 1
    // 
    // jmpe r1
    //   => if (fl == 0) jmp r1
    //      else nop
    
    throw "VM.exec not yet implemented";
  }
  
};

//--------------------------------------------------------------------------------------------------

try
{
  VM.exec();
}
catch(e)
{
  alert('\nError: ' + e + '\n');
}

//--------------------------------------------------------------------------------------------------
The file is pretty self explanatory. The aim is to write a virtual machine and run the code present in VM.mem. Then we should find a link to the last stage of the challenge hidden somewhere in memory.

There is one thing that is not explained though. This is the variable VM.cpu.firmware. It is not used at all for this part of the challenge but will be primordial for part 3. We can forget about it for the time being.

First let's change the name of the javascript file and create a HTML page to run it with our preferred browser.
$ mv 15b436de1f9107f3778aad525e5d0b20.js My\ VM.js

$ cat <<EOF > page.html
<html>
<script type="text/javascript" src="My VM.js"></script>
</html>
EOF

I suspected a trick so I first wrote a function to disassemble the code present in VM.mem. But no trick whatsoever. So I will just dump my solution that is pretty straightforward.
(Note : I don't usually code in javascript and I don't know it very much so it may be ugly but it does the job)
var VM = {
  
  cpu: {
    ...
  },
  
  mem: [
    ...
  ],
  
  exec: function()
  {
    while(1)
    {
        //Decode operand 1
        var opcode = VM.mem[VM.cpu.ip] >> 5;
        var mod = VM.mem[VM.cpu.ip] & 0x10;
        var param = VM.mem[VM.cpu.ip] & 0x0F;
        var param2 = VM.mem[VM.cpu.ip+1];
        
        var dest=VM.correct_registers(param); // Variable register indicated by operand 1
        
        switch(opcode)
        {
            // jmp
            case 0x00:
                var segment=param2;
                if (mod == 0)
                {
                    VM.cpu.ip=eval("VM.cpu.cs*0x10+"+dest); // Standard jmp are relative to the current CS. 
                    //Actually we should fetch instructions using the cs register (cs*0x10+ip) but we can take cs into account only in jmp and jmpe
                }
                else
                {
                    VM.cpu.ip=eval(segment+"*0x10 +"+dest);  // sixteen bytes segments
                    VM.cpu.cs = segment;    //  We must update the code segment when doing this kind of jump (this is what is done on x86)
                }
            break;
            
            //movr
            case 0x01:
                var src=param2;
                if (mod == 0)
                    src=VM.correct_registers(param2);
                
                eval(dest+"="+src);
                VM.cpu.ip+=2;
            break;
            
            //movm
            case 0x02:
                var src=VM.correct_registers(param2);
                if (mod == 0)
                    eval(dest+"=VM.mem[VM.cpu.ds*0x10+"+src+"]");
                else
                    eval("VM.mem[VM.cpu.ds*0x10+"+dest+"]="+src);
                VM.cpu.ip+=2;
            break;
            
            //add
            case 0x03:
                var src=param2;
                if (mod == 0)
                    src=VM.correct_registers(param2);
                
                eval(dest+"="+dest+"+"+src);
                VM.cpu.ip+=2;
            break;
            
            //xor
            case 0x04:
                var src=param2;
                if (mod == 0)
                    src=VM.correct_registers(param2);
                
                eval(dest+"="+dest+"^"+src);
                VM.cpu.ip+=2;
            break;
            
            //cmp
            case 0x05:
                var src=param2;
                if (mod == 0)
                    src=VM.correct_registers(param2);

                var result = eval(dest+"-"+src); //r1 -r2
                if (result == 0)
                    VM.cpu.fl=0;
                else if (result < 0) //r1 < r2
                    VM.cpu.fl=0xff;
                else if (result > 0) //r1 > r2
                    VM.cpu.fl=1;
                VM.cpu.ip+=2;
            break;

            //jmpe
            case 0x06:
                if (VM.cpu.fl == 0)
                {
                    var segment=param2;
                    if (mod == 0)
                    {
                        VM.cpu.ip=eval("VM.cpu.cs*0x10+"+dest); // Standard jmp are relative to the current CS !
                    }
                    else
                    {
                        VM.cpu.ip=eval(segment+"*0x10+"+dest);
                        VM.cpu.cs=segment;
                    }
                }
                else
                {
                    //NOP
                    if (mod == 0)
                        VM.cpu.ip++;
                    else
                        VM.cpu.ip+=2;
                }
            break;
            
            //hlt
            case 0x07:
                VM.dumphexa(); //Program ended : dumping result
                return 0;
            break;
            
            default :
                throw VM.mem[VM.cpu.ip].toString(16) + " Invalid OpCode<br>";
        }
    }
    },
  
    correct_registers: function(register_number)
    {
        if (register_number == 4)
            return  "VM.cpu.cs";
        else if (register_number == 5)
            return "VM.cpu.ds";
        else
            return "VM.cpu.r"+register_number;
    },
  
    dumphexa: function()
    {
        var i=0;
        for (i=0;i<VM.mem.length;i++)
        {
            var hexdata=VM.mem[i].toString(16);
            if (hexdata.length == 1)
                hexdata = "0"+hexdata;
            document.write(hexdata);
        }
    } 
  }  
};

//--------------------------------------------------------------------------------------------------

try
{
  VM.exec()
}
catch(e)
{
  alert('<br>Error: ' + e + '<br>');
}

//--------------------------------------------------------------------------------------------------
I used eval to do the job and that's why I needed something to get the register variable name from its number. This is what the function correct_registers is for.
Changing the structure of VM.cpu to an array would probably have been simpler.

There are two thing to keep in mind that I totally overlooked at first :
  • Instructions should be fetched relative to the code segment.
  • When there is a jump instruction with a target segment (mod 1) the code segment must be updated to the segment provided.
To fix my initial mistake I fixed the second point but for the first point I just made all jumps relative to the current code segment which basically boils down to the same thing as taking the code segment into account at fetching.

Running the Virtual Machine

To run it we open page.html with the browser of our choice.
It should dump the contents of the memory on the page as a long list of hexadecimal numbers once it reaches the end of VM.exec().

Finally we just take that dump to make it a file and search for anything interesting.
$ xxd -p -r <<EOF > My\ VM.dump
310433aa40028003520072017301b2503014c001800010100000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000003200750c3108333240028003520072017303b200c3b000301bc001ff000000
000000000000000000000000000000000000007510010000000000000000000000cc00000000000000
00000000000000007d1f15604d4d527d0e276d106d5a06564714420eb6b2b2e6ebb4838ed7e5d4d9c3
f08095f182829abd95a48d9a2b30694a6965551c7b691c6e04743521262f60034e371e335439e6bab4
a2ada4c595c8c1e48aece7928be881f0ad98a4d0c08dac2252657e272b5a12610a017a6b1d67474554
202f64613735333730666531356334313438626434636565633836316662646161352e657865204854
54502f312e300000000000000000000000000000377a07111f1d682532771e62235b475553301142f6
f1b1e6c3ccf8c5e4ccc0d385fd9ae3e681b5bbd7cd87a3d36b366f6f66553016455e09745c3f292b66
3d0d02302835150915ddecb8e2fbd8cbd8d18bd582d99af192abe8a6d6d08caad294cf454667207d44
146b456d5403176062555a4a6661115768750562367d02104b08224232bae2b9e2d6b9ffc3e98a8fc1
8fe1b8a496f18f81b18d89ccd4787661723e372356737179637c081120697a146805211e322759b7cf
abddd5cc9793f2e7c0ebffe9a3bfa1ab8bbb9e9e8ca0c19b5a2f2f4e4e000000000000000000000000
000000000000000000000000000000000000000000000000000000000000
EOF
 
$ strings My\ VM.dump
`MMR}
+0iJieU
t5!&/`
"Re~'+Z
gGET /da75370fe15c4148bd4ceec861fbdaa5.exe HTTP/1.0
h%2w
b#[GUS0
k6oofU0
E^      t\?)+f=
EFg }D
kEmT
`bUZJfa
xvar>7#Vsqyc|
Z//NN
Same thing than with Part1. A HTTP request to the next stage. Strangely this one is in HTTP 1.0 while the previous one was in HTTP 1.1. I couldn't make sense of that.
Anyway here is the full link : www.canyoucrackit.co.uk/da75370fe15c4148bd4ceec861fbdaa5.exe

The last and final part of the challenge is still ahead of us. It will be treated on the next post.

Saturday, December 24, 2011

Can you crack it ? A quite detailed solution (Part 1)

On the December 5 I saw an article about the site http://www.canyoucrackit.co.uk/. A little late since it was apparently up since November 2.
Canyoucrackit front page.

I decided to take the challenge and it took me 2 days to complete it. Now that I have some time to write articles I will walk you through it. It comes in 3 parts.
The challenge is over now but at time of writing all links are still working. I will provide you with local copies of all that is needed to complete the challenge anyway.

Prerequisites

First you need to know a little bit about x86 assembler and how the stack works. I guess the following wikipedia articles are a good start :
And if you are not quite at ease with it I would also recommend you to read the article about the endianness used on the x86 : Little endian.

I will assume that you have a GNU/Linux machine (or that you use cygwin for that matter) and that you know how to install missing packages.

Decoding the hexadecimal code

The hexadecimal code in the front page is actually a PNG image. The original image can still be found at this URL : http://www.canyoucrackit.co.uk/images/cyber.png

cyber.png

Let's translate this hexadecimal string to binary.
$ cat <<EOF | xxd -r -p > challengepart1.bin
EB 04 AF C2 BF A3 81 EC 00 01 00 00 31 C9 88 0C 0C FE C1 75 F9 31 C0 BA EF BE AD DE 02
04 0C 00 D0 C1 CA 08 8A 1C 0C 8A 3C 04 88 1C 04 88 3C 0C FE C1 75 E8 E9 5C 00 00 00 89
E3 81 C3 04 00 00 00 5C 58 3D 41 41 41 41 75 43 58 3D 42 42 42 42 75 3B 5A 89 D1 89 E6
89 DF 29 CF F3 A4 89 DE 89 D1 89 DF 29 CF 31 C0 31 DB 31 D2 FE C0 02 1C 06 8A 14 06 8A
34 1E 88 34 06 88 14 1E 00 F2 30 F6 8A 1C 16 8A 17 30 DA 88 17 47 49 75 DE 31 DB 89 D8
FE C0 CD 80 90 90 E8 9D FF FF FF 41 41 41 41
EOF

What can it be ? I have a good feeling for x86 machine code. So let's try and disassemble it to see what we get.
$ ndisasm -b32 challengepart1.bin
00000000  EB04              jmp short 0x6
00000002  AF                scasd
00000003  C2BFA3            ret 0xa3bf
00000006  81EC00010000      sub esp,0x100
0000000C  31C9              xor ecx,ecx
0000000E  880C0C            mov [esp+ecx],cl
00000011  FEC1              inc cl
00000013  75F9              jnz 0xe
00000015  31C0              xor eax,eax
00000017  BAEFBEADDE        mov edx,0xdeadbeef
0000001C  02040C            add al,[esp+ecx]
0000001F  00D0              add al,dl
00000021  C1CA08            ror edx,0x8
00000024  8A1C0C            mov bl,[esp+ecx]
00000027  8A3C04            mov bh,[esp+eax]
0000002A  881C04            mov [esp+eax],bl
0000002D  883C0C            mov [esp+ecx],bh
00000030  FEC1              inc cl
00000032  75E8              jnz 0x1c
00000034  E95C000000        jmp dword 0x95
00000039  89E3              mov ebx,esp
0000003B  81C304000000      add ebx,0x4
00000041  5C                pop esp
00000042  58                pop eax
00000043  3D41414141        cmp eax,0x41414141
00000048  7543              jnz 0x8d
0000004A  58                pop eax
0000004B  3D42424242        cmp eax,0x42424242
00000050  753B              jnz 0x8d
00000052  5A                pop edx
00000053  89D1              mov ecx,edx
00000055  89E6              mov esi,esp
00000057  89DF              mov edi,ebx
00000059  29CF              sub edi,ecx
0000005B  F3A4              rep movsb
0000005D  89DE              mov esi,ebx
0000005F  89D1              mov ecx,edx
00000061  89DF              mov edi,ebx
00000063  29CF              sub edi,ecx
00000065  31C0              xor eax,eax
00000067  31DB              xor ebx,ebx
00000069  31D2              xor edx,edx
0000006B  FEC0              inc al
0000006D  021C06            add bl,[esi+eax]
00000070  8A1406            mov dl,[esi+eax]
00000073  8A341E            mov dh,[esi+ebx]
00000076  883406            mov [esi+eax],dh
00000079  88141E            mov [esi+ebx],dl
0000007C  00F2              add dl,dh
0000007E  30F6              xor dh,dh
00000080  8A1C16            mov bl,[esi+edx]
00000083  8A17              mov dl,[edi]
00000085  30DA              xor dl,bl
00000087  8817              mov [edi],dl
00000089  47                inc edi
0000008A  49                dec ecx
0000008B  75DE              jnz 0x6b
0000008D  31DB              xor ebx,ebx
0000008F  89D8              mov eax,ebx
00000091  FEC0              inc al
00000093  CD80              int 0x80
00000095  90                nop
00000096  90                nop
00000097  E89DFFFFFF        call dword 0x39
0000009C  41                inc ecx
0000009D  41                inc ecx
0000009E  41                inc ecx
0000009F  41                inc ecx
It looks very good. Now we are going to analyze it a bit.

Analysis

The code begins with a jump that skips over the next 4 bytes. The 4 bytes skipped are 0xAFC2BFA3. They are not used for now but it is going to be very important for the last part of the challenge.
00000000  EB04              jmp short 0x6
00000002  AF                scasd          ;Unused byte
00000003  C2BFA3            ret 0xa3bf     ;Unused bytes

Then it allocates 0x100 bytes on the stack (in decimal that makes 256) and it loops through it. Basically it allocates an array just like this one :
unsigned char array[256] = {0,1,2,3, ... ,254,255};
(Note that register cl is 1 byte long and it can only store values from 0 to 255. It rolls over to 0 afterwards.)
00000006  81EC00010000      sub esp,0x100
0000000C  31C9              xor ecx,ecx
0000000E  880C0C            mov [esp+ecx],cl
00000011  FEC1              inc cl
00000013  75F9              jnz 0xe

Again another loop on the same 256 allocated bytes. I will save you the details but it uses 0xdeadbeef as a sort of key and rotates through its bytes to compute an index used to swap values of the array. To put it simple the array is "shuffled".
00000015  31C0              xor eax,eax
00000017  BAEFBEADDE        mov edx,0xdeadbeef
[...]
00000030  FEC1              inc cl
00000032  75E8              jnz 0x1c

Then there is a jump at address 0x95. If we follow the execution path we see that at address 0x95 there is a call to address 0x39, which is after the original jmp!
It seems useless, but this is a trick. The instruction call pushes on the stack the return address which is the address of the instruction following the call. In our case this is 0x0000009C.
00000034  E95C000000        jmp dword 0x95
--
00000095  90                nop
00000096  90                nop
00000097  E89DFFFFFF        call dword 0x39 ;Goes back just after the jmp
0000009C  41                inc ecx         ;The new stack will point there
0000009D  41                inc ecx
0000009E  41                inc ecx
0000009F  41                inc ecx

After that the original stack pointer (pointing to the 256 bytes array) is saved into ebx and the return address is popped into esp so that we point to a new stack beginning at 0x0000009C. This way the next value popped would be 0x41414141.
00000039  89E3              mov ebx,esp
0000003B  81C304000000      add ebx,0x4
00000041  5C                pop esp      ;esp contains the return address of the call

Then there is a kind of sanity check. To pass it the first value on the stack should be 0x41414141 and the next one 0x42424242. No problem for the first test, our stack contains the good value, but then we have a nothing else after that, this is the end of our code ! This is problematic.
Anyways we will not stop the analysis here. Let's note that and come back to it later.
(Note : instruction jnz 0x8d jumps to a bit of code that just exits the program. We will detail it later.)
00000042  58                pop eax
00000043  3D41414141        cmp eax,0x41414141
00000048  7543              jnz 0x8d
0000004A  58                pop eax
0000004B  3D42424242        cmp eax,0x42424242
00000050  753B              jnz 0x8d

Before we go on, a quick note on the instruction rep movsb:
  • movsb moves a byte from the address esi to the address edi and increments esi and edi.
  • rep makes the CPU do it ecx times.
So the following code pops another value from the stack that should be a length. It is saved into edx and ecx. Again the popped value is somewhere after our code. Let's note that and continue.
The source register esi is pointing in a zone after our code. The destination register edi is set to ebx which is the original stack pointer. ecx bytes are reserved on it for the data to be copied.
It looks like there should be a lot more data after our final 0x41414141 !
00000052  5A                pop edx     ;This is somewhere after the code
00000053  89D1              mov ecx,edx
00000055  89E6              mov esi,esp ;esi is somewhere further after the code
00000057  89DF              mov edi,ebx ;edi is the original esp
00000059  29CF              sub edi,ecx ;Reserve ecx bytes on the original stack
0000005B  F3A4              rep movsb   ;Copies data to the original stack

Then there is a part there is no need to dig into. Basically registers are initialized and there is a loop using the 256 bytes array and the data we have just copied. The data is XORed with the array in a strange way an the result put back in the data. It looks like a decryption of some sort.
0000005D  89DE              mov esi,ebx      ; Initialization
...
0000006B  FEC0              inc al           ; Start of the loop
...
00000085  30DA              xor dl,bl
...
0000008A  49                dec ecx
0000008B  75DE              jnz 0x6b

Finally this is the end of the program. int 0x80 with eax set to 1 is the system call sys_exit on Linux. The returned value is ebx which is set to zero.
0000008D  31DB              xor ebx,ebx
0000008F  89D8              mov eax,ebx
00000091  FEC0              inc al
00000093  CD80              int 0x80

Filling the missing part

From what we saw with the analysis we have a part of the code that is missing and it should be something like this.
000000A0  0x42424242 (value needed to pass the sanity check)
000000A4  0x???????? (length of the following data)
000000A8  0x???????? (data)
000000AC  0x???????? (data)
..........

The HTML source code of the page http://www.canyoucrackit.co.uk seems ok. (It is not available anymore).
All we have left is this PNG image. Let's see if it contains some indication.
$ strings cyber.png | head
IHDR
sRGB
        pHYs
tIME
]iTXtComment
QkJCQjIAAACR2PFtcCA6q2eaC8SR+8dmD/zNzLQC+td3tFQ4qx8O447TDeuZw5P+0SsbEcYR
78jKLw==2
IDATx
.^cwuW
n7-KtUwI
The highlighted part is very interesting stuff indeed ! It looks like it is Base64 encoded. We can tell that because it uses only the characters [A-Z], [a-z] + and / and it ends with '=' characters.

Let's trim the final '2' character and decode it.
$ cat <<EOF | base64 -d > pngcomment.bin
QkJCQjIAAACR2PFtcCA6q2eaC8SR+8dmD/zNzLQC+td3tFQ4qx8O447TDeuZw5P+0SsbEcYR
78jKLw==
EOF

$ od -Ax -tx1 pngcomment.bin
000000 42 42 42 42 32 00 00 00 91 d8 f1 6d 70 20 3a ab
000010 67 9a 0b c4 91 fb c7 66 0f fc cd cc b4 02 fa d7
000020 77 b4 54 38 ab 1f 0e e3 8e d3 0d eb 99 c3 93 fe
000030 d1 2b 1b 11 c6 11 ef c8 ca 2f
00003a
It looks very promising. We have the 0x42424242 we needed followed by 0x00000032 which is actually the length of the data (Total length : 0x3A - 0x8 bytes for length and initial 0x42424242).

So let's just append it to our binary file.
$ cat challengepart1.bin pngcomment.bin > challengepart1_full.bin

Time to run the code

Let's run the code. We will do it the way you traditionally do shellcode testing. xxd is very handy for that with the -i switch.
$ xxd -i challengepart1_full.bin
unsigned char challengepart1_full_bin[] = {
  0xeb, 0x04, 0xaf, 0xc2, 0xbf, 0xa3, 0x81, 0xec, 0x00, 0x01, 0x00, 0x00,
  0x31, 0xc9, 0x88, 0x0c, 0x0c, 0xfe, 0xc1, 0x75, 0xf9, 0x31, 0xc0, 0xba,
  0xef, 0xbe, 0xad, 0xde, 0x02, 0x04, 0x0c, 0x00, 0xd0, 0xc1, 0xca, 0x08,
  0x8a, 0x1c, 0x0c, 0x8a, 0x3c, 0x04, 0x88, 0x1c, 0x04, 0x88, 0x3c, 0x0c,
  0xfe, 0xc1, 0x75, 0xe8, 0xe9, 0x5c, 0x00, 0x00, 0x00, 0x89, 0xe3, 0x81,
  0xc3, 0x04, 0x00, 0x00, 0x00, 0x5c, 0x58, 0x3d, 0x41, 0x41, 0x41, 0x41,
  0x75, 0x43, 0x58, 0x3d, 0x42, 0x42, 0x42, 0x42, 0x75, 0x3b, 0x5a, 0x89,
  0xd1, 0x89, 0xe6, 0x89, 0xdf, 0x29, 0xcf, 0xf3, 0xa4, 0x89, 0xde, 0x89,
  0xd1, 0x89, 0xdf, 0x29, 0xcf, 0x31, 0xc0, 0x31, 0xdb, 0x31, 0xd2, 0xfe,
  0xc0, 0x02, 0x1c, 0x06, 0x8a, 0x14, 0x06, 0x8a, 0x34, 0x1e, 0x88, 0x34,
  0x06, 0x88, 0x14, 0x1e, 0x00, 0xf2, 0x30, 0xf6, 0x8a, 0x1c, 0x16, 0x8a,
  0x17, 0x30, 0xda, 0x88, 0x17, 0x47, 0x49, 0x75, 0xde, 0x31, 0xdb, 0x89,
  0xd8, 0xfe, 0xc0, 0xcd, 0x80, 0x90, 0x90, 0xe8, 0x9d, 0xff, 0xff, 0xff,
  0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x32, 0x00, 0x00, 0x00,
  0x91, 0xd8, 0xf1, 0x6d, 0x70, 0x20, 0x3a, 0xab, 0x67, 0x9a, 0x0b, 0xc4,
  0x91, 0xfb, 0xc7, 0x66, 0x0f, 0xfc, 0xcd, 0xcc, 0xb4, 0x02, 0xfa, 0xd7,
  0x77, 0xb4, 0x54, 0x38, 0xab, 0x1f, 0x0e, 0xe3, 0x8e, 0xd3, 0x0d, 0xeb,
  0x99, 0xc3, 0x93, 0xfe, 0xd1, 0x2b, 0x1b, 0x11, 0xc6, 0x11, 0xef, 0xc8,
  0xca, 0x2f
};
unsigned int challengepart1_full_bin_len = 218;
All we need to add is the standard shellcode testing bit.
$ xxd -i challengepart1_full.bin > challengepart1_full.c

$ cat <<EOF >>challengepart1_full.c
int main(int argc, char **argv)
{
  int (*func)();
  func = (int (*)()) challengepart1_full_bin;
  (int)(*func)();
}
EOF

Let's then just compile it and run it through gdb.
$ gcc -o challengepart1_full challengepart1_full.c
$ gdb challengepart1_full
We just run the program until the end.
(gdb) r
With a little analysis we can see that at the end of the program the register edi points to the end of the decoded data. The data is 0x32 bytes long so let's dump it.
(gdb) info registers edi
edi            0x22cbdc 2280412
(gdb) p (char*) (0x22cbdc-0x32)
$1 = 0x22cbaa "GET /15b436de1f9107f3778aad525e5d0b20.js HTTP/1.1"
Here we go. A HTTP request.
You can check what it is for yourself : www.canyoucrackit.co.uk/15b436de1f9107f3778aad525e5d0b20.js

The challenge is not over yet. Part 2 and 3 are to come.

Final Note

It didn't come clear to me since I am note very acquainted with it but the code is in fact a RC4 cipher. The initialization of the 256 bytes array is very characteristic. 0xDEADBEEF is the key used to decrypt the data that is inside the PNG comment.

Wednesday, December 21, 2011

Ruxcon DSD challenge

At the Ruxcon (an Australian computer security forum) the Defence Signals Directorate (Australian intelligence agency) had the following text on their stand :

Apply Now
6AAAAABbi8uDwx4zwDPSigOK
ETLCiAM8AHQrg8EBg8MB6+wz
/7/z+TEct0SlpGf5dRyl53US
YQEE56Ri7Kdkj8IAABkcOsw=

It looks so simple that we will impair ourselves a little and try to solve it using only a web browser and an Internet access. As we will see it isn't much of a problem though.

We are going to use only the following 4 sites.

    From Base64 to Asm

    The text is Base64 encoded. For those who don't know it is easy to spot since Base64 only uses alphanumeric characters plus + and /. The number of characters is always multiple of three padded with '=' if necessary.

    Using the translator we convert it to hexadecimal.
    E8 00 00 00 00 5B 8B CB 83 C3 1E 33 C0 33 D2 8A 03 8A 11 32 C2 88 03 3C 00 74 2B 83 C1 01 83 
    C3 01 EB EC 68 74 74 70 3A 2F 2F 77 77 77 2E 64 73 64 2E 67 6F 76 2E 61 75 2F 64 65 63 6F 64 
    65 64 2E 33 FF A6 EF C3 FD
    
    It may be not clear on the spot but it is x86 code (74 is a je instruction , E8 is a call instruction).
    So we feed it to our online disassembler.
    [0x00000000]  call 0x00000005 
    [0x00000005]  pop ebx  
    [0x00000006]  mov ecx,ebx  
    [0x00000008]  add ebx,0x1e
    [0x0000000b]  xor eax,eax  
    [0x0000000d]  xor edx,edx  
    [0x0000000f]  mov al,[ebx]  
    [0x00000011]  mov dl,[ecx]  
    [0x00000013]  xor al,dl
    [0x00000015]  mov [ebx],al  
    [0x00000017]  cmp al,0x0  
    [0x00000019]  jz 0x00000046 
    [0x0000001b]  add ecx,0x1  
    [0x0000001e]  add ebx,0x1  
    [0x00000021]  jmp near 0x0000000f 
    [...]
    [0x00000046]  int3 
    

    Code inspection

    Let's look closer :
    [0x00000000]  call 0x00000005
    It jumps to 0x5 which is the next instruction but before it pushes on the stack the address of the instruction following call. That is 5.
    [0x00000005]  pop ebx
    We get the address pushed by call into ebx. (ebx=5)
    [0x00000006]  mov ecx,ebx
    ecx=ebx(=5)
    [0x00000008]  add ebx,0x1e
    ebx=ebx+0x1e(=0x23)
    [0x0000000b]  xor eax,eax  
    [0x0000000d]  xor edx,edx
    eax and edx are set to zero.
    [0x0000000f]  mov al,[ebx]  
    [0x00000011]  mov dl,[ecx]
    al is set to the value residing at the address pointed by ebx.
    dl is set to the value residing at the address pointed by ecx.
    [0x00000013]  xor al,dl
    [0x00000015]  mov [ebx],al
    They are XORed together and the result is put in memory at the address pointed by ebx.
    [0x00000017]  cmp al,0x0
    [0x00000019]  jz 0x00000046
    We jump at the end of the program when al is zero. It would be the case only if al and dl were the same before the XOR operation.
    [0x0000001b]  add ecx,0x1
    [0x0000001e]  add ebx,0x1
    [0x00000021]  jmp near 0x0000000f
    
    Our two memory pointers ecx and ebx are incremented and we loop back to instruction [0x0000000f].

    The rest of the code is just data used for XORing. It wasn't decoded by the disassembler because it is not valid x86 machine code.

    With that quick inspection we see that the program does just a simple XOR between the code at 0x5 and the data beginning at 0x23, just after the code. The XORed result is put in memory starting at offset 0x23 so that it does not overwrite the code.

    Solution

    There are two ways to solve it then.

    Solution 1

    The first one is to write a C program that does exactly the same and use codepad to compile and execute it. The program is pretty much a line of C for a line of asm not counting the final printf to see the decoded result. It could be simplified to get rid of the unnecessary eax and edx.
    unsigned char mem[]={
    0xE8,0x00,0x00,0x00,0x00,0x5B,0x8B,0xCB,0x83,0xC3,0x1E,0x33,0xC0,0x33,0xD2,
    0x8A,0x03,0x8A,0x11,0x32,0xC2,0x88,0x03,0x3C,0x00,0x74,0x2B,0x83,0xC1,0x01,
    0x83,0xC3,0x01,0xEB,0xEC,0x33,0xFF,0xBF,0xF3,0xF9,0x31,0x1C,0xB7,0x44,0xA5,
    0xA4,0x67,0xF9,0x75,0x1C,0xA5,0xE7,0x75,0x12,0x61,0x01,0x04,0xE7,0xA4,0x62,
    0xEC,0xA7,0x64,0x8F,0xC2,0x00,0x00,0x19,0x1C,0x3A,0xCC}; //Hex dump
    
    void main()
    {
     unsigned char eax, edx;
     unsigned char ecx=5;
     unsigned char ebx=0x23;
    
     while(1)
     {
      eax=mem[ebx];
      edx=mem[ecx];
      eax^=edx;
      mem[ebx]=eax;
      if (eax == 0)
       break;
      ecx++;
      ebx++;
     }
     printf("%s",mem+0x23); //Dump mem starting at the offset it was modified
    }
    
    No fear about printf, we know the string will be null terminated because the loop is exited only when the XORed result is zero.

    The output is http://www.dsd.gov.au/decoded.html.

    Solution 2

    The other way is to use a XOR calculator to get the result but there is a trick. Let's first dump the XORed data.
    From address 0x23 to the end :
    33FFBFF3F9311CB744A5A467F9751CA5E775 12610104E7A462ECA7648FC2 0000191C3ACC
    
    From address 0x5 :
    5B8BCB83C31E33C033D28A038A1132C28803 3C00742B83C10183C301EBEC 33FFBFF3F931

    The trick is that memory zones overlap. The stroke-through part must not be dealt with immediately. When we arrive there the underlined part (which is referring to the same address than the strokethrough part) will have been overwritten.

    Let's take care of the first part and feed it to the XOR calculator. (Since it can't deal with more than 64 hex digits we have to cut it in two).It gives us :
    687474703a2f2f7777772e6473642e676f76 2e61752f6465636f6465642e
    The underlined part is the data with which the strokethrough part should be replaced.

    Now we do the replacement and XOR the last bytes which are :
    0000191C3ACC
    687474703A2F
    It gives
    68746d6c00e3

    Then we just concatenate the two and trim all that is after the first 0x00 digit since it is the end of the string.
    687474703a2f2f7777772e6473642e676f762e61752f6465636f6465642e68746d6c

    Finally we feed it to the Translator Hex to String and voila. We get this : http://www.dsd.gov.au/decoded.html.

    Monday, December 19, 2011

    How to make ksh93 echo newlines

    The short story

    If you can't get echo to interpret escape sequences like \n or \c with ksh and it does something like this.
    $ echo "\n"
    \n
    
    Try the following.
    $ builtin getconf; getconf UNIVERSE - att
    $ echo "\n"
    
    
    If your are under Ubuntu or the above doesn't work just change - for =
    $ builtin getconf; getconf UNIVERSE = att
    $ echo "\n"
     
    

    For those interested in the details

    The other day I had to port an old application with lots of shell scripts from a Solaris machine to RHEL 5. Those scripts use escapes sequences inside echo statements. It was fine on the Solaris machine but on RHEL all the escape sequences were displayed verbatim.
    $ echo "\n"
    \n
    
    The only difference was that the Solaris server came with ksh88 and RHEL 5 comes with ksh93.

    The main problem is that echo is not standardized. Some distributions make it interpret escape sequences by default but other require the -e option. You should avoid using echo if you write a portable script !

    My first try was to alias echo to "echo -e" but it does not work inside scripts as aliases can't be passed to subshells like you could do in ksh88 with alias -x.
    I then tried to put it into the .profile or .kshrc but unfortunately in ksh93 non-interactive, non-login shells do not read any profile file, ever !

    I was stuck when I fell on the UNIVERSE variable (don't think it is documented in the man page). Basically ksh93 behaves in two different ways with respect to echo depending on how it has been compiled.
    UNIVERSE can be :
    • ucb (University of California, Berkeley) : echo doesn't interpret escape sequences.
    • att (AT&T): echo interprets escape sequences.

      If you encountered the same problem it surely means that your UNIVERSE variable is set to ucb. There are two ways to check that.
      1. Look at the environment variable _AST_FEATURES
        $ env | grep AST
        _AST_FEATURES=UNIVERSE -ucb
        
      2. Use the built-in version of getconf. (By default my ksh93 uses getconf form the path which doesn't know UNIVERSE)
        $ builtin getconf
        $ getconf UNIVERSE
        ucb
        
      Fixing the problem is then just a simple matter of setting it to att. Again two ways.
      1. Changing the environment variable _AST_FEATURES. The main issue is that on RHEL it does not work immediately , only in subshells.
        $ export _AST_FEATURES="UNIVERSE - att"
        $ echo "\n"
        \n
        $ ksh
        $ echo "\n"
        
        
      2. Changing UNIVERSE via the built-in getconf. And it works immediately.
        $ builtin getconf; getconf UNIVERSE - att
        $ echo "\n"
        
        
        This is what I put into the .profile to get those scripts working.

      For Ubuntu 11 users or if the above did not work you should use = instead of - into the UNIVERSE variable. It looks like there is again a difference amongst the different shipped versions of ksh93 and some do not accept - to set UNIVERSE.
      1. Unlike RHEL changing the environment variable works immediately on Ubuntu.
      2. $ export _AST_FEATURES="UNIVERSE = att"
      3. Or the second version if you prefer.
        $ builtin getconf; getconf UNIVERSE = att