ForbiddenBITS 2013 - Crunch - 250

We get a PNG image that's all black. Looking at the structure there doesn't appear to be any other data, only a single IDAT chunk with deflated image bytes. Decompressing the data we can see a lot of runs with NULL bytes. So this is likely to be an image very limited in color, perhaps all black. After some trial and error we can "highlight" actual content by changing the gamma of the PNG, either via CLI or in Gimp using "Color" -> "Auto" -> "Normalize". This results in an image of a QR-Code that decodes to a URL.

$ pngtopnm -gamma 8 spawn.png > spawn.ppm
$ file spawn.ppm
spawn.ppm: Netpbm PPM "rawbits" image data
$ zbarimg spawn.ppm
scanned 1 barcode symbols from 1 images in 0.12 seconds

The downloaded content is a long ASCII string of HEX characters, which after conversion results in a data blob.

$ xxd -l64 060645cc5ebdf80e84ebc91547641b49 
0000000: 3939 6261 3964 3839 3734 3633 3263 3234  99ba9d8974632c24
0000010: 3262 3266 3734 3732 3637 3633 3632 3231  2b2f747267636221
0000020: 3734 3362 3636 3632 3964 6232 3734 3330  743b66629db27430
0000030: 3636 3633 3633 3638 3735 3732 3637 3633  6663636875726763

>>> from binascii import unhexlify
>>> dat = open("060645cc5ebdf80e84ebc91547641b49", "rb").read(1024*1024)
>>> open("blob.dat", "wb").write(unhexlify(dat))

$ xxd -l512 blob.dat 
0000000: 99ba 9d89 7463 2c24 2b2f 7472 6763 6221  ....tc,$+/trgcb!
0000010: 743b 6662 9db2 7430 6663 6368 7572 6763  t;fb..t0fcchurgc
0000020: 6368 7572 6763 6368 7572 6763 6368 7572  churgcchurgcchur
0000030: 6763 6368 7572 6763 6368 7572 6763 6368  gcchurgcchurgcch
0000040: 7572 6763 6368 7572 6763 6368 7572 6763  urgcchurgcchurgc
0000050: 6368 7572 6763 6368 758c bd62 2168 7572  churgcchu..b!hur
0000060: 6763 6368 7572 6763 6368 7572 6763 6368  gcchurgcchurgcch
0000070: 7572 6763 6368 7572 6763 6368 7572 6763  urgcchurgcchurgc
0000080: 6368 7572 6763 6368 7572 6763 6368 7572  churgcchurgcchur
0000090: 6763 6368 7572 6763 6368 7572 6763 9da9  gcchurgcchurgc..
00000a0: 7462 6e63 7768 0a70 6773 626b 6572 6573  tbncwh.pgsbkeres
00000b0: 6396 b073 7962 6269 7372 6763 6368 7473  c..sybbisrgcchts
00000c0: 6662 6269 7473 6566 676f 737b 6f68 6262  fbbitsefgos{ohbb
00000d0: 7571 99a6 6203 6473 6661 616e 737b 6064  uq..b.dsfaans{`d
00000e0: 6162 7375 6d6b 626a 7076 6764 7369 7674  absumkbjpvgdsivt
00000f0: 7576 4358 5032 3703 13e8 847b 7341 515d  uvCXP27....{sAQ]
0000100: e5d2 a76b 702d c5a2 8774 472a 2017 9757  ...kp-...tG* ..W
0000110: 311d 6351 5420 273b 2110 e2f6 c663 6c11  1.cQT ';!....cl.
0000120: 0311 17dd 5245 14e1 f1ab 5344 e4e7 f0cb  ....RE....SD....
0000130: d1c6 a2b6 802f 02e6 f0c1 d0da b787 99a6  ...../..........
0000140: 6274 7573 6461 6368 7572 6762 6269 7473  btusdachurgbbits
0000150: 6662 6269 7070 6364 6561 7571 6f9d a669  fbbippcdeauqo..i
0000160: 2162 6662 616d 7274 6066 666b 7376 6364  !bfbamrt`ffksvcd
0000170: 6569 7570 6262 606c 6575 4753 2338 8474  eiupbb`leuGS#8.t
0000180: 7203 13e8 d5c2 7471 f3a8 a592 6e77 4698  r.....tq....nwF.
0000190: 6256 5230 405c 6350 2337 10fb b655 5426  bVR0@\cP#7...UT&
00001a0: e0cb 6c45 2400 d060 5335 3534 805a 4330  ..lE$..`S554.ZC0
00001b0: 0207 16cc 8ba9 666e 6168 7471 7761 7369  ......fnahtqwasi
00001c0: 4b73 ebff 1087 ad72 7dad 46a2 769d 6438  Ks.....r}.F.v.d8
00001d0: b219 95ff aa93 bd46 9017 4f10 4601 cb43  .......F..O.F..C
00001e0: e5bf 05ff 0dc4 9368 f3c8 2ec2 72c3 93e2  .......h....r...
00001f0: 8cbc 7d05 fd6f 27ab f03c f67f 7559 8841  ..}..o'..<..uY.A

So, we can see that there's some structure to the decoded data blob, but it seems to be non-sense except for the header part, which may contain "encoded" ASCII characters. We could try and guess the file format and at least attempt basic XOR decryption with common magic values as the key in order to get the real key. Another approach is to assume that the header will contain runs of NULL bytes in which case in simple XOR the key will be as plain-text.

In the end it turned out to be a JPEG image XOR encrypted using the "fbbits" key. The image contains the flag in form of a hash string overlayed in red on top of the image. Also, notice that the "fbbits" key was in fact present in the data blob where the real JPEG had a run of NULL bytes at offset 0xC0, which is part of Huffman table.


Nuit du Hack 2013 Quals - Crackme3 - 300

We get credentials to a SSH server which has two files in the home directory once logged in. One is the challenge binary and the other is a Linux kernel image, both are 64-bit. Running the binary we get a password prompt and no matter what is entered is incorrect.
$ ./crackme
Password: qwerty
You loose :(

After downloading the binary and looking at the assembly it quickly becomes obvious that static analysis won't work. The "main()" function contains what appears to be garbage code and the ELF header has some unknown flags set. Also, running the binary on anything other than their server causes a segfault, since instructions make no sense.
$ readelf -h crackme
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x400610
  Start of program headers:          64 (bytes into file)
  Start of section headers:          4752 (bytes into file)
  Flags:                             0x20

.text:0000000000400610 start     proc near
.text:0000000000400610           and     ebp, [rsi-6E561383h]
.text:0000000000400616           xchg    eax, esp
.text:0000000000400617           jp      short near ptr _malloc+2
.text:000000000040061A           out     0D5h, al
.text:000000000040061C           rol     dword ptr [rsi], 1
.text:000000000040061E           xlat
.text:000000000040061F           mov     bl, 0Ah
.text:0000000000400621           lock sbb eax, 0FF98C41Bh
.text:0000000000400627           xchg    eax, ebx
.text:0000000000400628           xor     ch, fs:[rax-47h]
.text:000000000040062C           jrcxz   near ptr byte_40066F
.text:000000000040062E           rcpps   xmm4, xmm6
.text:0000000000400631           loopne  near ptr word_4005FA
.text:0000000000400633           adc     ch, [rbx+30879A93h]
.text:0000000000400639           sub     [rdx+8], bl

$ ./crackme
Segmentation fault (core dumped)

$ gdb crackme
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
Reading symbols from /mnt/hgfs/data/dxp/ctf/2013/ndh/crackme3/crackme...(no debugging symbols found)...done.
gdb$ r

Program received signal SIGSEGV, Segmentation fault.
  EAX: 0x0000001C  EBX: 0x00000000  ECX: 0x00000FFF  EDX: 0xF7DE9740  o d I t s z a p c
  ESI: 0x00300000  EDI: 0xF7FFE2C8  EBP: 0x00000000  ESP: 0xFFFFE530  EIP:Error while running hook_stop:
Value can't be converted to integer.
0x0000000000400610 in ?? ()
gdb$ x/5i $pc
=> 0x400610:    and    ebp,DWORD PTR [rsi-0x6e561383]
   0x400616:    xchg   esp,eax
   0x400617:    rex.XB jp 0x4005f2
   0x40061a:    out    0xd5,al
   0x40061c:    rol    DWORD PTR [rsi],1
gdb$ i r
rax            0x1c     0x1c
rbx            0x0      0x0
rcx            0xfff    0xfff
rdx            0x7ffff7de9740   0x7ffff7de9740
rsi            0x300000 0x300000
rdi            0x7ffff7ffe2c8   0x7ffff7ffe2c8
rbp            0x0      0x0
rsp            0x7fffffffe530   0x7fffffffe530
r8             0x1      0x1
r9             0x4      0x4
r10            0xd      0xd
r11            0x10800  0x10800
r12            0x400610 0x400610
r13            0x7fffffffe530   0x7fffffffe530
r14            0x0      0x0
r15            0x0      0x0
rip            0x400610 0x400610
eflags         0x10202  [ IF RF ]
cs             0x33     0x33
ss             0x2b     0x2b
ds             0x0      0x0
es             0x0      0x0
fs             0x0      0x0
gs             0x0      0x0

Initially, I tried dumping the memory from the binary on their server but without much success as the server was constantly dropping connections and it was nearly impossible to get anything done remotely. Later they removed GDB stating it was causing resource exhaustion problems. Considering that they also supplied a kernel image it would make sense to assume that the execution of certain files has to go through a decryption routine, similar to how iPhone executes encrypted apps. In the iPhone apps case the entire ".text" section is encrypted and the decryption key is within a kernel module. Thus, the idea is to analyze the supplied kernel image and identify where decryption occurs.

Poking around the kernel initially did not yield anything useful. Looking at the normal process execution sequence via the "execve()" call and following the entire procedure from start to finish did not yield anything that may indicate handling encryption. The typical sequence is for the correct system call stub to be called, which then goes into "sys_execve()" and onto "do_execve()" where the bulk of the logic is located. Nothing related to encryption stood out.

There's also functionality for handling various binary formats during process loading as well as the default ELF handling functions. The major one to parse the binary is the "load_elf_binary()" function. The function itself is a bit large, but not too complex and having a copy of the source code allows to go through it quicker and make sense of the structure parsing code.

Eventually, at very end of the function there's a rather peculiar code which first initializes a stack buffer with 35 constant values and then in a loop XORs a previously allocated buffer.

.text:FFFFFFFF8117A39C           mov     byte ptr [rbp+var_53], 12h
.text:FFFFFFFF8117A3A0           mov     byte ptr [rbp+var_53+1], 43h ; 'C'
.text:FFFFFFFF8117A3A4           mov     byte ptr [rbp+var_53+2], 34h ; '4'
.text:FFFFFFFF8117A3A8           mov     byte ptr [rbp+var_53+3], 65h ; 'e'
.text:FFFFFFFF8117A3AC           mov     rax, rsi
.text:FFFFFFFF8117A3AF           mov     byte ptr [rbp+var_53+4], 78h ; 'x'
.text:FFFFFFFF8117A3B3           mov     byte ptr [rbp+var_53+5], 0CFh ; '-'
.text:FFFFFFFF8117A3B7           sub     rax, rdx
.text:FFFFFFFF8117A3BA           mov     byte ptr [rbp+var_53+6], 0DCh ; '_'
.text:FFFFFFFF8117A3BE           mov     byte ptr [rbp+var_53+7], 0CAh ; '-'
.text:FFFFFFFF8117A3C2           mov     byte ptr [rbp+var_4B], 98h ; 'ÿ'
.text:FFFFFFFF8117A3C6           mov     byte ptr [rbp+var_4B+1], 90h ; 'É'
.text:FFFFFFFF8117A3CA           sub     rcx, rax
.text:FFFFFFFF8117A3CD           mov     byte ptr [rbp+var_4B+2], 65h ; 'e'
.text:FFFFFFFF8117A3D1           mov     byte ptr [rbp+var_4B+3], 31h ; '1'
.text:FFFFFFFF8117A3D5           xor     edx, edx              ; cnt = 0
.text:FFFFFFFF8117A3D7           mov     byte ptr [rbp+var_4B+4], 21h ; '!'
.text:FFFFFFFF8117A3DB           mov     byte ptr [rbp+var_4B+5], 56h ; 'V'
.text:FFFFFFFF8117A3DF           xor     eax, eax
.text:FFFFFFFF8117A3E1           mov     byte ptr [rbp+var_4B+6], 83h ; 'â'
.text:FFFFFFFF8117A3E5           mov     byte ptr [rbp+var_4B+7], 0FAh ; '·'
.text:FFFFFFFF8117A3E9           mov     [rbp+var_43], 0CDh ; '-'
.text:FFFFFFFF8117A3ED           mov     [rbp+var_42], 30h ; '0'
.text:FFFFFFFF8117A3F1           mov     [rbp+var_41], 0FDh ; '²'
.text:FFFFFFFF8117A3F5           mov     [rbp+var_40], 12h
.text:FFFFFFFF8117A3F9           mov     [rbp+var_3F], 84h ; 'ä'
.text:FFFFFFFF8117A3FD           mov     [rbp+var_3E], 98h ; 'ÿ'
.text:FFFFFFFF8117A401           mov     [rbp+var_3D], 0B7h ; '+'
.text:FFFFFFFF8117A405           mov     [rbp+var_3C], 54h ; 'T'
.text:FFFFFFFF8117A409           mov     [rbp+var_3B], 0A5h ; 'Ñ'
.text:FFFFFFFF8117A40D           mov     [rbp+var_3A], 62h ; 'b'
.text:FFFFFFFF8117A411           mov     [rbp+var_39], 61h ; 'a'
.text:FFFFFFFF8117A415           mov     [rbp+var_38], 0F9h ; '·'
.text:FFFFFFFF8117A419           mov     [rbp+var_37], 0E3h ; 'p'
.text:FFFFFFFF8117A41D           mov     [rbp+var_36], 9
.text:FFFFFFFF8117A421           mov     [rbp+var_35], 0C8h ; '+'
.text:FFFFFFFF8117A425           mov     [rbp+var_34], 94h ; 'ö'
.text:FFFFFFFF8117A429           mov     [rbp+var_33], 12h
.text:FFFFFFFF8117A42D           mov     [rbp+var_32], 0E6h ; 'µ'
.text:FFFFFFFF8117A431           mov     [rbp+var_31], 87h ; 'ç'

Converting this code to Python and applying it to the extracted ".text" section from the binary we get the "executable" code section. Simply replacing the original section with the new one allows us to run the binary offline. However, we still get the same "Password" prompt and have to figure out the correct input to get the key.

The logic of the binary is to read a stream of bytes from STDIN, check for specific characters and either increment or decrement two variables depending on the characters used. Next, the variables are used as offsets into an array of ones and zeros and when the value of "1" is indexed into the process will exit with failure. Thus, the solution is to construct the correct character sequence that results in proper indexing without hitting a "1" and the length of input must be 80 characters.

The array represents a 15x15 matrix which is a maze that has a single solution. In this case it was easier to just print out the maze and apply the character sequences (w, s, a, d) which represent direction (up, down, left, and right) without coding a maze solver. Start from top-left and finish at the bottom-right.
$ pyton maze.py
0   0 0   0 0 0       0   0    
0   0     0   0 0 0   0   0    
0 0 0   0 0   0     0 0 0 0    
    0     0         0     0 0 0
    0 0 0 0       0 0     0    
  0 0       0 0   0   0 0 0    
  0           0 0 0       0 0 0
  0 0 0 0 0 0     0   0 0 0    
0       0         0   0       0
0       0     0 0 0   0     0 0
0 0 0 0 0     0       0   0 0  
        0 0   0   0 0 0   0    
  0 0 0   0   0   0     0 0 0 0
  0   0 0 0   0   0       0   0
0 0           0   0 0 0 0 0   0
  0 0 0 0 0 0 0     0   0     0

$ ./crackme
Password: ssddsssassdddssssdssaawaasssddddddwwwwwwddwwwwwdwwdddsssssaassssaasssddddwwddsss
You win !


HITB 2011 Amsterdam Round Two Quals Binary

The provided binary is a ELF file designed to be run by inetd and accepts several character based commands followed by their parameters. The main loop reads a character from STDIN (in inetd a socket is duplicated into standard I/O descriptors) and based on the value picks a handler from an array of function pointers. The following commands exist:

  • 'f' - return meta data for a given file name
  • 'l' - list filenames from the home directory
  • 'q' - terminate the process
  • 's' - return symbolic link's path name
  • 'v' - verify input with a key file's data

After mapping out all the available handlers and reviewing how they work there were no obvious vulnerabilities (e.g. buffer overflow). The handler functions that expect additional input first allocate some heap-based memory to store this input and care is taken to make sure it's not overflowed. After the handler completes its work the memory is freed. However, there's a design flaw in the way allocated memory is used when the key text is verified. Specifically, the key data is read into a allocated buffer and compared to the string supplied by a user. Once this is done the memory is simply freed, which returns the memory block to the free pool still containing the key data.

As a result, we can use another handler, which allocates memory for its operation and returns the results to the user. One such command is the 's' character, which accepts a symbolic link as a parameter. Additionally, after listing the contents of the home directory using the 'l' command we find few symbolic links that can be used for the 's' command. Thus, when requesting to view a symbolic link the real path will be copied to a allocated buffer. Since the buffer will come from the free pool and the contents were not cleared then whatever data was there will also be displayed. If the resulting path is shorter in length than the data size of the "KeyFile" we can disclose part of the secret key string.

Looking at the handler for the 'v' command it was identified that a string comparison is performed only on the last 14 bytes of the key file. This tells us how much of the disclosed key file's data we need to grab. Thus, to reproduce we need to submit the 'v' command with any key, which stores the key data into a buffer, but does not clear it after the comparison. Then, submit the 's' command to display the path of the "t1" link, which is short enough to disclose more than 14 bytes of the key file. Finally, take the last 14 bytes and submit them via the 'v' command again and this time the comparison succeeds and gives us the solution key.

The solution string is (w/o quotes): "DwightIzK00l"


Codegate 2011 Quals - Binary 300

The question is:
Find a malicious ID!!

The binary is a Browser Helper Object (BHO) DLL with a static XOR key "securecodegate", which is used to decrypt few arrays with statically assigned characters to each index. The "sub_1000233E" function is called with the array and a XOR key as input to perform the decryption. This occurs three times within a handler function "sub_1000270A".

The first two calls are irrelevant as they result in decryption of "google_ads_frame" (key "secure") and "client" (key "code"). However, the third call produces the answer string using the "gate" key.

This was identified by looking for various interesting strings in the binary and locating their use references. The XOR decryption routine is fairly simple and can be performed via a IDC script.

We used a lazy/simple option. Register the DLL ("regsvr32 b300.dll"). Launch Internet Explorer, attach a debugger, locate the handler function ("sub_1000270A"). Modify EIP to jump to the buffer initialization sequence, which is right before the decryption function call (e.g. @ 0x10002C96 for the "gate" key).

The answer is:

Codegate 2011 Quals - Binary 200

The question is:
Reverse Me!!

The binary is a console based PE file. Running the file produces no output due to a certain routine terminating the process before the "main()" function starts. Looking around the code the "sub_401130" function stands out due to initialization of a local array with various bytes. At the end of this function a decryption routine is called ("sub_401070") with the array as input. The decryption loop performs an XOR operation using the string's length as the key.

To obtain the answer a breakpoint was placed @ 0x00401494, which calls the "ExitProcess()" library function prior to "main()". Next, modify EIP to point to the start of the array initialization routine and execute until the decryption function is called. Let it do its XOR job and look at a local buffer once complete to get the answer string.

The answer is:

Codegate 2011 Quals - Forensics 300

The question is:
we are investigating the military secret's leaking. we found traffic with leaking secrets while monitoring the network. Security team was sent to investigate, immediately. But, there was no one present. It was found by forensics team that all the leaked secrets were completely deleted by wiping tool. And the team has found a leaked trace using potable device. Before long, the suspect was detained. But he denies allegations.

Now, the investigation is focused on potable device. The given files are acquired registry files from system. The estimated time of the incident is Mon, 21 February 2011 15:24:28(KST). Find a trace of portable device used for the incident.

The Key : "Vendor name" + "volume name" + "serial number" (please write in capitals)

Enumerate a timeline of USB activity from the backup system hive
Disk&Ven_Corsair&Prod_UFD&Rev_0.00,Thu Feb 17 04:41:02 2011,ddf08fb7a86075&0,Thu Feb 17 04:41:03 2011,Corsair UFD USB Device,
Disk&Ven_FM&Prod_Memorette_Swing&Rev_1.00,Thu Feb 17 06:38:21 2011,2008090256000000000000BE&0,Thu Feb 17 06:38:22 2011,FM Memorette Swing USB Device,

The enumeration shows all of the USB devices ever connected to the system. The registry last modified times are written the first time the device is connected, but are not updated when a device is subsequently connected.

Running a timeline on the registry (via "regripper"), we see that only one USB device is connected on Feb 21:
Mon Feb 21 06:24:21 2011Z HKLM\ControlSet001\Enum\WpdBusEnumRoot\UMB\2&37c186b&0&STORAGE#VOLUME#_??_USBSTOR#DISK&VEN_CORSAIR&PROD_UFD&REV_0.00#DDF08FB7A86075&0#\Properties\{80d81ea6-7473-4b0c-8216-efc11a2c4c8b}

From there, we know that the suspect device vendor is "CORSAIR" and the serial number of the device is "DDF08FB7A86075". Inspection of this registry path reveals that the default name of the device has been changed. The registry key "FriendlyName" has a value of "PR0N33R", which is the displayed volume name when the device is connected.

The answer is:

Thanks to our team member tina for the solution.

Codegate 2011 Quals - Network 100

The question is:
This data is related to any attack.
calculate the md5sum of the intended file.

(calc md5 uppercase)

The provided binary is a PCAP file containing bunch of HTTP traffic and some SMB chatter. The question mentions an attack. Since it's heavy on HTTP usage then it made sense to get a list of all requests. Two strange requests stand out:

GET /H1A1.html HTTP/1.1
GET /H1A1.exe HTTP/1.1

Carving out (using Wireshark's "Follow TCP Stream" -> "Save As") the "H1A1.exe" response and removing the HTTP response header we end up with a regular PE file. Next, calculate its MD5 checksum and convert to upper case.

The answer is: