Monday, 16 October 2017

Flare-On Challenge 2017 Writeup

Flare-on is an annual CTF style challenge organized by Fire-eye with a focus on reverse engineering. The contest falls into its fourth year this season. Taking part in these challenges gives us a nice opportunity to learn something new and this year was no exception. Overall, there were 12 challenges to complete. Official solution to the challenges has already been published at the FireEye blog. Hence instead of a detailed write-up, I will just cover the important parts.

#1 - Login.html

The first problem was as simple as it gets. There is an HTML file with a form. We need to provide a flag and check for its correctness.

Figure 1: Check thy flag
The code simply performs a ROT-13 of the input and compares it with another string. To get back the flag, re-apply ROT-13.

Figure 2: ROT-13 again
Flag: ClientSideLoginsAreEasy@flare-on.com


#2 - IgniteMe.exe

This is one of the classical crackme challenges. A PE is provided. It takes a text as input and checks whether it's correct or not. The following script obtains the flag.

target = [13, 38, 73, 69, 42, 23, 120, 68, 43, 108, 93, 94, 69,
18, 47, 23, 43, 68,111, 110, 86, 9, 95, 69, 71, 115, 38, 10, 
13, 19, 23, 72, 66, 1, 64, 77, 12, 2, 105]

flag = []
v = 4

for x in reversed(target):
    v = x ^ v
    flag.append(v)

print ''.join(reversed(map(chr, flag)))  

Flag: R_y0u_H0t_3n0ugH_t0_1gn1t3@flare-on.com


#3 - greek_to_me.exe

This is a PE which takes its input over the network. The input is a 32-bit integer. The most significant byte of this integer is used to decrypt a piece of encrypted code. A fletcher's checksum checks for correctness of the decrypted data. Like others have done, I bruteforced the key in C.

#include <stdio.h>
#include <string.h>

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;

uint8_t data[] = {
 51, 225, 196, 153, 17, 6, 129, 22, 240, 50, 159, 196, 145, 23, 6, 129, 20, 240, 
 6, 129, 21, 241, 196, 145, 26, 6, 129, 27, 226, 6, 129, 24, 242, 6, 129, 25, 241, 
 6, 129, 30, 240, 196, 153, 31, 196, 145, 28, 6, 129, 29, 230, 6, 129, 98, 239, 6, 
 129, 99, 242, 6, 129, 96, 227, 196, 153, 97, 6, 129, 102, 188, 6, 129, 103, 230, 
 6, 129, 100, 232, 6, 129, 101, 157, 6, 129, 106, 242, 196, 153, 107, 6, 129, 104, 
 169, 6, 129, 105, 239, 6, 129, 110, 238, 6, 129, 111, 174, 6, 129, 108, 227, 6, 
 129, 109, 239, 6, 129, 114, 233, 6, 129, 115, 124
}; 

uint8_t buf[121];

uint16_t fletcher16(uint8_t *data, int count )
{
    uint16_t sum1 = 0;
    uint16_t sum2 = 0;
    int index;
 
    for( index = 0; index < count; ++index )
    {
       sum1 = (sum1 + data[index]) % 255;
       sum2 = (sum2 + sum1) % 255;
    }
 
    return (sum2 << 8) | sum1;
 }

void main()
{
 uint16_t checksum;
 int i;
 for (int key = 1; key <= 255; key++)
 {
  memcpy(buf, data, 121);
  printf("[*] Trying key: %x -> ", key);
  for (i = 0; i <= 121; i++)
  {
   buf[i] ^= key;
   buf[i] += 0x22;
  }

  checksum = fletcher16(buf, 121);
  printf("%x\n", checksum);
  if (checksum == 0xFB5E)
  {
   printf("[!] Xor key: %x\n", key);
   break;
  }
 }
}

The decryption key is 0xa2. Running the program with this key we can retrieve the flag from memory.

Figure 3: Yes, even we too bruteforce!

Flag: et_tu_brute_force@flare-on.com


#4 - notepad.exe

This is one of those challenges which need a bit of spark to complete. The challenge searches for PE files in the directory %USERPROFILE%/flareon2016 challenge. The files must have a specific timestamp or else we fail. This set me off for quite some time as I didn't have any idea what it meant. Fortunately, searching for one of the timestamps on Google lead to a clue. The values are basically the timestamps of the first four PE files from last years challenge. Dropping those files in the proper directory and after a bit of fiddling gives the flag.

Flag: bl457_fr0m_th3_p457@flare-on.com


#5 - pewpewboat.exe

This is an x86_64 ELF although named as an exe. It's one of the hidden ships game. A 8x8 grid is provided. Some of the cells have a ship hidden beneath. The task is to complete all the levels. To play the game in a semi-automatic fashion I developed a script using the python bindings of radare.

#!/usr/bin/env python

import r2pipe
import sys

def get_ships(state):
    print 'Writing moves...'
    f = open('moves', 'w')
    for row in xrange(8):
        for col in xrange(8):
            bitmask = 1 << ((row * 8) + col)
            if state & bitmask != 0:
                f.write('%s%s' %(chr(65+row), chr(49+col)))
                f.write('\n')
    f.close()


def main():
    r2 = r2pipe.open('tcp://127.0.0.1:5555')
    # r2.cmd('aa')

    # r2.cmd('doo')
    """
    .text:0000000000403EB1 mov     rdi, rax
    .text:0000000000403EB4 call    play_map
    .text:0000000000403EB9 mov     [rbp+var_4C], eax
    .text:0000000000403EBC cmp     [rbp+var_4C], 1
    """
    # Set breakpoint on play_map
    r2.cmd('db 0x403EB4')

    # Resume execution
    r2 = r2pipe.open('tcp://127.0.0.1:5555')
    r2.cmd('dc')

    while True:
        # Breakpoint hit, get address of map in rdi
        r2 = r2pipe.open('tcp://127.0.0.1:5555')
        map_addr = r2.cmdj('drj')['rdi']

        # Get goal state
        r2 = r2pipe.open('tcp://127.0.0.1:5555')
        goal_state = r2.cmdj('pv8j @  %d' %map_addr)['value']

        get_ships(goal_state)

        # Resume execution
        r2 = r2pipe.open('tcp://127.0.0.1:5555')
        r2.cmd('dc')


if __name__ == '__main__':
    main()

On completing all of the levels, it displays a message. Cleaning it up reveals.
Aye! You found some letters did ya? To find what you're looking for, you'll want to re-order them: 9, 1, 2, 7, 3, 5, 6, 5, 8, 0, 2, 3, 5, 6, 1, 4. Next you let 13 ROT in the sea! THE FINAL SECRET CAN BE FOUND WITH ONLY THE UPPER CASE.
As instructed, we ROT-13 the letters to get the string BUTWHEREISTHERUM. Feeding this to the application, we can get the flag.

Flag: y0u__sUnK_mY__P3Wp3w_b04t@flare-on.com

#6 - payload.dll

A PE32+ dll is provided which exports a single function named EntryPoint. However, trying to call this export via rundll32 fails. This was because DllMain modified the export table when called. Dumping the running dll from memory we were clearly able to see that the exported function name was indeed getting changed. The exported function took a name depending on the value of (year+month)%26. Using an x64dbg script, I was able to recover all of the possible names.

ctr = 0
@run:
init "C:\Documents and Settings\Administrator\Desktop\payload.dll"
doSleep 100
run 180005DDD
mov eax, ctr
run 180005D24
log =========================================================
log {d:ctr}
find rdx+33, 00
log function: {s:$result+1}
log =========================================================
stop
ctr = ctr + 1
cmp ctr, 1a
jl @run

Calling the obtained names using a batch script reveals the flag letter by letter,

@echo off
setlocal enabledelayedexpansion

set fn[0]=filingmeteorsgeminately
set fn[1]=leggykickedflutters
set fn[2]=incalculabilitycombustionsolvency
set fn[3]=crappingrewardsanctity
set fn[4]=evolvablepollutantgavial
set fn[5]=ammoniatesignifiesshampoo
set fn[6]=majesticallyunmarredcoagulate
set fn[7]=roommatedecapitateavoider
set fn[8]=fiendishlylicentiouslycolouristic
set fn[9]=sororityfoxyboatbill
set fn[10]=dissimilitudeaggregativewracks
set fn[11]=allophoneobservesbashfulness
set fn[12]=incuriousfatherlinessmisanthropically
set fn[13]=screensassonantprofessionalisms
set fn[14]=religionistmightplaythings
set fn[15]=airglowexactlyviscount
set fn[16]=thonggeotropicermines
set fn[17]=gladdingcocottekilotons
set fn[18]=diagrammaticallyhotfootsid
set fn[19]=corkerlettermenheraldically
set fn[20]=ulnacontemptuouscaps
set fn[21]=impureinternationalisedlaureates
set fn[22]=anarchisticbuttonedexhibitionistic
set fn[23]=tantalitemimicryslatted
set fn[24]=basophileslapsscrapping
set fn[25]=orphanedirreproducibleconfidences

for /l %%n in (0,1,25) do (
    set /a year=2001+%%n
    date 1-2-!year!
    rundll32 payload.dll !fn[%%n]! !fn[%%n]!
)

Flag: wuuut-exp0rts@flare-on.com

#7 - zsud.exe

The 7th challenge is a maze game implemented in PowerShell. The script was embedded inside a parent executable and ran using CLR Hosting. Dumping the script from memory was easy. The ps script implementing the game was obfuscated. After deobfuscating it manually, I was able to draw the maze by following the code.

Figure 4: The Maze
We need to traverse the maze in a specific way or else we fail. The function which executed a move was as follows.

function Invoke-MoveDirection($char, $room, $direction, $trailing) {
 $nextroom = $null
 $movetext = "You can't go $direction."
 $statechange_tristate = $null

 $nextroom = Get-RoomAdjoining $room $direction
 if ($nextroom -ne $null) {
  $key = Get-ThingByKeyword $char 'key'
  if (($key -ne $null) -and ($script:okaystopnow -eq $false)) {
   $dir_short = ([String]$direction[0]).ToLower()

   ${N} = ${sCRiPt:MSVcRt}::("rand").Invoke()%6
            Write-Host $N

   if ($directions_enum[$dir_short] -eq ($n)) {
    $script:key_directions += $dir_short
    $newdesc = Invoke-XformKey $script:key_directions $key.Desc
    $key.Desc = $newdesc
    if ($newdesc.Contains("@")) {
     $nextroom = $script:map.StartingRoom
     $script:okaystopnow = $true
    }
    $statechange_tristate = $true
   } else {
    $statechange_tristate = $false
   }
  }

  $script:room = $nextroom
  $movetext = "You go $($directions_short[$direction.ToLower()])"

  if ($statechange_tristate -eq $true) {
   $movetext += "`nThe key emanates some warmth..."
  } elseif ($statechange_tristate -eq $false) {
   $movetext += "`nHmm..."
  }

  if ($script:autolook -eq $true) {
   $movetext += "`n$(Get-LookText $char $script:room $trailing)"
  }
 } else {
  $movetext = "You can't go that way."
 }

 return "$movetext"
}

At a first glance, the use of random function seemed a bit strange. The first few correct moves could be obtained by brute-force. Inspecting the executable which hosted this script revealed that the rand function was actually hooked. Instead of returning random numbers the function returned numbers from the following list in sequential order.

3, 0, 0, 2, 2, 1, 1, 1, 0, 2, 3, 0, 2, 2, 3, 3, 3, 5, 4, 0, 5, 4, 0, 5, 4, 0, 1, 4, 0, 2, 4, 0, 1, 2, 3, 5, 4, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 5, 4, 0

Mapping the numbers to the directions we get the following list of 53 moves.

w, n, n, e, e, s, s, s, n, e, w, n, e, e, w, w, w, d, u, n, d, u, n, d, u, n, s, u, n, e, u, n, s, e, w, d, u, n, s, e, w, s, e, w, s, e, w, s, e, w, d, u, n

I used a macro creator tool to send these moves to the game window. On performing the last move we are taken back to the starting point at the vestibule. From there it was a matter to go to the office to get the flag.

Flag: mudd1ng_by_y0ur53lph@flare-on.com

#8 - flair.apk

Every year there is at least one challenge related to Android. This is it. An apk is provided. The challenge consists of four levels implemented by four different activities named Michael, Brian, Milton and Chotchkie. We need to solve them sequentially in order to get back the flag.

The first activity can be easily solved statically to get the password MYPRSHE__FTW. The second activity compared our input password with a string generated at runtime. The last two activities performs a calculation on our input to generate an array which was again compared. To solve these three levels, I used frida to hook on the relevant functions.

import frida
import time

jscode = """
console.log("[+] Script loaded successfully...");
console.log("[+] Java available: " + Java.available);

Java.perform(function x() 
{   
    console.log("[+] Entered perform.."); 
    
    // Second level
    var brian = Java.use("com.flare_on.flair.Brian");    
    brian.teraljdknh.implementation = function(s1,s2) 
    {
        console.log("[+] Entered com.flare_on.flair.Brian.teraljdknh"); 
        console.log("[*] arg1=" + s1);
        console.log("[*] arg2=" + s2);
        return this.teraljdknh(s1, s2);
    };
    console.log("[+] Replaced com.flare_on.flair.Brian.teraljdknh");


    // Third level    
    var milton = Java.use("com.flare_on.flair.Milton");        
    milton.nbsadf.implementation = function () 
    {
        console.log("[+] Entered com.flare_on.flair.Milton.nbsadf");        
        retval = this.nbsadf();
        console.log("[*] retval="+retval);                  
        return retval;        
    };
    console.log("[+] Replaced com.flare_on.flair.flair.Milton.nbsadf");  
        
    
    //Fourth level
    var stapler = Java.use("com.flare_on.flair.Stapler");        
    
    //Hook string decryptor
    stapler.iemm.implementation = function (p) 
    {
        console.log("[+] Entered com.flare_on.flair.Stapler.iemm");        
        retval = this.iemm(p);
        console.log("[*] arg1="+p);                  
        console.log("[*] retval="+retval);                  
        return retval;        
    };
    console.log("[+] Replaced com.flare_on.flair.flair.Stapler.iemm");    
    
    //Hook hardcoded array
    stapler.poserw.implementation = function (p) 
    {
        console.log("[+] Entered com.flare_on.flair.Stapler.poserw");        
        retval = this.poserw(p);                          
        console.log("[*] retval="+retval);                  
        return retval;        
    };
    console.log("[+] Replaced com.flare_on.flair.flair.Stapler.poserw");    

});
"""

device =  frida.get_usb_device()
process = device.attach('com.flare_on.flair')
script = process.create_script(jscode)
script.load()
raw_input()

The script hooks the important functions and logs both the arguments and the return value. Running this we get a log.

[+] Script loaded successfully...
[+] Java available: true
[+] Entered perform..
[+] Replaced com.flare_on.flair.Brian.teraljdknh
[+] Replaced com.flare_on.flair.flair.Milton.nbsadf
[+] Replaced com.flare_on.flair.flair.Stapler.iemm
[+] Replaced com.flare_on.flair.flair.Stapler.poserw
[+] Entered com.flare_on.flair.Brian.teraljdknh
[*] arg1=hashtag_covfefe_Fajitas!
[*] arg2=hashtag_covfefe_Fajitas!
[+] Entered com.flare_on.flair.Milton.nbsadf
[+] Entered com.flare_on.flair.Stapler.poserw
[*] retval=16,-82,-91,-108,-125,30,11,66,-71,86,-59,120,-17,-102,109,68,-18,57,-109,-115
[*] retval=16,-82,-91,-108,-125,30,11,66,-71,86,-59,120,-17,-102,109,68,-18,57,-109,-115
[+] Entered com.flare_on.flair.Milton.nbsadf
[+] Entered com.flare_on.flair.Stapler.poserw
[*] retval=16,-82,-91,-108,-125,30,11,66,-71,86,-59,120,-17,-102,109,68,-18,57,-109,-115
[*] retval=16,-82,-91,-108,-125,30,11,66,-71,86,-59,120,-17,-102,109,68,-18,57,-109,-115
[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=e.RP9SR8x9.GH.G8M9.GHkG
[*] retval=android.content.Context
[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=LHG1@@!SxeGS9.M9.GHkG
[*] retval=getApplicationContext

--------------
snip
--------------

[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=H?ye!v
[*] retval=equals
[+] Entered com.flare_on.flair.Stapler.poserw
[*] retval=95,27,-29,-55,-80,-127,-60,13,-33,-60,-96,35,-127,86,0,-114,-25,30,36,-92
[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=e.RP9SR8x9.GH.G8M9.GHkG
[*] retval=android.content.Context
[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=LHG1@@!SxeGS9.M9.GHkG
[*] retval=getApplicationContext

--------------
snip
--------------

[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=,e}e8yGS!81PPe(v
[*] retval=java.util.Arrays
[+] Entered com.flare_on.flair.Stapler.iemm
[*] arg1=H?ye!v
[*] retval=equals
[+] Entered com.flare_on.flair.Stapler.poserw
[*] retval=95,27,-29,-55,-80,-127,-60,13,-33,-60,-96,35,-127,86,0,-114,-25,30,36,-92

From the trace, we can see that the password for the 2nd activity is hashtag_covfefe_Fajitas!.The third and fourth activity required a bit of brute-force to get the respective password.

Third Activity
public class Main
{
  public static void main(String args[])
  {
    byte[] b_arr = new byte[] {16,-82,-91,-108,-125,30,11,66,-71,86,-59,120,-17,-102,109,68,-18,57,-109,-115};
    
    for (byte b: b_arr)
    {
      next:
      for (char c1 = 32; c1 < 123; c1++)
      {
        for (char c2 = 32; c2 < 123; c2++)
        {
          byte x = (byte)((Character.digit(c1, 16) << 4) + Character.digit(c2, 16));
          if (b == x)
          {
            System.out.print(c1 + "" + c2);
            break next;
          }
        }
      }
    }
  }
}
Password: 10aea594831e0b42b956c578ef9a6d44ee39938d

Fourth activity
public class Main
{
  public static void main(String args[])
  {
    byte[] b_arr = new byte[] {16,-82,-91,-108,-125,30,11,66,-71,86,-59,120,-17,-102,109,68,-18,57,-109,-115};
    
    for (byte b: b_arr)
    {
      next:
      for (char c1 = 32; c1 < 123; c1++)
      {
        for (char c2 = 32; c2 < 123; c2++)
        {
          byte x = (byte)((Character.digit(c1, 16) << 4) + Character.digit(c2, 16));
          if (b == x)
          {
            System.out.print(c1 + "" + c2);
            break next;
          }
        }
      }
    }
  }
}
Password: 5f1be3c9b081c40ddfc4a0238156008ee71e24a4

Flag: pc_lo4d_l3tt3r_gl1tch@flare-on.com

#9 - remorse.ino.hex

The challenge consists of a binary for an Arduino Uno in Intel Hex format. The microcontroller board used in the Uno is ATmega328p. This is an 8-bit RISC microprocessor. After consulting the pinout diagram it was evident that the state of Pin D was used as an input.

Figure 5: Pinout diagram of the ATmega328.
Source: https://www.arduino.cc/en/Hacking/PinMapping168
The 8 bit input from the pin is used as a key to xor a block of bytes. If  the decrypted data contained an @ sign at the appropriate position we have found the correct decryption key.

Figure 6: The xor loop in IDA
We can easily brute-force the decryption key and thus the flag in python.

li = [0xB5, 0xB5, 0x86, 0xB4, 0xF4, 0xB3, 0xF1, 0xB0, 0xB0, 
0xF1, 0xED, 0x80, 0xBB, 0x8F, 0xBF, 0x8D, 0xC6, 0x85, 0x87, 0xC0, 0x94, 0x81, 0x8C]

output = [0]  * len(li)

for k in xrange(255):
  for i in xrange(len(li)):
    v = (li[i]^k)+i
    if v > 255:
      v -= 256
    output[i] = chr(v)
  if output[-3] == 'c' and output[-2] == 'o' and output[-1] == 'm':
    print 'Xor key=', hex(k)
    print ''.join(output)
    break

The decryption key i.e. the state of Pin D must be 0xdb. We can also emulate the binary in AVR Studio with the XOR key to get the flag.

Figure 7: Simulating in AVR Studio
Flag: no_r3m0rs3@flare-on.com

#10 - shell.php

This one required a lot of manual work to go through. The overall idea of this level is to decrypt data encrypted using multi-byte xor in a chained fashion. I developed an interactive GUI tool in python which allowed me to try different keys and view the result of the decryption at the same time.

Figure 8: A tool to decrypt interactively
There are four such sub-levels in this challenge based on the same idea. Solving i.e finding the correct decryption key gives the flag.

Flag: th3_xOr_is_waaaay_too_w34k@flare-on.com


#11 - covefefe.exe

This was definitely the finest challenge of Flare-On 2017. The problem is based on a VM the instruction set of which consists of only a single instruction - subtracting the contents of one memory location from another. A search on Google reveals that this is a Subleq VM. The entire VM is implemented in few lines of code.

Figure 9: The decompiled code implementing the VM
With such a small code, I implemented the entire logic in python.

from __future__ import print_function
from vm import code
import random

userinput = None
input_idx = -1

# printf("%c", char)
def printfc(char):
    print(chr(char), end='')


# scanf("%c", &returned)
def scanfc():
    global userinput, input_idx
    if userinput is None:
        userinput = raw_input()
        input_idx = 0
        if userinput == '':
            return 0xA
        else:
            return ord(userinput[input_idx])
    else:
        input_idx += 1
        if input_idx == len(userinput):
            input_idx -= 1
            return 0xA
        else:
            return ord(userinput[input_idx])


def dispatch(a, b, c):
    code[b] -= code[a]
    if c != 0:
        return code[b] <= 0
    else:
        return False


def exec_vm(entry, size):
    pc = entry
    while pc + 3 <= size:
        if dispatch(code[pc], code[pc+1], code[pc+2]):
            if code[pc+2] == -1:
                return 1
            pc = code[pc+2]

        else:
            pc += 3

        if code[4] == 1:
            printfc(code[2])
            code[4] = 0
            code[2] = 0

        if code[3] == 1:
            code[1] = scanfc()
            code[3] = 0


def main():
    code[273] = random.randint(0, 32767) % code[272]
    exec_vm(1123, 4352)


if __name__ == '__main__':
    main()

From here, I modified the VM code to print the instruction pointer during each loop. This did not help as the total number of executed instructions were well over 100k. The trace looked like the screenshot below.

Figure 10: Execution trace of the VM
With such a huge trace, it was extremely difficult to figure out what was going on. After a bit of trial and errors, I decided to run a taint analysis to track how the input flowed through the code. The VM is essentially an array of integers. The idea was to mark a cell dirty if it was controllable by user input. If there was a comparison it would immediately show through. The VM code was modified to introduce the tainting features.

from __future__ import print_function
from vm import code
import random

scanf_count = 0
user_input = 'abcdefghijklmnopqrstuvwxyz_0123'[::-1]
tainted = []
track_taints = False
tainted_at_least_once = []

def taint(address, taint_src):
    if address not in tainted:
        tainted.append(address)
        print('[+] Tainted [{}] = {}'.format(address, code[address]))
    else:
        print('[+] Re-tainted [{}] = {}'.format(address, code[address]))

    if address not in tainted_at_least_once:
        tainted_at_least_once.append(address)


def is_tainted(address):
    return address in tainted

def untaint(address):
    if address in tainted:
        tainted.remove(address)
        print('[-] Untainted [{}]'.format(address))


# printf("%c", char)
def printfc(char):
    print(chr(char), end='')


def dispatch(a, b, c):
    global track_taints

    code[b] -= code[a]

    if track_taints:
        if a == b and is_tainted(b):
            untaint(b)

        elif is_tainted(a):
            taint(b, a)

        elif is_tainted(b):
            taint(b, a) # Retaint

    
    if c != 0:
        return code[b] <= 0
    else:
        return False


def exec_vm(entry, size):
    global scanf_count, track_taints
    pc = entry
    while pc + 3 <= size:
        if dispatch(code[pc], code[pc+1], code[pc+2]):
            if code[pc+2] == -1:
                return 1
            pc = code[pc+2]

        else:
            pc += 3

        if code[4] == 1:
            if track_taints:
                track_taints = False
                print('[!] Taint tracking OFF')
            printfc(code[2])
            code[4] = 0
            code[2] = 0

        if code[3] == 1:
            print('[*] Call scanf <<<<<<<<<<<<<<<<<<<<<<<<')
            if scanf_count == 0: 
                print('[!] Taint tracking ON')
                track_taints = True
                code[1] = ord(user_input[scanf_count])
                scanf_count += 1
                taint(1, 'external')

            elif scanf_count < len(user_input):
                code[1] = ord(user_input[scanf_count])
                scanf_count += 1
                taint(1, 'external')

            else:
                code[1] = 0xA

            code[3] = 0

def main():
    global tainted, tainted_at_least_once
    code[273] = 0 #random.randint(0, 32767) % code[272]
    exec_vm(1123, 4352)

    print(tainted)
    for t in tainted:
        print('[{}] = {}'.format(t, code[t]))
    print('Tainted at least once')
    print(tainted_at_least_once)


if __name__ == '__main__':
    main()

The taint trace was significantly reduced in length compared to the execution trace.

Figure 11: Taint trace

From the trace, it was clear, that the characters at the odd positions very multiplied by 15 and left shifted seven times. Inspecting the VM bytecode there were a series of numbers which looked to be the result of the calculation on the characters at the odd positions.

Figure 12: A strange sequence of integers!
Reversing the calculation, the characters at the odd position were found. The remaining characters at the even positions were found using Google and an English dictionary. The regex search feature in Notepad++ really helped here.

Flag: subleq_and_reductio_ad_absurdum@flare-on.com

#12 - [missing]

The last challenge is so convoluted that an entire CTF contest can be made on this. A malware ran on a machine and exfiltrated some files. The task is to reconstruct the exact set of events that occurred based on the network traffic capture. I am not going to describe everything in detail as it will easily cover many blog posts. Instead, I will only focus my approach.

The network topology can be described by the next figure.

Figure 13: The network topology
The Command & Control communicates with the machine having an IP 192.168.221.91. This is used as a pivot to attack the other system 192.168.221.105. The pcap file which we have been provided contains the network traffic between C&C and the first system.

There are multiple stages in the malware. The first stage (coolprogram.exe) downloads the second stage (srv.exe) and executes it. The second stage is the principal malware. The functionality of this malware is built upon plugins. Plugins can be of three types - cryptography (CRYP), compression (COMP) and command (CMD). The traffic between the malware and its C&C is mostly encrypted except at the start when the CRYP plugins have not yet been loaded. Plugins are DLL files with a modified PE header.

To recreate the exact set of events, we need to replay the network traffic from the PCAP. I wrote a Python script using the pyshark library. Since we were replaying the packets there were no necessity to listen for the responses from the malware. However with this approach the malware freezed after running for some time as the send buffer filled up. To remedy the situation I had to patch ws2_32.send to discard all the packets sent to it.

import pyshark
import socket
import time
import threading

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('', 9443))
sock.listen(5)
print '[++] Waiting for connection...'
(clientsocket, address) = sock.accept()
print '[++] Accepting connection from', address


cap = pyshark.FileCapture('data-only.pcap')

while idx < 4144:
    packet = cap[idx]
    data = packet.data.data.decode('hex')
    buflen = len(data)
 

 # Data packet from server to client
    if packet.ip.src == server_ip:
        clientsocket.send(data) 
        print '[<-] Sent %d bytes from index %d' %(buflen, idx)
        time.sleep(0.1)
  
    idx += 1 

When the malware is running under the control of the debugger it's possible to intercept the process and dump the buffer containing the plugins from memory. Using another script I corrected the PE header of the dumped DLLs so that IDA and other tools can analyze it.

import sys
import struct

ifile = open(sys.argv[1], 'rb')
ofile = open(sys.argv[2], 'wb')

# MZ header
print '[+] Correcting MZ header'
assert ifile.read(2) == 'LM'
ofile.write('MZ')

# Write up to e_lfanew
ofile.write(ifile.read(0x3c-2))

e_lfanew = struct.unpack('<I', ifile.read(4))[0]
ofile.write(struct.pack('<I', e_lfanew))

ofile.write(ifile.read(e_lfanew - (0x3c + 4)))

# PE header
print '[+] Correcting PE header'
assert ifile.read(4) == 'NOP\0'
ofile.write('PE\0\0')

# Machine
print '[+] Correcting PE.Machine'
assert ifile.read(2) == '32'
ofile.write('\x4c\x01')

ofile.write(ifile.read(0x22))

print '[+] Correcting Address of entrypoint'
# Address of entrypoint
entrypoint = struct.unpack('<I', ifile.read(4))[0] ^ 0xabcdabcd
ofile.write(struct.pack('<I', entrypoint))

ofile.write(ifile.read())

ifile.close()
ofile.close()

print '[+] Done' 

The second stage running on 192.168.221.91 loads 9 plugins. Each plugin has a unique 16-byte signature. The signature is also present in each of packets to determine which plugin will process that particular data packet. Out of the 9 plugins - 4 deal with crypto, 1 with compression and the remaining are command plugins.

The second stage then uses psexec to copy itself over to the third system at 192.168.221.105 and execute it. One of the command plugin acts as a relay and forwards all traffic between the C&C and the third system in both directions. This third stage loads up 9 more plugins - all of which are relayed by the second system from the C&C.

Thus there are 18 plugins in total out of which 8 deals with cryptography, 2 with compression and remaining are command plugins. The crypto and compression plugins are shown in the table below.



Algorithm Type Key Size IV Size Mode
STAGE 2 RC4 Crypto 16 - -
Transposition cipher Crypto - - -
Custom base64 Crypto - - -
XTEA Crypto 16 8 CBC
ZLIB Comp - - -
STAGE 3 Blowfish Crypto 16 8 CBC
XOR Crypto 4 - ECB
Triple DES Crypto 24 8 CBC
Camellia Crypto 16 - ECB
Aplib Comp - - -


The above plugins implement standard crypto/compression algorithms. Hence I reimplemented them in python.

I have provided the source code of all of the plugins implemented in python.
https://gist.github.com/extremecoders-re/b7caf1a5d2f884733a75dcdc80d8e384

Once the plugins were implemented in Python, decrypting the traffic was simple. The decrypted traffic contained a BMP image without a header. The entire image was split across multiple packets. After assembling them properly and adding the header we get the following image containing a password.

Figure 13: Decrypted BMP

The third stage running on larryjohnson-pc encrypted a file lab10.zip to lab10.zip.cry and exfiltrated it to the server via the stage2 relay. Decrypting the traffic using our plugins and reassembling the pieces we can reconstruct the cry file.

The encryptor named cf.exe is present in the captured traffic. Based on the decompiled C# code of the encryptor we can build a decrypter to get back the zip file.

using System;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace cf_decrypter
{
    class Program
    {
        static void Main(string[] args)
        {
            using (FileStream fileStream = File.Open("lab10.zip.cry", FileMode.Open))
            {
                byte[] signature = new byte[4];
                
                fileStream.Read(signature, 0, 4);
                string sign = Encoding.ASCII.GetString(signature);

                if (sign.Equals("cryp"))
                {
                    byte[] IV = new byte[16];

                    // Read IV
                    fileStream.Read(IV, 0, IV.Length);

                    byte[] sha256_hash = new byte[32];

                    //Read SHA256 hash
                    fileStream.Read(sha256_hash, 0, sha256_hash.Length);

                    int ciphertext_len = (int)(fileStream.Length - fileStream.Position);

                    byte[] ciphertext = new byte[ciphertext_len];

                    // Read cipher text
                    fileStream.Read(ciphertext, 0, ciphertext_len);

                    byte[] key = Convert.FromBase64String("tCqlc2+fFiLcuq1ee1eAPOMjxcdijh8z0jrakMA/jxg=");
                    Aes aes = Aes.Create();
                    aes.KeySize = 256;
                    aes.Key = key;
                    aes.IV = IV;
                    aes.Padding = PaddingMode.PKCS7;
                    aes.Mode = CipherMode.CBC;

                    ICryptoTransform transform = aes.CreateDecryptor();

                    using (MemoryStream memoryStream = new MemoryStream())
                    {
                        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(ciphertext, 0, ciphertext.Length);
                            //cryptoStream.FlushFinalBlock();
                            File.WriteAllBytes("decrypted", memoryStream.ToArray());                            
                        }
                    }
                }
            }
        }
    }
}
 

The decrypted zip file was password protected. The password can be found in the BMP image. Opening the zip there is an x86_64 ELF written in Golang. Running the ELF gives the flag.

Flag: n3v3r_gunna_l3t_you_down_1987_4_ever@flare-on.com


Final Words

Overall, I feel the challenges this year were harder than the previous years. Challenge #11 and #12 deserve special mention. Challenge #12, in particular, can get very tough and time taking if not approached in the proper way. With this, we come to the end of this blog post. I would like to thank  Crystalboy, rand0m, Alexander Polyakov and Peter Huene for their tips.

Tuesday, 11 July 2017

Deobfuscating PjOrion using bytecode simplifier

Bytecode simplifier is a tool to de-obfuscate PjOrion protected python scripts. This post is a short tutorial to show how to use this module to deobfuscate a protected python script.

I have used the sample code below to demonstrate its usage. This is a small program to calculate the factorial of a number.

# Python program to find the factorial of a number using recursion
 
def recur_factorial(n):
   """Function to return the factorial
   of a number using recursion"""
   if n == 1:
       return n
   else:
       return n*recur_factorial(n-1)
 
 
# take input from the user
num = int(input("Enter a number: "))
 
# check is the number is negative
if num < 0:
   print("Sorry, factorial does not exist for negative numbers")
elif num == 0:
   print("The factorial of 0 is 1")
else:
   print("The factorial of",num,"is",recur_factorial(num))

I have first compiled the script and protected it as shown in Figure 1 & 2.

Compiling the script
Figure 1: Compiling the script
Figure 2: Protecting the generated pyc file
Figure 2: Protecting the generated pyc file
After protection, we get a large file example.pyc about 27 KiB in size. This is the file we will be working on.

The stock python interpreter does not have bytecode tracing facilities inbuilt. Hence we have to use a modified version of Python which supports bytecode tracing. I have provided a precompiled version of Python 2.7.13 with bytecode tracing support at Github. The python27.dll file has to be copied to  C:\Windows\System32\. Make sure to backup the existing dll so that you can revert when finished.

Step - 1: Unwrapping the layers


The first step is to unwrap the protection layers to get hold of the actual obfuscated code object. For this, we will be using the pjunwrapper module as shown below.

C:\pj-dump>python pjunwrapper.py --ifile=example.pyc
XXX lineno: 1, opcode: 156
[*] Dumped 1 code object
XXX lineno: 1, opcode: 213
XXX lineno: 1, opcode: 184
XXX lineno: 1, opcode: 240
XXX lineno: 1, opcode: 240
XXX lineno: 1, opcode: 240
XXX lineno: 1, opcode: 240
[*] Dumped 1 code object
XXX lineno: 1, opcode: 7
XXX lineno: 1, opcode: 45
[*] Dumped 1 code object
XXX lineno: 1, opcode: 161
Enter a number: ^D
Error in module '__main__': unexpected EOF while parsing (<string>, line 1)

PjUnwrapper requires the pystack extension module. Make sure that the extension is present in python path. Running this, some files having names of wrapper_ would be dumped. These are basically the wrapper layers over the actual obfuscated code. In our case, the obfuscated code has a file name wrapper_3.pyc as shown in Figure 3. In general, the highest numbered file contains the final obfuscated code.


Figure 3: Unwrapping the protection layers
Figure 3: Unwrapping the protection layers

Step - 2: Deobfuscating


The final step is to run bytecode_simplifier over wrapper_3.pyc as shown below.

C:\bytecode_simplifier\main.py --ifile=wrapper_3.pyc --ofile=wrapper_deobf.pyc
INFO:__main__:Opening file wrapper_3.pyc
INFO:__main__:Input pyc file header matched
DEBUG:__main__:Unmarshalling file
INFO:__main__:Processing code object \x0f\x1d\n\x00\x07\x0f\x0f
DEBUG:deobfuscator:Code entrypoint matched PjOrion signature v1
INFO:deobfuscator:Original code entrypoint at 269
INFO:deobfuscator:Starting control flow analysis...
DEBUG:disassembler:Finding leaders...
DEBUG:disassembler:Start leader at 269
DEBUG:disassembler:End leader at 272
DEBUG:disassembler:Start leader at 272
DEBUG:disassembler:End leader at 117
DEBUG:disassembler:Start leader at 117
DEBUG:disassembler:End leader at 82
DEBUG:disassembler:Start leader at 82
DEBUG:disassembler:End leader at 28
DEBUG:disassembler:Start leader at 28
DEBUG:disassembler:End leader at 177
DEBUG:disassembler:Start leader at 177
DEBUG:disassembler:End leader at 125
DEBUG:disassembler:Start leader at 125
DEBUG:disassembler:End leader at 155
DEBUG:disassembler:Start leader at 155
DEBUG:disassembler:End leader at 60
DEBUG:disassembler:Start leader at 60
DEBUG:disassembler:End leader at 165
DEBUG:disassembler:Start leader at 165
DEBUG:disassembler:End leader at 353
DEBUG:disassembler:Start leader at 353
DEBUG:disassembler:End leader at 303
DEBUG:disassembler:Start leader at 303
DEBUG:disassembler:End leader at 190
DEBUG:disassembler:Start leader at 190
DEBUG:disassembler:End leader at 235
DEBUG:disassembler:Start leader at 235
DEBUG:disassembler:Start leader at 235
DEBUG:disassembler:End leader at 51
DEBUG:disassembler:Start leader at 51
DEBUG:disassembler:End leader at 238
DEBUG:disassembler:Start leader at 238
DEBUG:disassembler:End leader at 313
DEBUG:disassembler:Start leader at 313
DEBUG:disassembler:End leader at 105
DEBUG:disassembler:Start leader at 105
DEBUG:disassembler:End leader at 246
DEBUG:disassembler:Start leader at 246
DEBUG:disassembler:End leader at 142
DEBUG:disassembler:Start leader at 142
DEBUG:disassembler:End leader at 71
DEBUG:disassembler:Start leader at 71
DEBUG:disassembler:End leader at 229
DEBUG:disassembler:Start leader at 229
DEBUG:disassembler:End leader at 33
DEBUG:disassembler:Start leader at 33
DEBUG:disassembler:Start leader at 33
DEBUG:disassembler:End leader at 44
DEBUG:disassembler:Start leader at 44
DEBUG:disassembler:End leader at 342
DEBUG:disassembler:Start leader at 342
DEBUG:disassembler:End leader at 36
DEBUG:disassembler:Start leader at 36
DEBUG:disassembler:End leader at 94
DEBUG:disassembler:Start leader at 94
DEBUG:disassembler:End leader at 17
DEBUG:disassembler:Start leader at 17
DEBUG:disassembler:End leader at 285
DEBUG:disassembler:Start leader at 285
DEBUG:disassembler:End leader at 295
DEBUG:disassembler:Start leader at 295
DEBUG:disassembler:End leader at 257
DEBUG:disassembler:Start leader at 257
DEBUG:disassembler:End leader at 197
DEBUG:disassembler:Start leader at 197
DEBUG:disassembler:End leader at 349
DEBUG:disassembler:End leader at 207
DEBUG:disassembler:Start leader at 207
DEBUG:disassembler:End leader at 361
DEBUG:disassembler:Start leader at 361
DEBUG:disassembler:End leader at 221
DEBUG:disassembler:Start leader at 221
DEBUG:disassembler:End leader at 332
DEBUG:disassembler:Start leader at 332
DEBUG:disassembler:End leader at 324
DEBUG:disassembler:Start leader at 324
DEBUG:disassembler:End leader at 134
DEBUG:disassembler:Start leader at 134
DEBUG:disassembler:End leader at 369
DEBUG:disassembler:Start leader at 369
DEBUG:disassembler:End leader at 6
DEBUG:disassembler:Start leader at 6
DEBUG:disassembler:End leader at 94
DEBUG:disassembler:Start leader at 94
DEBUG:disassembler:Found 81 leaders
DEBUG:disassembler:Constructing basic blocks...
DEBUG:disassembler:Creating basic block 0x24bd800 spanning from 5 to 6, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca558 spanning from 14 to 17, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca5d0 spanning from 25 to 28, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca648 spanning from 33 to 33, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca698 spanning from 36 to 36, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca6e8 spanning from 44 to 44, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca738 spanning from 51 to 51, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca788 spanning from 57 to 60, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca800 spanning from 68 to 71, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca878 spanning from 79 to 82, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca8f0 spanning from 93 to 94, end exclusive
DEBUG:disassembler:Creating basic block 0x24ca940 spanning from 94 to 94, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca990 spanning from 102 to 105, both inclusive
DEBUG:disassembler:Creating basic block 0x24caa30 spanning from 114 to 117, both inclusive
DEBUG:disassembler:Creating basic block 0x24caaf8 spanning from 122 to 125, both inclusive
DEBUG:disassembler:Creating basic block 0x24cabc0 spanning from 131 to 134, both inclusive
DEBUG:disassembler:Creating basic block 0x24cac88 spanning from 141 to 142, both inclusive
DEBUG:disassembler:Creating basic block 0x24cad50 spanning from 152 to 155, both inclusive
DEBUG:disassembler:Creating basic block 0x24cae18 spanning from 162 to 165, both inclusive
DEBUG:disassembler:Creating basic block 0x24caee0 spanning from 174 to 177, both inclusive
DEBUG:disassembler:Creating basic block 0x24cafa8 spanning from 187 to 190, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce0a8 spanning from 196 to 197, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce170 spanning from 204 to 207, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce238 spanning from 218 to 221, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce300 spanning from 228 to 229, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce3c8 spanning from 235 to 235, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce468 spanning from 238 to 238, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce508 spanning from 243 to 246, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce5d0 spanning from 254 to 257, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce698 spanning from 269 to 272, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce760 spanning from 282 to 285, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce828 spanning from 292 to 295, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce8f0 spanning from 300 to 303, both inclusive
DEBUG:disassembler:Creating basic block 0x24ce9b8 spanning from 310 to 313, both inclusive
DEBUG:disassembler:Creating basic block 0x24cea80 spanning from 321 to 324, both inclusive
DEBUG:disassembler:Creating basic block 0x24ceb48 spanning from 332 to 332, both inclusive
DEBUG:disassembler:Creating basic block 0x24cebe8 spanning from 342 to 342, both inclusive
DEBUG:disassembler:Creating basic block 0x24cec88 spanning from 349 to 349, both inclusive
DEBUG:disassembler:Creating basic block 0x24ced28 spanning from 350 to 353, both inclusive
DEBUG:disassembler:Creating basic block 0x24cedf0 spanning from 360 to 361, both inclusive
DEBUG:disassembler:Creating basic block 0x24ceeb8 spanning from 366 to 369, both inclusive
DEBUG:disassembler:41 basic blocks created
DEBUG:disassembler:Constructing edges between basic blocks...
DEBUG:disassembler:Adding explicit edge from block 0x24bd800 to 0x24ca8f0
DEBUG:disassembler:Adding explicit edge from block 0x24ca800 to 0x24ca648
DEBUG:disassembler:Adding explicit edge from block 0x24ce828 to 0x24cec88
DEBUG:disassembler:Adding explicit edge from block 0x24ca878 to 0x24ca5d0
DEBUG:disassembler:Adding explicit edge from block 0x24ce0a8 to 0x24cedf0
DEBUG:disassembler:Adding explicit edge from block 0x24ca940 to 0x24ce828
DEBUG:disassembler:Adding explicit edge from block 0x24ca6e8 to 0x24ca940
DEBUG:disassembler:Adding explicit edge from block 0x24ce170 to 0x24ce238
DEBUG:disassembler:Adding explicit edge from block 0x24ca990 to 0x24cac88
DEBUG:disassembler:Adding explicit edge from block 0x24ce9b8 to 0x24ce508
DEBUG:disassembler:Adding explicit edge from block 0x24caa30 to 0x24ca878
DEBUG:disassembler:Adding explicit edge from block 0x24cea80 to 0x24cabc0
DEBUG:disassembler:Adding explicit edge from block 0x24caaf8 to 0x24cad50
DEBUG:disassembler:Adding explicit edge from block 0x24ce300 to 0x24ca6e8
DEBUG:disassembler:Adding explicit edge from block 0x24ceb48 to 0x24ca940
DEBUG:disassembler:Adding implicit edge from block 0x24ce3c8 to 0x24ce468
DEBUG:disassembler:Adding explicit edge from block 0x24ce3c8 to 0x24ca738
DEBUG:disassembler:Adding explicit edge from block 0x24cabc0 to 0x24ceeb8
DEBUG:disassembler:Adding explicit edge from block 0x24cebe8 to 0x24ca558
DEBUG:disassembler:Adding explicit edge from block 0x24ce468 to 0x24ca990
DEBUG:disassembler:Adding explicit edge from block 0x24cac88 to 0x24ce300
DEBUG:disassembler:Adding explicit edge from block 0x24ce508 to 0x24ca800
DEBUG:disassembler:Adding explicit edge from block 0x24ced28 to 0x24ce8f0
DEBUG:disassembler:Adding explicit edge from block 0x24ce238 to 0x24cea80
DEBUG:disassembler:Adding explicit edge from block 0x24ca558 to 0x24ce5d0
DEBUG:disassembler:Adding explicit edge from block 0x24ce8f0 to 0x24cafa8
DEBUG:disassembler:Adding explicit edge from block 0x24ca5d0 to 0x24caee0
DEBUG:disassembler:Adding explicit edge from block 0x24ce5d0 to 0x24ce170
DEBUG:disassembler:Adding explicit edge from block 0x24cedf0 to 0x24ceb48
DEBUG:disassembler:Adding explicit edge from block 0x24cae18 to 0x24ced28
DEBUG:disassembler:Adding implicit edge from block 0x24ca648 to 0x24ca698
DEBUG:disassembler:Adding explicit edge from block 0x24ca648 to 0x24cebe8
DEBUG:disassembler:Adding explicit edge from block 0x24ca698 to 0x24ce760
DEBUG:disassembler:Adding explicit edge from block 0x24ceeb8 to 0x24bd800
DEBUG:disassembler:Adding explicit edge from block 0x24caee0 to 0x24caaf8
DEBUG:disassembler:Adding explicit edge from block 0x24ca738 to 0x24ce9b8
DEBUG:disassembler:Adding explicit edge from block 0x24ce760 to 0x24ce0a8
DEBUG:disassembler:Adding explicit edge from block 0x24ce698 to 0x24caa30
DEBUG:disassembler:Adding explicit edge from block 0x24ca788 to 0x24cae18
DEBUG:disassembler:Adding explicit edge from block 0x24cafa8 to 0x24ce3c8
DEBUG:disassembler:Adding explicit edge from block 0x24cad50 to 0x24ca788
INFO:deobfuscator:Control flow analysis completed.
INFO:deobfuscator:Starting simplication of basic blocks...
DEBUG:simplifier:Eliminating forwarders...
INFO:simplifier:Adding explicit edge from block 0x24ceb48 to 0x24ce828
INFO:simplifier:Adding explicit edge from block 0x24ca6e8 to 0x24ce828
INFO:simplifier:Adding implicit edge from block 0x24ca8f0 to 0x24ce828
DEBUG:simplifier:Forwarder basic block 0x24ca940 eliminated
INFO:simplifier:Adding explicit edge from block 0x24ce300 to 0x24ce828
DEBUG:simplifier:Forwarder basic block 0x24ca6e8 eliminated
INFO:simplifier:Adding explicit edge from block 0x24cedf0 to 0x24ce828
DEBUG:simplifier:Forwarder basic block 0x24ceb48 eliminated
INFO:simplifier:Adding explicit edge from block 0x24ca648 to 0x24ca558
DEBUG:simplifier:Forwarder basic block 0x24cebe8 eliminated
INFO:simplifier:Adding implicit edge from block 0x24ce3c8 to 0x24ca990
DEBUG:simplifier:Forwarder basic block 0x24ce468 eliminated
INFO:simplifier:Adding implicit edge from block 0x24ca648 to 0x24ce760
DEBUG:simplifier:Forwarder basic block 0x24ca698 eliminated
INFO:simplifier:Adding explicit edge from block 0x24ce3c8 to 0x24ce9b8
DEBUG:simplifier:Forwarder basic block 0x24ca738 eliminated
INFO:simplifier:7 basic blocks eliminated
DEBUG:simplifier:Merging basic blocks...
INFO:simplifier:Adding explicit edge from block 0x24ceeb8 to 0x24ca8f0
DEBUG:simplifier:Basic block 0x24bd800 merged with block 0x24ceeb8
INFO:simplifier:Adding explicit edge from block 0x24ce508 to 0x24ca648
DEBUG:simplifier:Basic block 0x24ca800 merged with block 0x24ce508
INFO:simplifier:Adding explicit edge from block 0x24caa30 to 0x24ca5d0
DEBUG:simplifier:Basic block 0x24ca878 merged with block 0x24caa30
INFO:simplifier:Adding explicit edge from block 0x24ce760 to 0x24cedf0
DEBUG:simplifier:Basic block 0x24ce0a8 merged with block 0x24ce760
INFO:simplifier:Adding implicit edge from block 0x24ceeb8 to 0x24ce828
DEBUG:simplifier:Basic block 0x24ca8f0 merged with block 0x24ceeb8
INFO:simplifier:Adding explicit edge from block 0x24ce5d0 to 0x24ce238
DEBUG:simplifier:Basic block 0x24ce170 merged with block 0x24ce5d0
INFO:simplifier:Adding explicit edge from block 0x24ce698 to 0x24ca5d0
DEBUG:simplifier:Basic block 0x24caa30 merged with block 0x24ce698
INFO:simplifier:Adding explicit edge from block 0x24ce238 to 0x24cabc0
DEBUG:simplifier:Basic block 0x24cea80 merged with block 0x24ce238
INFO:simplifier:Adding explicit edge from block 0x24caee0 to 0x24cad50
DEBUG:simplifier:Basic block 0x24caaf8 merged with block 0x24caee0
INFO:simplifier:Adding explicit edge from block 0x24cac88 to 0x24ce828
DEBUG:simplifier:Basic block 0x24ce300 merged with block 0x24cac88
DEBUG:simplifier:Basic block 0x24cec88 merged with block 0x24ce828
INFO:simplifier:Adding implicit edge from block 0x24cafa8 to 0x24ca990
INFO:simplifier:Adding explicit edge from block 0x24cafa8 to 0x24ce9b8
DEBUG:simplifier:Basic block 0x24ce3c8 merged with block 0x24cafa8
INFO:simplifier:Adding explicit edge from block 0x24ce238 to 0x24ceeb8
DEBUG:simplifier:Basic block 0x24cabc0 merged with block 0x24ce238
INFO:simplifier:Adding explicit edge from block 0x24ca990 to 0x24ce828
DEBUG:simplifier:Basic block 0x24cac88 merged with block 0x24ca990
INFO:simplifier:Adding explicit edge from block 0x24ce9b8 to 0x24ca648
DEBUG:simplifier:Basic block 0x24ce508 merged with block 0x24ce9b8
INFO:simplifier:Adding explicit edge from block 0x24cae18 to 0x24ce8f0
DEBUG:simplifier:Basic block 0x24ced28 merged with block 0x24cae18
INFO:simplifier:Adding explicit edge from block 0x24ce5d0 to 0x24ceeb8
DEBUG:simplifier:Basic block 0x24ce238 merged with block 0x24ce5d0
INFO:simplifier:Adding explicit edge from block 0x24cae18 to 0x24cafa8
DEBUG:simplifier:Basic block 0x24ce8f0 merged with block 0x24cae18
INFO:simplifier:Adding explicit edge from block 0x24ce698 to 0x24caee0
DEBUG:simplifier:Basic block 0x24ca5d0 merged with block 0x24ce698
INFO:simplifier:Adding explicit edge from block 0x24ca558 to 0x24ceeb8
DEBUG:simplifier:Basic block 0x24ce5d0 merged with block 0x24ca558
INFO:simplifier:Adding explicit edge from block 0x24ce760 to 0x24ce828
DEBUG:simplifier:Basic block 0x24cedf0 merged with block 0x24ce760
INFO:simplifier:Adding explicit edge from block 0x24ca788 to 0x24cafa8
DEBUG:simplifier:Basic block 0x24cae18 merged with block 0x24ca788
INFO:simplifier:Adding explicit edge from block 0x24ce9b8 to 0x24ca558
INFO:simplifier:Adding implicit edge from block 0x24ce9b8 to 0x24ce760
DEBUG:simplifier:Basic block 0x24ca648 merged with block 0x24ce9b8
INFO:simplifier:Adding implicit edge from block 0x24ca558 to 0x24ce828
DEBUG:simplifier:Basic block 0x24ceeb8 merged with block 0x24ca558
INFO:simplifier:Adding explicit edge from block 0x24ce698 to 0x24cad50
DEBUG:simplifier:Basic block 0x24caee0 merged with block 0x24ce698
INFO:simplifier:Adding explicit edge from block 0x24cad50 to 0x24cafa8
DEBUG:simplifier:Basic block 0x24ca788 merged with block 0x24cad50
INFO:simplifier:Adding implicit edge from block 0x24cad50 to 0x24ca990
INFO:simplifier:Adding explicit edge from block 0x24cad50 to 0x24ce9b8
DEBUG:simplifier:Basic block 0x24cafa8 merged with block 0x24cad50
INFO:simplifier:Adding implicit edge from block 0x24ce698 to 0x24ca990
INFO:simplifier:Adding explicit edge from block 0x24ce698 to 0x24ce9b8
DEBUG:simplifier:Basic block 0x24cad50 merged with block 0x24ce698
INFO:simplifier:28 basic blocks merged.
INFO:deobfuscator:Simplication of basic blocks completed.
INFO:deobfuscator:Beginning verification of simplified basic block graph...
INFO:deobfuscator:Verification succeeded.
INFO:deobfuscator:Assembling basic blocks...
DEBUG:assembler:Performing a DFS on the graph to generate the layout of the blocks.
DEBUG:assembler:Morphing some JUMP_ABSOLUTE instructions to make file decompilable.
DEBUG:assembler:Verifying generated layout...
DEBUG:assembler:Successfully verified layout.
DEBUG:assembler:Calculating addresses of basic blocks.
DEBUG:assembler:Calculating instruction operands.
DEBUG:assembler:Generating code...
INFO:deobfuscator:Successfully assembled. 
INFO:__main__:Successfully deobfuscated code object \x0f\x1d\n\x00\x07\x0f\x0f
INFO:__main__:Collecting constants for code object \x0f\x1d\n\x00\x07\x0f\x0f
INFO:__main__:Code object \x0f\x1d\n\x00\x07\x0f\x0f contains embedded code object recur_factorial
INFO:__main__:Processing code object recur_factorial
DEBUG:deobfuscator:Code entrypoint matched PjOrion signature v2
INFO:deobfuscator:Original code entrypoint at 161
INFO:deobfuscator:Starting control flow analysis...
DEBUG:disassembler:Finding leaders...
DEBUG:disassembler:Start leader at 161
DEBUG:disassembler:End leader at 164
DEBUG:disassembler:Start leader at 164
DEBUG:disassembler:End leader at 46
DEBUG:disassembler:Start leader at 46
DEBUG:disassembler:End leader at 141
DEBUG:disassembler:Start leader at 141
DEBUG:disassembler:End leader at 19
DEBUG:disassembler:Start leader at 19
DEBUG:disassembler:Start leader at 19
DEBUG:disassembler:End leader at 127
DEBUG:disassembler:Start leader at 127
DEBUG:disassembler:End leader at 22
DEBUG:disassembler:Start leader at 22
DEBUG:disassembler:End leader at 66
DEBUG:disassembler:Start leader at 66
DEBUG:disassembler:End leader at 105
DEBUG:disassembler:Start leader at 105
DEBUG:disassembler:End leader at 87
DEBUG:disassembler:Start leader at 87
DEBUG:disassembler:End leader at 126
DEBUG:disassembler:End leader at 154
DEBUG:disassembler:Start leader at 154
DEBUG:disassembler:End leader at 113
DEBUG:disassembler:Start leader at 113
DEBUG:disassembler:End leader at 93
DEBUG:disassembler:Start leader at 93
DEBUG:disassembler:End leader at 34
DEBUG:disassembler:Start leader at 34
DEBUG:disassembler:End leader at 11
DEBUG:disassembler:Start leader at 11
DEBUG:disassembler:End leader at 53
DEBUG:disassembler:Found 32 leaders
DEBUG:disassembler:Constructing basic blocks...
DEBUG:disassembler:Creating basic block 0x24ce3a0 spanning from 10 to 11, both inclusive
DEBUG:disassembler:Creating basic block 0x24bddc8 spanning from 19 to 19, both inclusive
DEBUG:disassembler:Creating basic block 0x24bda30 spanning from 22 to 22, both inclusive
DEBUG:disassembler:Creating basic block 0x24bdad0 spanning from 31 to 34, both inclusive
DEBUG:disassembler:Creating basic block 0x24bd9b8 spanning from 43 to 46, both inclusive
DEBUG:disassembler:Creating basic block 0x24bde68 spanning from 53 to 53, both inclusive
DEBUG:disassembler:Creating basic block 0x24bdc10 spanning from 63 to 66, both inclusive
DEBUG:disassembler:Creating basic block 0x24bdcb0 spanning from 84 to 87, both inclusive
DEBUG:disassembler:Creating basic block 0x24bd7d8 spanning from 92 to 93, both inclusive
DEBUG:disassembler:Creating basic block 0x24bdb98 spanning from 102 to 105, both inclusive
DEBUG:disassembler:Creating basic block 0x24bdf58 spanning from 110 to 113, both inclusive
DEBUG:disassembler:Creating basic block 0x24bdb48 spanning from 126 to 126, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca8a0 spanning from 127 to 127, both inclusive
DEBUG:disassembler:Creating basic block 0x24caf58 spanning from 138 to 141, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca7b0 spanning from 151 to 154, both inclusive
DEBUG:disassembler:Creating basic block 0x24ca670 spanning from 161 to 164, both inclusive
DEBUG:disassembler:16 basic blocks created
DEBUG:disassembler:Constructing edges between basic blocks...
DEBUG:disassembler:Adding explicit edge from block 0x24bdc10 to 0x24bdcb0
DEBUG:disassembler:Adding explicit edge from block 0x24ca7b0 to 0x24bdf58
DEBUG:disassembler:Adding explicit edge from block 0x24bda30 to 0x24bdb98
DEBUG:disassembler:Adding explicit edge from block 0x24ca670 to 0x24bd9b8
DEBUG:disassembler:Adding explicit edge from block 0x24ca8a0 to 0x24bdc10
DEBUG:disassembler:Adding explicit edge from block 0x24bdcb0 to 0x24ca7b0
DEBUG:disassembler:Adding explicit edge from block 0x24bdad0 to 0x24ce3a0
DEBUG:disassembler:Adding explicit edge from block 0x24bdf58 to 0x24bd7d8
DEBUG:disassembler:Adding explicit edge from block 0x24bdb98 to 0x24bdb48
DEBUG:disassembler:Adding explicit edge from block 0x24ce3a0 to 0x24bde68
DEBUG:disassembler:Adding explicit edge from block 0x24bd9b8 to 0x24caf58
DEBUG:disassembler:Adding implicit edge from block 0x24bddc8 to 0x24bda30
DEBUG:disassembler:Adding explicit edge from block 0x24bddc8 to 0x24ca8a0
DEBUG:disassembler:Adding explicit edge from block 0x24bd7d8 to 0x24bdad0
DEBUG:disassembler:Adding explicit edge from block 0x24caf58 to 0x24bddc8
INFO:deobfuscator:Control flow analysis completed.
INFO:deobfuscator:Starting simplication of basic blocks...
DEBUG:simplifier:Eliminating forwarders...
INFO:simplifier:Adding implicit edge from block 0x24bddc8 to 0x24bdb98
DEBUG:simplifier:Forwarder basic block 0x24bda30 eliminated
INFO:simplifier:Adding explicit edge from block 0x24bddc8 to 0x24bdc10
DEBUG:simplifier:Forwarder basic block 0x24ca8a0 eliminated
INFO:simplifier:2 basic blocks eliminated
DEBUG:simplifier:Merging basic blocks...
INFO:simplifier:Adding explicit edge from block 0x24bdcb0 to 0x24bdf58
DEBUG:simplifier:Basic block 0x24ca7b0 merged with block 0x24bdcb0
DEBUG:simplifier:Basic block 0x24bde68 merged with block 0x24ce3a0
INFO:simplifier:Adding explicit edge from block 0x24bdc10 to 0x24bdf58
DEBUG:simplifier:Basic block 0x24bdcb0 merged with block 0x24bdc10
INFO:simplifier:Adding explicit edge from block 0x24bd7d8 to 0x24ce3a0
DEBUG:simplifier:Basic block 0x24bdad0 merged with block 0x24bd7d8
DEBUG:simplifier:Basic block 0x24bdb48 merged with block 0x24bdb98
INFO:simplifier:Adding explicit edge from block 0x24bdc10 to 0x24bd7d8
DEBUG:simplifier:Basic block 0x24bdf58 merged with block 0x24bdc10
DEBUG:simplifier:Basic block 0x24ce3a0 merged with block 0x24bd7d8
INFO:simplifier:Adding explicit edge from block 0x24ca670 to 0x24caf58
DEBUG:simplifier:Basic block 0x24bd9b8 merged with block 0x24ca670
INFO:simplifier:Adding implicit edge from block 0x24caf58 to 0x24bdb98
INFO:simplifier:Adding explicit edge from block 0x24caf58 to 0x24bdc10
DEBUG:simplifier:Basic block 0x24bddc8 merged with block 0x24caf58
DEBUG:simplifier:Basic block 0x24bd7d8 merged with block 0x24bdc10
INFO:simplifier:Adding implicit edge from block 0x24ca670 to 0x24bdb98
INFO:simplifier:Adding explicit edge from block 0x24ca670 to 0x24bdc10
DEBUG:simplifier:Basic block 0x24caf58 merged with block 0x24ca670
INFO:simplifier:11 basic blocks merged.
INFO:deobfuscator:Simplication of basic blocks completed.
INFO:deobfuscator:Beginning verification of simplified basic block graph...
INFO:deobfuscator:Verification succeeded.
INFO:deobfuscator:Assembling basic blocks...
DEBUG:assembler:Performing a DFS on the graph to generate the layout of the blocks.
DEBUG:assembler:Morphing some JUMP_ABSOLUTE instructions to make file decompilable.
DEBUG:assembler:Verifying generated layout...
DEBUG:assembler:Successfully verified layout.
DEBUG:assembler:Calculating addresses of basic blocks.
DEBUG:assembler:Calculating instruction operands.
DEBUG:assembler:Generating code...
INFO:deobfuscator:Successfully assembled. 
INFO:__main__:Successfully deobfuscated code object recur_factorial
INFO:__main__:Collecting constants for code object recur_factorial
INFO:__main__:Generating new code object for recur_factorial
INFO:__main__:Generating new code object for \x0f\x1d\n\x00\x07\x0f\x0f
INFO:__main__:Writing deobfuscated code object to disk
INFO:__main__:Success

Running this we get back the deobfuscated code in the file wrapper_deobf.pyc. We can now run a python decompiler on this to get back our deobfuscated code as shown in Figure 4.

Figure 4: Decompiling the deobfuscated code
Figure 4: Decompiling the deobfuscated code

Monday, 10 July 2017

Introducing bytecode simplifier

Bytecode simplifier is a tool to deobfuscate PjOrion protected python scripts. It is a complete rewrite of my earlier tool PjOrion Deobfuscator. I have reimplemented the deobfuscation functionality from scratch and have used networkx specifically for this purpose. Using networkx made reasoning about the code much simpler.
The PjOrion version used is 1.3.2 (Filename: PjOrion_Uncompyle6_01.10.2016.zip)

The code is at https://github.com/extremecoders-re/bytecode_simplifier

A short tutorial can be found here: https://0xec.blogspot.com/2017/07/deobfuscating-pjorion-using-bytecode.html

Saturday, 1 April 2017

Remote debugging in IDA Pro by http tunnelling

IDA Pro provides remote debugging capability that allows us to debug a target binary residing on a different machine over the network. This feature is very useful in situations such as when we want to debug an executable for an arm device as installing IDA on it is not possible. IDA can remotely debug another binary in two ways - through a gdbserver or by the provided debugger servers (located in dbgsrv directory).

These debugging servers transport the debugger commands, messages and relevant data over a TCP/IP network through BSD sockets. So far so good, but what if the debugging server resided on a virtual host hosting multiple domain names? We cannot use sockets anymore.

A socket connection between two endpoints is characterized by a pair of socket addresses, one for each node. The socket address, in turn, comprises of the IP address and a port number. For an incoming socket connection, a server hosting multiple domains on the same IP address cannot decide which domain to actually forward the request based on socket address alone. Thus remote debugging using sockets is not possible. However, this is not entirely true as there are techniques such as port forwarding (aka virtual server) that can be used to reroute the incoming traffic to various private IPs based on a pre-decided table. Port forwarding capability is not available everywhere so we can ignore it for now. Instead, it would be much better if sockets supported connections based on domain names as described in this paper Name-based Virtual Hosting in TCP.

The Application Layer Protocol HTTP solves the virtual host problem by including the Host header in HTTP messages. It seems that if we can wrap the transport layer socket traffic in plain old HTTP messages our problem would be solved. The rest of the blog post describes this process in detail.

The problem

A few days ago, I was trying some CTF challenge involving an arm binary. The binary was loaded in IDA within a Windows XP VM. Debugging the binary would require a Linux box at the minimum with qemu-arm installed. Rather than powering up my ubuntu VM, I decided to debug it remotely on cloud9. Cloud9 is a sort of VPS that provide Docker Ubuntu containers called as workspaces where we can run whatever we want. The arm binary can be debugged using qemu as follows:

$ qemu-arm-static -g 8081 ./challenge

We are using the user mode emulation capability of qemu to run non-native elf binaries. The port on which qemu listens for incoming gdb connections is specified by the -g flag and is 8081 in this case. We have specified port 8081 as it is one of the few ports cloud9 allows incoming connections. Now, if we try to attach to the process in IDA using remote gdb debugger as the debugger type configured as shown in Figure 1, IDA fails.
Remote debugger configuration
Figure 1: Remote debugger configuration (ignore the paths)
This is expected as the container on which the debuggee is running is on a virtual host where multiple containers have same IP addresses with different domain names. A socket connects by IP addresses and not by domain names thus it is not possible to connect to our container using sockets. We can get a clearer picture using netcat.

Let us create a netcat server listening on port 8081 as shown in Figure 2.
Netcat server listening on port 8081
Figure 2: Netcat server listening on port 8081
We can try to connect to this server from our Windows XP VM as shown in Figure 3.

Trying to connect to our netcat server
Figure 3: Trying to connect to our netcat server

Unsurprisingly, this fails too for the same reason.

The workaround

We have seen that socket connection is be made using IP addresses. However, if we connect using HTTP we can use domain names. This is possible because of the Host header as mentioned earlier, Let's test this concept.

We create a netcat server listening on port 8081 which replies with a HTTP "HELLO WORLD" message. This is done as shown in Figure 4

Netcat server replying with http message
Figure 4: Netcat server replying with HTTP message
For the client part in the Windows XP box, we use curl instead of netcat as shown in Figure 5. We choose curl over netcat as we are performing an HTTP transaction and not a socket connection.

Figure 5: Using curl to connect to the netcat server

The connection succeeds and we get the HELLO WORLD response. The netcat server running on cloud9 also displays the success status as in Figure 6.
Netcat server replied to the request
Figure 6: Netcat server replied to the request
From the above experiments, it is clear that we must use HTTP in order to establish a connection to the remote container running on a virtual host. Similarly, if we intend to debug remotely an app using IDA we must also use HTTP instead of sockets.

Using HTTP Tunnelling

We have seen that connection using HTTP is only possible. If we want to use sockets, it must be wrapped in HTTP. This technique of encapsulating one protocol over HTTP is called HTTP tunnelling. Wikipedia explains this best. Primarily, HTTP tunnels are used to bypass restrictive network environments like firewalls where only connections to well-known ports are permitted. We can reuse the tunnelling technique for debugging in IDA as well.

A Http tunnel application consists of two parts - server and client both communicating over HTTP. Before using a Http tunnel the situation was like Figure 7.

Socket connection
Figure 7: Socket connection

After using Http tunnel, the situation would look like Figure 8.

Http tunnelling
Figure 8: Http tunnelling
The debugger and tunnel client reside on the same machine though they are depicted as separate computers. Similarly, the tunnel client and the debuggee reside on the same cloud9 container. The tunnel client-server pair encapsulates the socket in an Http connection. Using this mechanism we can remotely debug using IDA.

Searching for a Http tunnelling application, I came across Chisel. It is open-source and written in Go. Compiling this from source is simple:

$ git clone https://github.com/jpillora/chisel.git
$ cd chisel
$ go build -o chisel # for compiling native linux binaries
$ GOOS=windows GOARCH=386 go build -o chisel.exe # cross compiling for windows x86

Remote configuration


We run the chisel server on cloud9 listening on port 8081 on all network interfaces:

$ ./chisel server --port=8081
2017/03/31 19:57:03 server: Fingerprint 07:4e:00:e4:82:9b:76:3a:3a:70:55:30:2e:1d:c2:82
2017/03/31 19:57:03 server: Listening on 8081...

qemu runs with the gdbserver listening on port 23946 for incoming gdb connections from IDA.

$ qemu-arm-static -g 23946 ./challenge

The connection between chisel server and qemu is through sockets. The debugger traffic wrapped in Http will be passed to chisel server at port 8081, chisel will extract the payload of the message and pass it to qemu at port 23946 over a socket.

Local configuration

In our Windows XP box we run chisel in client mode with the following command line:

C:\>chisel client qemu-extremecoders-re.c9users.io:8081 1234:23946
2017/04/01 01:28:36 client: Connecting to ws://qemu-extremecoders-re.c9users.io:8081
2017/04/01 01:28:38 client: Fingerprint 07:4e:00:e4:82:9b:76:3a:3a:70:55:30:2e:1d:c2:82
2017/04/01 01:28:39 client: Connected (Latency 203.125ms)

The remote Url on which the chisel server listens (qemu-extremecoders-re.c9users.io:8081) is specified along with the port.

The second set of port (1234:23946) separated by a colon specifies the port mapping from local to remote. It means incoming traffic to chisel client at local port 1234 will be forwarded to the chisel server which will, in turn, relay the traffic over a socket to port 23946 where qemu is listening.

Finally, we need to configure IDA to use the local chisel client as the remote host. This is done as per Figure 9.
 IDA remote debugger configuration
Figure 9: IDA remote debugger configuration
The hostname is specified as 127.0.0.1 and the port as 1234. This is the address where the chisel client is accepting socket connections.

At this point, if we try to attach to the remote process, it succeeds with the following message as in Figure 10.
Attach successful
Figure 10: Attach successful

Mission accomplished!

Final words

Http tunnelling is a very effective technique in scenarios where only Http connections are allowed or possible. In this case of remote debugging, we used http tunnelling since normal socket connections cannot be established. With this we come to the end of this post. Hope you find this useful. Ciao!