Showing posts with label Reverse. Show all posts
Showing posts with label Reverse. Show all posts

Thursday, October 22, 2015

FireEye Second Flare-On Reversing Challenges

My solutions for Flare-On reversing challenges is here

FireEye published some statistics in there blog
- Official Solutions and Winner Statistics
- Announcing the Second FLARE On Challenge

And this is what I got as prize from FLARE team

Wednesday, September 23, 2015

CSAW CTF - RE500 - wyvern

We got a 64-bit ELF for this challenge. Running strings shows the use of Obfuscator-LLVM
Obfuscator-LLVM clang version 3.6.1 (tags/RELEASE_361/final) (based on Obfuscator-LLVM 3.6.1)
The binary expects a valid key!
$ ./wyvern_c85f1be480808a9da350faaa6104a19b 
+-----------------------+
|    Welcome Hero       |
+-----------------------+

[!] Quest: there is a dragon prowling the domain.
 brute strength and magic is our only hope. Test your skill.

Enter the dragon's secret:
For further analysis, I used PIN based tracer. First objective was to find the length. Supplying an input of 100 bytes, the CMP instruction was found using PIN
0x4046b6 : cmp rax, rcx
0x4046b6 : [0x64] [0x1c]
The length of key is 28 bytes. Now lets see if we could find the algorithm by tracking user input using PIN tool. Supplying input as BAAAAAAAAAAAAAAAAAAAAAAAAAAA, below instructions were found:
0x4017e8 : add ecx, dword ptr [rax]
0x4017e8 : [0x42] [0] := [0x42]      -> input B
--
0x402a7f : cmp eax, ecx
0x402a7f : [0x64] [0x42]      -> compared with 0x64
Now supplying input as dBAAAAAAAAAAAAAAAAAAAAAAAAAA
0x4017e8 : add ecx, dword ptr [rax]
0x4017e8 : [0x42] [0x64] := [0xa5]   -> input A and 0x64 from previous operation
--
0x402a7f : cmp eax, ecx       
0x402a7f : [0xd6] [0xa5]
So the algorithm reads each byte of user input, compares with a hard coded array, which is a sum input and previous result
0x00 + input[0] == 0x64
0x64 + input[1] == 0xd6
.....
Lets fetch the array using GDB by setting breakpoint at 0x402a7f, and compute the flag
import gdb

sum_array = [0]
def exit_handler(event):
    key = ''
    for i in range(len(sum_array)-1):
        key += chr(sum_array[i+1] - sum_array[i]) 
    print key
    
def callback_fetch_array():
    EAX = int(gdb.parse_and_eval("$eax"))
    sum_array.append(EAX)
    gdb.execute("set $ecx = $eax")

class HitBreakpoint(gdb.Breakpoint):
    def __init__(self, loc, callback):
        super(HitBreakpoint, self).__init__(
            loc, gdb.BP_BREAKPOINT, internal=False)
        self.callback = callback
        
    def stop(self):
        self.callback()
        return False

HitBreakpoint("*0x402a7f", callback_fetch_array) 
gdb.events.exited.connect(exit_handler)
$ gdb -q ./wyvern_c85f1be480808a9da350faaa6104a19b

gdb-peda$ source re500.py
Breakpoint 1 at 0x402a7f

gdb-peda$ run
Enter the dragon's secret: AAAAAAAAAAAAAAAAAAAAAAAAAAAA

[+] A great success! Here is a flag{AAAAAAAAAAAAAAAAAAAAAAAAAAAA}
[Inferior 1 (process 33655) exited normally]
dr4g0n_or_p4tric1an_it5_LLVM
So the key is dr4g0n_or_p4tric1an_it5_LLVM

CSAW CTF - RE300 - FTP

The challenge binary is a 64 bit executable. The objective was to login as user blankwall by finding the password. The supplied password is processed using a hash function at 0x00401540. Below is the decompiled code of the function:
uint32_t calc_hash(char *input)
{
  int counter; 
  uint32_t state; 

  state = 5381;
  for (counter = 0; input[counter]; ++counter)
    state = 33 * state + input[counter];
  return state;
}
The output of this function must be equal to 0x0D386D209. The length of the password is not known and state variable is 32-bit, hence modulo 0xffffffff. I used Z3 to solve this and multiple solutions are possible. Below is the solver:
#!/usr/bin/env python

from z3 import * 

# >>> (0x1505 * 0x21 * 0x21 * 0x21) < 0xD386D209
# True
# minimum length of password
passlen = 4

def init_solver(passlen):
    s = Solver()
    vectors = {}
    for c in range(passlen):
        vec = BitVec(str(c), 8)
        vectors[c] = vec
        s.add(And(0x20 < vec, vec < 0x7F))
    return s, vectors

while True:
    s, vectors = init_solver(passlen)
    state = 0x1505 
    for i in range(passlen):
        vec = ZeroExt(24, vectors[i])
        state = state * 0x21 + vec
    s.add(state == 0xD386D209)

    if s.check() == sat: break
    passlen += 1

m = s.model()
c = lambda m, i: chr(m[vectors[i]].as_long())
print ''.join([c(m,i) for i in range(passlen)])
This gives the password UIcy1y. Login using the password as
con.write('USER blankwall' + chr(0xa))
password = 'PASS UIcy1y\x00' + chr(0xa)
con.write(password)
The file re_solution had the flag, flag{n0_c0ok1e_ju$t_a_f1ag_f0r_you}

CSAW CTF - RE200 - Hacking Time

For this challenge we got a NES ROM to reverse engineer.
HackingTime_03e852ace386388eb88c39a02f88c773.nes: iNES ROM dump, 2x16k PRG, 1x8k CHR, [Vert.]
I used FCEUX emulator to run the ROM file. The game asks for a password after few messages



The ROM memory could be viewed using Hex viewer, provided as part of FCEUX debug tools. I supplied a password ABCDABCDABCDABCDABCDABCD and found this is memory



Then set read memory access breakpoint for address 0x5 i.e. first byte of string. Now on continuing the game, the breakpoint is hit



The program counter is at 0x82F7, so we know what part of code to analyze. Then I found the Nintendo Entertainment System (NES) ROM loader module for IDA Pro, for analyzing the challenge ROM.
ROM:82F1 algorithm:                              ; CODE XREF: main_function+1C7
ROM:82F1                 LDY     #0
ROM:82F3                 LDA     #0
ROM:82F5                 STA     VAL
ROM:82F7
ROM:82F7 process_loop:                           ; CODE XREF: algorithm+44
ROM:82F7                 LDA     5,Y             ; LDA INPUTKEY,A
ROM:82FA                 TAX                     ; X = A
ROM:82FB                 ROL     A               ; ROL A,1
ROM:82FC                 TXA                     ; A = X
ROM:82FD                 ROL     A               ; ROL A, 1
ROM:82FE                 TAX                     ; X = A
ROM:82FF                 ROL     A               ; ROL A, 1
ROM:8300                 TXA                     ; A = X
ROM:8301                 ROL     A               ; ROL A,1
ROM:8302                 TAX                     ; X = A
The PC 0x82F7, takes us to the actual validation algorithm used. The algorithm processes one byte at a time using multiple rotate operations and couple of XOR's with hardcoded arrays values. Good reference to instruction set is found here. The algorithm was rewritten in python and bruteforce gave the solution
import string

CHECKA = [0x70, 0x30, 0x53, 0xa1, 0xd3, 0x70, 0x3f, 0x64, 0xb3, 0x16,
          0xe4, 0x04, 0x5f, 0x3a, 0xee, 0x42, 0xb1, 0xa1, 0x37, 0x15,
          0x6e, 0x88, 0x2a, 0xab]
CHECKB = [0x20, 0xac, 0x7a, 0x25, 0xd7, 0x9c, 0xc2, 0x1d, 0x58, 0xd0,
          0x13, 0x25, 0x96, 0x6a, 0xdc, 0x7e, 0x2e, 0xb4, 0xb4, 0x10,
          0xcb, 0x1d, 0xc2, 0x66, 0x3b]

CF = 0
def ROR(REG): 
    global CF
    if CF: REG |= 0x100
    CF = REG & 1
    REG  >>= 1
    return REG
    
def ROL(REG):
    global CF
    REG <<= 1
    if CF: REG |= 1
    CF = 1 if (REG > 0xFF) else 0
    return REG & 0xFF

STACK = 0
ADDR_3B = 0
KEY = ''

for Y in range(24):
    for A in string.uppercase+string.digits:
        C = A
        VAL_3B = ADDR_3B
        A = ord(A)  # ROM:82F7                 LDA     $5,Y  
        X = A       # ROM:82FA                 TAX
        A = ROL(A)  # ROM:82FB                 ROL     A
        A = X       # ROM:82FC                 TXA
        A = ROL(A)  # ROM:82FD                 ROL     A
        X = A       # ROM:82FE                 TAX 
        A = ROL(A)  # ROM:82FF                 ROL     A
        A = X       # ROM:8300                 TXA   
        A = ROL(A)  # ROM:8301                 ROL     A 
        X = A       # ROM:8302                 TAX     
        A = ROL(A)  # ROM:8303                 ROL     A
        A = X       # ROM:8304                 TXA      
        A = ROL(A)  # ROM:8305                 ROL     A
        STACK = A   # ROM:8306                 PHA 
        A = VAL_3B  # ROM:8307                 LDA     ROLL 
        X = A       # ROM:8309                 TAX
        A = ROR(A)  # ROM:830A                 ROR     A 
        A = X       # ROM:830B                 TXA  
        A = ROR(A)  # ROM:830C                 ROR     A
        X = A       # ROM:830D                 TAX  
        A = ROR(A)  # ROM:830E                 ROR     A
        A = X       # ROM:830F                 TXA 
        A = ROR(A)  # ROM:8310                 ROR     A
        VAL_3B = A  # ROM:8311                 STA     ROLL
        A = STACK   # ROM:8313                 PLA
        CF = 0      # ROM:8314                 CLC 
        A = (A + VAL_3B) & 0xFF # ROM:8315     ADC     ROLL
        A = A ^ CHECKA[Y]   # ROM:8317     EOR     $955E,Y
        VAL_3B = A  # ROM:831A                 STA     ROLL
        X = A       # ROM:831C                 TAX 
        A = ROL(A)  # ROM:831D                 ROL     A
        A = X       # ROM:831E                 TXA
        A = ROL(A)  # ROM:831F                 ROL     A
        X = A       # ROM:8320                 TAX
        A = ROL(A)  # ROM:8321                 ROL     A
        A = X       # ROM:8322                 TXA
        A = ROL(A)  # ROM:8323                 ROL     A 
        X = A       # ROM:8324                 TAX
        A = ROL(A)  # ROM:8325                 ROL     A
        A = X       # ROM:8326                 TXA
        A = ROL(A)  # ROM:8327                 ROL     A
        X = A       # ROM:8328                 TAX    
        A = ROL(A)  # ROM:8329                 ROL     A 
        A = X       # ROM:832A                 TXA      
        A = ROL(A)  # ROM:832B                 ROL     A
        A = A ^ CHECKB[Y]   # ROM:832C         EOR     $9576,Y
        if A == 0:
            KEY += C
            ADDR_3B = VAL_3B
            break
        else: 
            VAL_3B = 0
print KEY
The flag is NOHACK4UXWRATHOFKFUHRERX

Monday, July 13, 2015

PoliCTF RE350 - JOHN THE PACKER - PIN + Z3

The 32bit ELF is a self modyfing code. So I decided to use PIN for futher analysis.
$ pin -t obj-ia32/exectrace.so -- ./re350 flag{ABCDEFGHIJKLMNOPQRTSUVWXYZ0}

0x80488f8 : cmp eax, 0x21
0x80488f8 : [0x21] [0x21]
$ ltrace -i ./re350 flag{ABCDEFGHIJKLMNOPQRTSUVWXYZ0}
[0x80488f5] strlen("flag{ABCDEFGHIJKLMNOPQRTSUVWXYZ0"...) = 33
Length of flag is 33 bytes
Once the length is know, lets search for other interesting stuffs from the PIN trace
0x8048a84 : cmp ebx, eax
0x8048a84 : [0x41] [0x70]  -> A is compared with p
$ pin -t obj-ia32/exectrace.so -- ./re350 flag{pBCDEFGHIJKLMNOPQRTSUVWXYZ0}

0x8048a96 : cmp eax, dword ptr [ebp-0x10]
0x8048a96 : [0x1] [0x6]
--
0x8048a84 : cmp ebx, eax
0x8048a84 : [0x70] [0x70]  -> p is a valid comparison 
--
0x8048a96 : cmp eax, dword ptr [ebp-0x10]
0x8048a96 : [0x2] [0x6]
--
0x8048a84 : cmp ebx, eax   -> B is compared to a
0x8048a84 : [0x42] [0x61]
The first 6 unknown bytes are compared directly, which could be retrieved as 'packer'. Going further we could see this:
0x804892f : cmp ebx, eax
0x804892f : [0x16] [0x21]
--
0x8048953 : xor eax, ecx
0x8048953 : [0x50] [0x10] := [0x40]
--
0x804896f : cmp al, byte ptr [ebp-0xe]
0x804896f : [0x40] [0x51]
User supplied 'P' ^ 0x10 == User supplied 'Q'. On futher analysis, the algo looks like this
key[20] ^ 0x10 == key[21]
key[21] ^ 0x44 == key[22]
......
......
key[31] ^ 0x00 == key[32]
There are multiple inputs which satisfies these constraints. Using Z3 one can quickly find all the ascii printables satisfying the condition. Below is the solver:

#!/usr/bin/env python

from z3 import *

def get_models(s):
    # from 0vercl0k's z3tools.py
    while s.check() == sat:
        m = s.model()
        yield m
        s.add(Or([sym() != m[sym] for sym in m.decls()]))

s = Solver()
a, b, c, d, e, f, g, h, i, j, k, l = BitVecs('a b c d e f g h i j k l', 8)

# ascii printables
s.add(And(0x20 < a, a < 0x7f))
s.add(And(0x20 < b, b < 0x7f))
s.add(And(0x20 < c, c < 0x7f))
s.add(And(0x20 < d, d < 0x7f))
s.add(And(0x20 < e, e < 0x7f))
s.add(And(0x20 < f, f < 0x7f))
s.add(And(0x20 < g, g < 0x7f))
s.add(And(0x20 < h, h < 0x7f))
s.add(And(0x20 < i, i < 0x7f))
s.add(And(0x20 < j, j < 0x7f))
s.add(And(0x20 < k, k < 0x7f))
s.add(And(0x20 < l, l < 0x7f))

# from PIN trace
s.add(a ^ 0x10 == b)
s.add(b ^ 0x44 == c)
s.add(c ^ 0x07 == d)
s.add(d ^ 0x43 == e)
s.add(e ^ 0x59 == f)
s.add(f ^ 0x1c == g)
s.add(g ^ 0x5b == h)
s.add(h ^ 0x1e == i)
s.add(i ^ 0x19 == j)
s.add(j ^ 0x47 == k)
s.add(k ^ 0x00 == l)

for m in get_models(s):
    serial = [m[a].as_long(), m[b].as_long(), m[c].as_long(), m[d].as_long(),
       m[e].as_long(), m[f].as_long(), m[g].as_long(), m[h].as_long(),
       m[i].as_long(), m[j].as_long(), m[k].as_long(), m[l].as_long()]
    
    key = ''
    for _ in serial: key += chr(_)
    print key

# probable solution =-in-th3-4ss
The most matching characters looked like '=-in-th3-4ss'. Thats 12 bytes of flag. Now we have flag{packerXXXXXXXXX=-in-th3-4ss}. After this, there wasn't much details in the trace file [I didn't trace floating point operations]. We need to find how the middle part of flag is validated

The ltrace had few calls to pow() function. Lets see, if there is anything related to this. Self modyfing code might cause issue with breakpoints.
2681 [0x80487b3] pow(0, 0x40180000, 0, 0x40080000)                                      = 1
2681 [0x80487e0] pow(0, 0x40180000, 0, 0x40000000)                                      = 1
2681 [0x8048843] pow(0, 0x40000000, 0, 0x40504000)                                      = 1
gdb-peda$ break *0x8048843
Breakpoint 1 at 0x8048843

gdb-peda$ run flag{packerAAAAAAAAA=-in-th3-4ss}

gdb-peda$ generate-core-file
Now, lets analyze the core. Function at 0x08048AA5 has some 7 checks. With little debugging one can find the 5th check is the one that validates the missing parts of flag

.text:08048B67 push    eax
.text:08048B68 push    26h
.text:08048B6D push    offset check_five
.text:08048B72 call    call_mprotect
.text:08048B77 add     esp, 10h
.text:08048B7A add     [ebp+check_count], eax
.text:08048B7D mov     eax, [ebp+arg]  
check_five validates key[11] - key[21]. It futher calls a function 0x08048813 to perform some floating point operations. The return value of the floating point operation is compared to validate the flag. Note that the comparison is done sequentially. So for each valid byte, more code is executed. One can use PIN to count instructions and check if a byte is valid or not.

.text:080489F9                 push    offset floating_point
.text:080489FE                 call    call_mprotect
.text:08048A03                 add     esp, 20h
.text:08048A06                 test    eax, eax
.text:08048A08                 jnz     short success   ; flag byte passes first check
.text:08048A0A                 mov     eax, 0          ; failure - invalid flag byte
.text:08048A0F                 jmp     short ret
.text:08048A11 ; ---------------------------------------------------------------------------
.text:08048A11
.text:08048A11 success:                                ; CODE XREF: check_five+5Fj
.text:08048A11                 mov     eax, [ebp+flag]
.text:08048A14                 add     eax, 17
.text:08048A17                 movzx   eax, byte ptr [eax]
.text:08048A1A                 movsx   eax, al
.text:08048A1D                 and     eax, 1
.text:08048A20                 test    eax, eax
.text:08048A22                 jnz     short inc ; flag byte [17] passes second check
.text:08048A24                 mov     eax, 0
.text:08048A29                 jmp     short ret
.text:08048A2B ; ---------------------------------------------------------------------------
.text:08048A2B
.text:08048A2B inc:                                    ; CODE XREF: check_five+79j
.text:08048A2B                 add     [ebp+counter], 1
.text:08048A2F
.text:08048A2F loop:                                   ; CODE XREF: check_five+25j
.text:08048A2F                 cmp     [ebp+counter], 0Ah
.text:08048A33                 jle     short loop_body
.text:08048A35                 mov     eax, 1
.text:08048A3A
.text:08048A3A ret:                                    ; CODE XREF: check_five+66j
.text:08048A3A                                         ; check_five+80j
Using these information retrieve other bytes. Make sure to count only instructions from main executable.
#!/usr/bin/env python

import subprocess

start = "flag{packer"
end   = "=-in-th3-4ss}"

for c in range(33, 127):
    trial = start + chr(c) + "A"*8 + end
    msg = subprocess.check_output(['pin', '-t', 'obj-ia32/count.so', '--' , './re350', trial])
    count = msg.strip()[-3:]
    print chr(c), count
$ python counter.py

* 774
+ 774
, 774
- 825
. 774
/ 774
0 774
1 774
2 774
We choose '-' as valid input. The binary accepts multiple solutions. One may have to manually pick up chars to get a meaningful key. If count is same for all possible chars at a particular index, it could be that all chars are valid. Finally, flag for the challenge is
$ ./re350 flag{packer-15-4-?41=-in-th3-4ss}
You got the flag: flag{packer-15-4-?41=-in-th3-4ss}
I arrived at this solution few minutes after the CTF ended :D

PoliCTF RE200 - REVERSEMEPLZ

This is a 32 bit binary which validates a key. Removing the dead codes, this is what the algorithm looks like:
    for (size_t i = 0; i < 0xF; i++) {
        if (key[i] < 'a') key[i] = transform(key[1] & 1);
        if (key[i] > 'z') key[i] = transform(key[1] & 2);
        dec[i] = transform(key[i]);
        if (dec[i] != 0xCF && dec[i] > 0xCC) flag = true; 
    }    

    if (flag) return 0;

    for (size_t i = 1; i < 0xF; i++) {
        if (dec[i] - dec[i-1] != diff_array[i]) return 0;
    }

    return transform(key[0]) == 'b';
So the key length is 15 bytes. The first byte of key is transformed to 'b'. The function at 0x08048519 takes single byte as input and gives a single byte output. Considering all ascii small letters as input, one can create transformation table. If that doesn't work, assume key[1] value and build table for ascii printables under transform(0), transform(1) or transform(2)

Below is the solution:

int8_t flag[16] = {0};
int8_t table[256][1] = {0};

int main(int argc, char **argv)
{
    for (size_t c = 97; c <= 122; c++)
        table[transform(c)][0] = c;

    flag[0] = table['b'][0];

    for (size_t i = 0; i < 14; i++)
        flag[i+1] = table[table[flag[i]][0] + diff_array[i]][0];

    printf("%s\n", flag);
    return 0;
}
Flag for the challenge is flag{onetwotheflagyo}. Full source is found here

Wednesday, April 29, 2015

CONFidence DS CTF Teaser 2015 - So Easy - Reverse Engineering

We were given a 32 bit ELF which validates the flag. main function reads user input, converts lower case to upper case, upper case to lower case, then compares with string dRGNs{tHISwASsOsIMPLE}. But this is not the flag, there is something else happening. Lets check the .init_array

gdb-peda$ maintenance info sections

 [16]     0x8048d2c->0x8049120 at 0x00000d2c: .eh_frame ALLOC LOAD READONLY DATA HAS_CONTENTS
 [17]     0x804af04->0x804af0c at 0x00001f04: .init_array ALLOC LOAD DATA HAS_CONTENTS
 [18]     0x804af0c->0x804af10 at 0x00001f0c: .fini_array ALLOC LOAD DATA HAS_CONTENTS
 [19]     0x804af10->0x804af14 at 0x00001f10: .jcr ALLOC LOAD DATA HAS_CONTENTS
 [20]     0x804af14->0x804affc at 0x00001f14: .dynamic ALLOC LOAD DATA HAS_CONTENTS

gdb-peda$ x/4x 0x804af04
0x804af04: 0x080488be 0x08048570 0x08048550 0x00000000

0x080488be is priority constructor, which does some operation before main is executed. Below are the operations of the constructor

[*] Overwrite GOT entry of printf with address of function calling strlen
[*] Call calloc(32, 4)
[*] Then registers a series of destructors using ___cxa_atexit
[*] First destructor registered is the key validation routine, followed by a series of destructor to write a single DWORD to the calloc memory
.text:080488DE                 mov     [ebp+plt_printf], 8048412h
.text:080488E5                 mov     eax, [ebp+plt_printf]
.text:080488E8                 mov     eax, [eax]
.text:080488EA                 mov     [ebp+addr_printf], eax
.text:080488ED                 mov     eax, [ebp+addr_printf]
.text:080488F0                 mov     dword ptr [eax], offset strlen_w ; overwrite with strlen
.text:080488F6                 mov     dword ptr [esp+4], 4 ; size
.text:080488FE                 mov     dword ptr [esp], 32 ; nmemb
.text:08048905                 call    _calloc
.text:0804890A                 mov     ds:calloc_address, eax
.text:0804890F                 mov     dword ptr [esp], offset check_key
.text:08048916                 call    register_destructor
.text:0804891B                 mov     dword ptr [esp], offset a
.text:08048922                 call    register_destructor
.text:08048927                 mov     dword ptr [esp], offset b
Then the program goes into main, reads and modifies user input as mentioned earlier. Later, __run_exit_handlers executes the destructors in the reverse order. Finally, check_key validates the key.
gdb-peda$ break *0x000000000804873C
Breakpoint 1 at 0x804873c

gdb-peda$ heap used
Used chunks of memory on heap
-----------------------------
     0: 0x0804c008 -> 0x0804c08f      136 bytes uncategorized::136 bytes |64 00 00 00 52 00 00 00 47 00 00 00 4e 00 00 00 73 00 00 00 7b 00 00 00 6e 00 00 00 4f 00 00 00 |d...R...G...N...s...{...n...O...|
User supplied flag is is case converted and compared with the bytes found above. So flag for the challenge is DrgnS{NotEvenWarmedUp}

Monday, July 7, 2014

Pwnium CTF 2014 - Breakpoints - RE 300 - [Team SegFault]

We were given a 64-bit stripped ELF for this challenge. We need to generate a valid password for this executable.
[root@renorobert Pwnium]# ./re300
Password : A
:(
Analyzing the file in IDA, there was only 2 functions. Main function at 0x040661A and CheckPassword function at 0x040063A. The Main function had nothing interesting except a ptrace call for anti-debugging.

This is what CheckPassword looked like:
The CheckPassword function was large, so we decided to trace the interesting instructions in it, as the program executes. Idea was to supply arbitrary input and trace the execution.
[root@renorobert MyPinTool]# pin -t trace.so -- ./re300 
Password : Z
:(
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400de2: cmp eax, 0xe81c0d24
0x400ded: cmp eax, 0xe81c0d24
0x400e69: cmp eax, 0xf03ad87a
0x400e74: cmp eax, 0xf03ad87a
0x400eb8: cmp eax, 0xf487628c
0x400ec3: cmp eax, 0xf487628c
0x400eda: cmp eax, 0xf6fbb4e5
0x400ee5: cmp eax, 0xfe129837
0x40580e: cmp al, 0x44
0x40582d: cmp al, 0x64
0x40584c: cmp al, 0x31
0x40586b: cmp al, 0x34
0x40588a: cmp al, 0x2a
0x4058a9: cmp al, 0xb
0x4058c8: cmp al, 0x3d
0x4058e7: cmp al, 0x66
0x405906: cmp al, 0x63
0x405925: cmp al, 0x46
0x405944: cmp al, 0x36
0x405963: cmp al, 0x69
0x405982: cmp al, 0x6d
0x40599a: add qword ptr [rbp-0x8], 0x1
0x400586: sub rax, 0x606b20
0x40058c: cmp rax, 0xe
The user supplied value is compared against 0x44, 0x64 ,.., 0x6D. But there was no other computations performed. Looks like only comparisons are made with supplied password.
Lets try another run by supplying 0x44(D) as input.
[root@renorobert MyPinTool]# pin -t trace.so -- ./re300 
Password : D
:(
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400de2: cmp eax, 0xe81c0d24
0x400ded: cmp eax, 0xe81c0d24
0x400e69: cmp eax, 0xf03ad87a
0x400e74: cmp eax, 0xf03ad87a
0x400eb8: cmp eax, 0xf487628c
0x400ec3: cmp eax, 0xf487628c
0x400eda: cmp eax, 0xf6fbb4e5
0x400ee5: cmp eax, 0xfe129837
0x40580e: cmp al, 0x44
0x40581c: add qword ptr [rbp-0x8], 0x1
0x400586: sub rax, 0x606b20
0x40058c: cmp rax, 0xe
When the comparison succeeds, rest of the checks are skipped. That looked promising. Lets pass 2 bytes input as 'DZ'
[root@renorobert MyPinTool]# pin -t trace.so -- ./re300 
Password : DZ
:(
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400de2: cmp eax, 0xe81c0d24
0x400ded: cmp eax, 0xe81c0d24
0x400e69: cmp eax, 0xf03ad87a
0x400e74: cmp eax, 0xf03ad87a
0x400eb8: cmp eax, 0xf487628c
0x400ec3: cmp eax, 0xf487628c
0x400eda: cmp eax, 0xf6fbb4e5
0x400ee5: cmp eax, 0xfe129837
0x40580e: cmp al, 0x44
0x40581c: add qword ptr [rbp-0x8], 0x1
0x40064d: cmp eax, 0x712a6ee0
0x400658: cmp eax, 0x712a6ee0
0x400aac: cmp eax, 0xb2fc91ab
0x400ab7: cmp eax, 0xb2fc91ab
0x400cd0: cmp eax, 0xdd01eab3
0x400cdb: cmp eax, 0xdd01eab3
0x400ce6: cmp eax, 0xcfcddef9
0x400cf1: cmp eax, 0xcfcddef9
0x400d6d: cmp eax, 0xd81f6c49
0x400d78: cmp eax, 0xd81f6c49
0x400d7f: cmp eax, 0xd28dcea2
0x400d8a: cmp eax, 0xd4f27687
0x400d95: cmp eax, 0xd2283d5c
0x401c5c: cmp al, 0x33
0x401c7b: cmp al, 0x23
0x401c9a: cmp al, 0x36
0x400586: sub rax, 0x606b20
0x40058c: cmp rax, 0xe
The first char is checked against 0x44 which succeeds. The second char is checked against 0x33(3), 0x23 and 0x36. So the next valid char is '3'. Thus by dumping the first checks of each block of comparison we got D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD
[root@renorobert Pwnium]# ./re300
Password : D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD
:)
So flag for the challenge is D3bugG1nG_Th1s_ObfuSc4t3d_C0d3_1s_R34lly_H4rD

Tuesday, February 25, 2014

Codegate CTF Quals 2014 - dodoCrackme - Reverse 200 - [Team SegFault]

The binary starts with mmap() syscall to allocate a memory of 0x7530 bytes.
.text:00000000004000E0 exit:                                  
.text:00000000004000E0                 mov     eax, 3Ch
.text:00000000004000E5                 mov     edi, 0
.text:00000000004000EA                 syscall
.text:00000000004000EC                 mov     r9d, 0
.text:00000000004000F2                 mov     r8, 0FFFFFFFFFFFFFFFFh
.text:00000000004000F9                 mov     r10d, 22h
.text:00000000004000FF                 mov     edx, 3
.text:0000000000400104                 mov     esi, 7530h
.text:0000000000400109                 mov     edi, 0
.text:000000000040010E                 mov     eax, 9
.text:0000000000400113                 syscall                 ; mmap
.text:0000000000400115                 cmp     rax, 0
.text:0000000000400119                 jle     short exit
.text:000000000040011B                 mov     rbp, rax
.text:000000000040011E                 add     rbp, 3A98h
The allocated memory is then used as stack for performing memory operations. There is lot of lea, inc, dec instructions throughout the binary to compute values. Below is the syscall to write() to print chr(r) and chr(o)
.text:0000000000400276                 lea     rbp, [rbp+8]
.text:000000000040027A                 dec     byte ptr [rbp+0]
.text:000000000040027D                 dec     byte ptr [rbp+0]
.text:0000000000400280                 dec     byte ptr [rbp+0]
.text:0000000000400283                 mov     eax, 1
.text:0000000000400288                 mov     edi, 1
.text:000000000040028D                 mov     rsi, rbp
.text:0000000000400290                 mov     edx, 1
.text:0000000000400295                 syscall                 ; r
.text:0000000000400297                 dec     byte ptr [rbp+0]
.text:000000000040029A                 dec     byte ptr [rbp+0]
.text:000000000040029D                 dec     byte ptr [rbp+0]
.text:00000000004002A0                 mov     eax, 1
.text:00000000004002A5                 mov     edi, 1
.text:00000000004002AA                 mov     rsi, rbp
.text:00000000004002AD                 mov     edx, 1
.text:00000000004002B2                 syscall                 ; o
What I can see is that, value pointed by RBP is being operated upon to create needed characters. Few more system calls prints root@localhost's password: .
[ctf@renorobert codegate14]$ ./crackme_d079a0af0b01789c01d5755c885da4f6 
root@localhost's password: asdfghjkl
Permission denied (password).
Supplying a wrong key prints Permission denied (password). and a maximum of 32 characters are read. Then I decided to break at read() syscall to inspect memory. The idea was to check if all the operations and memory referred by RBP, left the key or any interesting string in the mmap'ed area.
gdb-peda$ break *0x00000000004065AA
gdb-peda$ x/600x $rbp
0x7ffff7ff9b30: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b38: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b40: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b48: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b50: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b58: 0x48 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b60: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b68: 0x34 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b70: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b78: 0x50 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b80: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b88: 0x50 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7ffff7ff9b90: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
All these bytes yields the flag H4PPY_C0DEGaTE_2014_CU_1N_K0RE4

Tuesday, June 11, 2013

Boston Key Party CTF 2013 - Reverse 100 - Randy - [Team xbios]

Randy is a ELF 64-bit LSB executable. We have to figure out the valid key
[ctf@renorobert BKPCTF]$ ./randy 
Password: qwerty
:(
Analysing the binary, we notice the following

[*] Key is 28 characters long
[*] Each 4 bytes from key is used as seed argument for srandom() function ie. we have 7 blocks of 4 characters each
[*] Then random() function is called 4 times, on each block of 4 characters and generated numbers are compared to validate the key
[*] So the idea is to figure out the seed value used for srandom() function

We can bruteforce the seed value to figure out the key. Here is the C code to do this
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {

    unsigned int seed;
    long int rand;
    unsigned int de;
    unsigned int ad;
    unsigned int be;
    unsigned int ef;
 
    for(de = 0x20; de <= 0x7e; de++) {
        for(ad = 0x20; ad <= 0x7e; ad++) {
            for(be = 0x20; be <= 0x7e; be++) {
                for(ef = 0x20; ef <= 0x7e; ef++) {
                    seed = (de << 24 | ad << 16 | be << 8 | ef << 0 );
                    srandom(seed);
                    rand = random();
                    if(rand == 0x7358837a)
                        printf("Possible seed for 00-03 bytes: 0x%x\n", seed);
                    else if(rand == 0x34d8c3b5)
                        printf("Possible seed for 04-07 bytes: 0x%x\n", seed);
                    else if(rand == 0x1f49456c)
                        printf("Possible seed for 08-11 bytes: 0x%x\n", seed);
                    else if(rand == 0x1fea6614)
                        printf("Possible seed for 12-15 bytes: 0x%x\n", seed);
                    else if(rand == 0x4e81abc7)
                        printf("Possible seed for 16-19 bytes: 0x%x\n", seed);
                    else if(rand == 0x683d3f5d)
                        printf("Possible seed for 20-23 bytes: 0x%x\n", seed);
                    else if(rand == 0x28c9a8fe)
                        printf("Possible seed for 24-27 bytes: 0x%x\n", seed);
                }
            }
        }
    }
}
Running the code, we got the seeds in sometime
[ctf@renorobert BKPCTF]$ ./brute_randy 
Possible seed for 00-03 bytes: 0x2074306e
Possible seed for 24-27 bytes: 0x21212121
Possible seed for 08-11 bytes: 0x30646e34
Possible seed for 20-23 bytes: 0x31316120
Possible seed for 12-15 bytes: 0x3420306d
Possible seed for 04-07 bytes: 0x72203073
Possible seed for 16-19 bytes: 0x72337466
Now converting this to text gives us the valid key
>>> from struct import pack
>>> pack("<I",0x2074306e)+pack("<I",0x72203073)+pack("<I",0x30646e34)+pack("<I",0x3420306d)+pack("<I",0x72337466)+pack("<I",0x31316120)+pack("<I",0x21212121)
'n0t s0 r4nd0m0 4ft3r a11!!!!'
[ctf@renorobert BKPCTF]$ ./randy 
Password: n0t s0 r4nd0m0 4ft3r a11!!!!
:)
Flag for the challenge is n0t s0 r4nd0m0 4ft3r a11!!!!

Sunday, June 2, 2013

EBCTF Teaser 2013 - Bin 100 - [Team xbios]

For this challenge we were give a PE32 executable. Its a dice game, we have to throw some correct sequence of numbers to get the flag.
[ctf@renorobert EBCTF]$ file ebCTF-Teaser-BIN100-Dice.exe
ebCTF-Teaser-BIN100-Dice.exe: PE32 executable for MS Windows (console) Intel 80386 32-bit

[ctf@renorobert EBCTF]$ wine ebCTF-Teaser-BIN100-Dice.exe

[*] ebCTF 2013 Teaser - BIN100 - Dice Game
    To get the flag you will need to throw the correct numbers.

[*] You will first need to throw a three, press enter to throw a dice!

 -------
| O   O |
|       |
| O   O |
 -------

[*] You rolled a 4 That is not a three :/
[*] Game over!
Analysing the binary with IDA, we noticed the following

[*] We have to throw the sequence 3 - 1 - 3 - 3 - 7 to get flag
[*] Binary randomly generates numbers between 1 to 6 and does the comparison

We simply patched the essential JNZ instructions to JZ instruction, such that binary will stop execution only when right numbers are thrown. Here is the IDA dif file we used for patching
ebCTF-Teaser-BIN100-Dice.exe
00000D2B: 75 74
00000F92: 75 74
00001069: 75 74
000013A5: 75 74
0000163D: 75 74
000016D6: 85 84
00001A29: 75 74
00001A50: 85 84
Now execute the binary, we got the flag in the first run
[*] You rolled a seven, with a six sided dice! How awesome are you?!

[*] You rolled 3-1-3-3-7, what does that make you? ELEET! \o/
[*] Nice job, here is the flag: ebCTF{64ec47ece868ba34a425d90044cd2dec}
Flag for the challenge is ebCTF{64ec47ece868ba34a425d90044cd2dec}

Friday, May 31, 2013

Defeating anti-debugging techniques using IDA and x86 emulator plugin

Recently I was trying to reverse a small crackme by Tosh. The challenge was to find a password by reversing the binary, bypassing the anti-debugging techniques used. Below is some data about the binary.
[ctf@renorobert chall]$ file ch13
ch13: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped
[ctf@renorobert chall]$ ./ch13
Enter the password: reno
Bad password, sorry !
[ctf@renorobert chall]$ strace -i ./ch13
[      38b7aa6dd7] execve("./ch13", ["./ch13"], [/* 48 vars */]) = 0
[ Process PID=5359 runs in 32 bit mode. ]
[         8048074] signal(SIGTRAP, 0x80480e2) = 0 (SIG_DFL)
[         8048078] --- SIGTRAP (Trace/breakpoint trap) @ 0 (0) ---
[        f77f3408] sigreturn()          = 0
[         804808c] signal(SIGTRAP, 0x8048194) = 0 (SIG_DFL)
[         8048090] --- SIGTRAP (Trace/breakpoint trap) @ 0 (0) ---
[        f77f3408] sigreturn()          = 0
[         80480a4] signal(SIGTRAP, 0x8048104) = 0 (SIG_DFL)
[         80480a8] --- SIGTRAP (Trace/breakpoint trap) @ 0 (0) ---
[         80481d4] write(1, "Enter the password: ", 20Enter the password: ) = 20
[         80481dc] read(0, reno
"reno\n", 128) = 5
[        f77f3408] sigreturn()          = 0
[         80480bc] signal(SIGTRAP, 0x8048138) = 0 (SIG_DFL)
[         80480c0] --- SIGTRAP (Trace/breakpoint trap) @ 0 (0) ---
[        f77f3408] sigreturn()          = 0
[         80480d4] signal(SIGTRAP, 0x8048149) = 0 (SIG_DFL)
[         80480d8] --- SIGTRAP (Trace/breakpoint trap) @ 0 (0) ---
[         80481d4] write(1, "Bad password, sorry !\n", 22Bad password, sorry !
) = 22
[         80481e4] _exit(1)             = ?
Here is the disassembly in IDA
.text:08048060 ; Segment type: Pure code
.text:08048060 ; Segment permissions: Read/Execute
.text:08048060 _text        segment para public 'CODE' use32
.text:08048060         assume cs:_text
.text:08048060         ;org 8048060h
.text:08048060         assume es:nothing, ss:nothing, ds:_text, fs:nothing, gs:nothing
.text:08048060
.text:08048060 ; =============== S U B R O U T I N E =======================================
.text:08048060
.text:08048060 ; Attributes: noreturn
.text:08048060
.text:08048060         public start
.text:08048060 start        proc near
.text:08048060         jmp     short loc_8048063
.text:08048060 ; ---------------------------------------------------------------------------
.text:08048062         db 0E8h
.text:08048063 ; ---------------------------------------------------------------------------
.text:08048063
.text:08048063 loc_8048063:          
.text:08048063         mov     eax, 30h
.text:08048068         mov     ebx, 5
.text:0804806D         mov     ecx, offset sub_80480E2
.text:08048072         int     80h        ; LINUX - sys_signal
.text:08048074         jmp     short loc_8048077
.text:08048074 ; ---------------------------------------------------------------------------
.text:08048076         db 0CEh
.text:08048077 ; ---------------------------------------------------------------------------
.text:08048077
.text:08048077 loc_8048077:         
.text:08048077         int     3        ; Trap to Debugger
.text:08048078         jmp     short loc_804807B
.text:08048078 ; ---------------------------------------------------------------------------
.text:0804807A         db 0EBh
.text:0804807B ; ---------------------------------------------------------------------------
.text:0804807B
.text:0804807B loc_804807B:         
.text:0804807B         mov     eax, 30h
.text:08048080         mov     ebx, 5        ; signo
.text:08048085         mov     ecx, offset dword_8048194
.text:0804808A         int     80h        ; LINUX - sys_signal
.text:0804808C         jmp     short loc_804808F
.text:0804808C ; ---------------------------------------------------------------------------
.text:0804808E         db 0E8h
.text:0804808F ; ---------------------------------------------------------------------------
.text:0804808F
.text:0804808F loc_804808F:          
.text:0804808F         int     3        ; Trap to Debugger
.text:08048090         jmp     short loc_8048093
.text:08048090 ; ---------------------------------------------------------------------------
.text:08048092         db 0CEh
.text:08048093 ; ---------------------------------------------------------------------------
.text:08048093
.text:08048093 loc_8048093:         
.text:08048093         mov     eax, 30h
.text:08048098         mov     ebx, 5        ; signo
.text:0804809D         mov     ecx, offset dword_8048104
.text:080480A2         int     80h        ; LINUX - sys_signal
.text:080480A4         jmp     short loc_80480A7
.text:080480A4 ; ---------------------------------------------------------------------------
.text:080480A6         db 0EBh
.text:080480A7 ; ---------------------------------------------------------------------------
.text:080480A7
.text:080480A7 loc_80480A7:          
.text:080480A7         int     3        ; Trap to Debugger
.text:080480A8         jmp     short loc_80480AB
.text:080480A8 ; ---------------------------------------------------------------------------
.text:080480AA         db 0E8h
.text:080480AB ; ---------------------------------------------------------------------------
.text:080480AB
.text:080480AB loc_80480AB:          
.text:080480AB         mov     eax, 30h
.text:080480B0         mov     ebx, 5        ; signo
.text:080480B5         mov     ecx, offset dword_8048138
.text:080480BA         int     80h        ; LINUX - sys_signal
.text:080480BC         jmp     short loc_80480BF
.text:080480BC ; ---------------------------------------------------------------------------
.text:080480BE         db 0CEh
.text:080480BF ; ---------------------------------------------------------------------------
.text:080480BF
.text:080480BF loc_80480BF:          
.text:080480BF         int     3        ; Trap to Debugger
.text:080480C0         jmp     short loc_80480C3
.text:080480C0 ; ---------------------------------------------------------------------------
.text:080480C2         db 0EBh
.text:080480C3 ; ---------------------------------------------------------------------------
.text:080480C3
.text:080480C3 loc_80480C3:          
.text:080480C3         mov     eax, 30h
.text:080480C8         mov     ebx, 5        ; signo
.text:080480CD         mov     ecx, offset byte_8048149
.text:080480D2         int     80h        ; LINUX - sys_signal
.text:080480D4         jmp     short loc_80480D7
.text:080480D4 ; ---------------------------------------------------------------------------
.text:080480D6         db 0E8h
.text:080480D7 ; ---------------------------------------------------------------------------
.text:080480D7
.text:080480D7 loc_80480D7:          
.text:080480D7         int     3        ; Trap to Debugger
.text:080480D8         jmp     short loc_80480DB
.text:080480D8 ; ---------------------------------------------------------------------------
.text:080480DA         db 0E8h
.text:080480DB ; ---------------------------------------------------------------------------
.text:080480DB
.text:080480DB loc_80480DB:          
.text:080480DB         mov     eax, 1
.text:080480E0         int     80h        ; LINUX - sys_exit
.text:080480E0 start        endp
.text:080480E0
.text:080480E2
.text:080480E2 ; =============== S U B R O U T I N E =======================================
.text:080480E2
.text:080480E2
.text:080480E2 sub_80480E2     proc near        
.text:080480E2         mov     eax, offset dword_8048104
.text:080480E7         jmp     short loc_8048101
.text:080480E9 ; ---------------------------------------------------------------------------
.text:080480E9
.text:080480E9 loc_80480E9:         
.text:080480E9            
.text:080480E9         cmp     eax, 80482E8h
.text:080480EE         jz      short locret_8048103
.text:080480F0         jmp     short loc_80480F3
.text:080480F0 ; ---------------------------------------------------------------------------
.text:080480F2         db 0E8h
.text:080480F3 ; ---------------------------------------------------------------------------
.text:080480F3
.text:080480F3 loc_80480F3:          
.text:080480F3         xor     dword ptr [eax], 8048FC1h
.text:080480F9         add     eax, 4
.text:080480FC         jmp     short loc_80480FF
.text:080480FC ; ---------------------------------------------------------------------------
.text:080480FE         db 0EBh
.text:080480FF ; ---------------------------------------------------------------------------
.text:080480FF
.text:080480FF loc_80480FF:          
.text:080480FF         jmp     short loc_80480E9
.text:08048101 ; ---------------------------------------------------------------------------
.text:08048101
.text:08048101 loc_8048101:          
.text:08048101         jmp     short loc_80480E9
.text:08048103 ; ---------------------------------------------------------------------------
.text:08048103
.text:08048103 locret_8048103:          
.text:08048103         retn
.text:08048103 sub_80480E2     endp
.text:08048103
.text:08048103 ; ---------------------------------------------------------------------------
.text:08048104 dword_8048104   dd 8048E7Ah, 8A3936C1h, 1CBE87C5h, 0E0048FC1h, 8048F74h
.text:08048104         dd 8048F7Ah, 8A5536C1h, 88BE87C5h, 0E0048FC1h, 8048F68h
.text:08048104         dd 0C86DE7Ah, 43C78EC9h, 0CB048C07h
.text:08048138 dword_8048138   dd 0C86DE79h, 83C0FC9h, 388489B5h, 0FDEFCF3Dh
.text:08048148         db 2
.text:08048149 byte_8048149    db 37h, 55h, 8Ah        
.text:0804814C         dd 0D9BF87C5h, 820C8B43h, 7D0FB5C9h, 8FD0FC8h, 4B4492B5h
.text:0804814C         dd 9BF7E2Ah, 0B1048FC1h, 0DC6h, 804997Bh, 85367C1h, 6AEC8FC1h
.text:0804814C         dd 0B3048FC1h, 8048FC0h, 0C856B78h, 82735C9h, 36EC8FC1h
.text:0804814C         dd 0E0048FC1h, 8048F88h
.text:08048194 dword_8048194   dd 68BF4FF0h, 890C8B41h, 0C866B3Ah, 3B03FBC9h, 0CC70CC2h
.text:08048194         dd 0ECBF7E2Ah, 330C8B43h, 0B31DFBC2h, 8048FC0h, 0C869278h
.text:08048194         dd 82435C9h, 0EEC8FC1h, 0E0048FC1h, 8048FD0h, 8003702h
.text:08048194         dd 88C98FC1h, 8073702h, 88C98FC1h, 8053702h, 88C98FC1h
.text:08048194         dd 7C65FD86h, 7C24A3BBh, 2877E6A9h, 7C24FCA8h, 6F24EAA9h
.text:08048194         dd 2860E0AEh, 7B77EEB1h, 6C76E0B6h, 4A0EAEE1h, 7824EBA0h
.text:08048194         dd 7F77FCA0h, 2460FDAEh, 7A6BFCE1h, 2924F6B3h, 4151DECBh
.text:08048194         dd 2825AF95h, 6B61F784h, 6A65FBB4h, 6124EAADh, 6769AFB2h
.text:08048194         dd 6162E6A5h, 2924EBA4h, 7C6ACACBh, 7C24FDA4h, 7824EAA9h
.text:08048194         dd 7F77FCA0h, 3260FDAEh, 8048FE1h, 1Fh dup(8048FC1h), 95CB2AC1h
.text:08048194         dd 0BC8C5275h, 0A7912054h, 9FCB3B49h, 8D90A78h, 765EE0CBh
.text:08048194 _text        ends
.text:08048194
.text:08048194
.text:08048194         end start
From 0x08048104, the program appears as data instead of code. This part of program is protected against static analysis. First, lets figure out the de-obfuscation algorithm becuase it looks more like a self-modifying binary.

This is what the binary does to call the de-obfuscation routine:
.text:08048063         mov     eax, 30h         ; signal
.text:08048068         mov     ebx, 5   ; SIGTRAP 
.text:0804806D         mov     ecx, 0x080480E2  ; handler - de-obfuscation routine
.text:08048072         int     80h         ; LINUX - sys_signal
.text:08048077         int     3
Analysing the instructions at 0x080480E2, we can find the de-obfuscation routine:
mov eax, 0x08048104
;loop
    cmp eax, 0x080482e8
    if equal:
        ret
    else:
        xor [eax], 0x08048fc1
        add eax, 4
Bytes between 0x08048104 and 0x080482e4 is xor'ed with 0x08048fc1 to generate new sequence of instructions. Below is the permissions on the binary, 0x08048000-0x08049000 has read, write and execute permissions
(gdb) info target
Symbols from "/Windows/E/Desktop_Recent/Recent/chall/ch13".
Unix child process:
 Using the running image of child process 19533.
 While running this, GDB does not access memory from...
Local exec file:
 `/Windows/E/Desktop_Recent/Recent/chall/ch13', file type elf32-i386.
 Entry point: 0x8048060
 0x08048060 - 0x080482e8 is .text
(gdb) shell cat /proc/19533/maps 
08048000-08049000 rwxp 00000000 08:05 190663                             /Windows/E/Desktop_Recent/Recent/chall/ch13
f7ffd000-f7ffe000 r-xp 00000000 00:00 0                                  [vdso]
fffe9000-ffffe000 rwxp 00000000 00:00 0                                  [stack]
Now we can convert the bytes to valid instructions since we know de-obfuscation routine. Interestingly I came across x86 Emulator plugin for IDA . Using this I was able to step through the code and convert the bytes to valid assembly instructions


This is how the bytes look after the de-obfuscation process ie after running under x86 emulator plugin
.text:08048104 dword_8048104   dd 1BBh, 823DB900h, 14BA0804h, 0E8000000h, 0B5h, 0BBh
.text:08048104         dd 8251B900h, 80BA0804h, 0E8000000h, 0A9h, 48251BBh, 4BC30108h
.text:08048104         dd 0C30003C6h
.text:08048138 dword_8048138   dd 48251B8h, 388008h, 30800674h, 0F5EB40FCh
.text:08048148         db 0C3h
.text:08048149 byte_8048149    db 0B8h, 51h, 82h       
.text:0804814C         dd 0D1BB0804h, 8A080482h, 750B3A08h, 0F98009h, 43401D74h
.text:0804814C         dd 1BBF1EBh, 0B9000000h, 8048207h, 16BAh, 57E800h, 62E80000h
.text:0804814C         dd 0BB000000h, 1, 481E4B9h, 23BA08h, 3EE80000h, 0E8000000h
.text:0804814C         dd 49h
.text:08048194 dword_8048194   dd 60BBC031h, 81080480h, 482E4FBh, 33077408h, 4C38303h
.text:08048194         dd 0E4BBF1EBh, 3B080482h, 0BB197403h, 1, 4821DB9h, 20BA08h
.text:08048194         dd 6E80000h, 0E8000000h, 11h, 4B8C3h, 80CD0000h, 3B8C3h
.text:08048194         dd 80CD0000h, 1B8C3h, 80CD0000h, 74617247h, 74202C7Ah
.text:08048194         dd 20736968h, 74207369h, 67206568h, 20646F6Fh, 73736170h
.text:08048194         dd 64726F77h, 420A2120h, 70206461h, 77737361h, 2C64726Fh
.text:08048194         dd 726F7320h, 21207972h, 4955510Ah, 20212054h, 63657845h
.text:08048194         dd 62617475h, 6920656Ch, 6F6D2073h, 69666964h, 21206465h
.text:08048194         dd 746E450Ah, 74207265h, 70206568h, 77737361h, 3A64726Fh
.text:08048194         dd 20h, 1Fh dup(0), 9DCFA500h, 0B488DDB4h, 0AF95AF95h
.text:08048194         dd 97CFB488h, 0DD85B9h, 7E5A6F0Ah
.text:080482E8         align 1000h
.text:080482E8 _text        ends
Convert these bytes to instructions using IDA's Analyse selected area option.


.text:08048104 loc_8048104:          
.text:08048104            
.text:08048104         mov     ebx, 1
.text:08048109         mov     ecx, offset aEnterThePasswo ; "Enter the password: "
.text:0804810E         mov     edx, 14h
.text:08048113         call    sub_80481CD
.text:08048118         mov     ebx, 0
.text:0804811D         mov     ecx, 8048251h
.text:08048122         mov     edx, 80h
.text:08048127         call    sub_80481D5
.text:0804812C         mov     ebx, 8048251h
.text:08048131         add     ebx, eax
.text:08048133         dec     ebx
.text:08048134         mov     byte ptr [ebx], 0
.text:08048137         retn
.text:08048138 ; ---------------------------------------------------------------------------
.text:08048138
.text:08048138 loc_8048138:          
.text:08048138         mov     eax, 8048251h
.text:0804813D
.text:0804813D loc_804813D:          
.text:0804813D         cmp     byte ptr [eax], 0
.text:08048140         jz      short locret_8048148
.text:08048142         xor     byte ptr [eax], 0FCh
.text:08048145         inc     eax
.text:08048146         jmp     short loc_804813D
.text:08048148 ; ---------------------------------------------------------------------------
.text:08048148
.text:08048148 locret_8048148:          
.text:08048148         retn
.text:08048149 ; ---------------------------------------------------------------------------
.text:08048149
.text:08048149 loc_8048149:          
.text:08048149         mov     eax, 8048251h
.text:0804814E         mov     ebx, offset aEAIXpxpiCE
.text:08048153
.text:08048153 loc_8048153:          
.text:08048153         mov     cl, [eax]
.text:08048155         cmp     cl, [ebx]
.text:08048157         jnz     short loc_8048162
.text:08048159         cmp     cl, 0
.text:0804815C         jz      short loc_804817B
.text:0804815E         inc     eax
.text:0804815F         inc     ebx
.text:08048160         jmp     short loc_8048153
.text:08048162 ; ---------------------------------------------------------------------------
.text:08048162
.text:08048162 loc_8048162:          
.text:08048162         mov     ebx, 1
.text:08048167         mov     ecx, (offset loc_8048206+1)
.text:0804816C         mov     edx, 16h
.text:08048171         call    sub_80481CD
.text:08048176         call    sub_80481DD
.text:0804817B ; ---------------------------------------------------------------------------
.text:0804817B
.text:0804817B loc_804817B:          
.text:0804817B         mov     ebx, 1
.text:08048180         mov     ecx, offset loc_80481E4
.text:08048185         mov     edx, 23h
.text:0804818A         call    sub_80481CD
.text:0804818F         call    sub_80481DD
.text:08048194 ; ---------------------------------------------------------------------------
.text:08048194
.text:08048194 loc_8048194:          
.text:08048194         xor     eax, eax
.text:08048196         mov     ebx, offset start
.text:0804819B
.text:0804819B loc_804819B:          
.text:0804819B         cmp     ebx, offset loc_80482E4
.text:080481A1         jz      short loc_80481AA
.text:080481A3         xor     eax, [ebx]
.text:080481A5         add     ebx, 4
.text:080481A8         jmp     short loc_804819B
.text:080481AA ; ---------------------------------------------------------------------------
.text:080481AA
.text:080481AA loc_80481AA:          
.text:080481AA         mov     ebx, offset loc_80482E4
.text:080481AF         cmp     eax, [ebx]
.text:080481B1         jz      short locret_80481CC
.text:080481B3         mov     ebx, 1
.text:080481B8         mov     ecx, offset loc_804821D
.text:080481BD         mov     edx, 20h
.text:080481C2         call    sub_80481CD
.text:080481C7         call    sub_80481DD
.text:080481CC ; ---------------------------------------------------------------------------
.text:080481CC
.text:080481CC locret_80481CC:          
.text:080481CC         retn
.text:080481CD
.text:080481CD ; =============== S U B R O U T I N E =======================================
.text:080481CD
.text:080481CD
.text:080481CD sub_80481CD     proc near        
.text:080481CD            
.text:080481CD         mov     eax, 4
.text:080481D2         int     80h        ; LINUX - sys_write
.text:080481D4         retn
.text:080481D4 sub_80481CD     endp
.text:080481D4
.text:080481D5
.text:080481D5 ; =============== S U B R O U T I N E =======================================
.text:080481D5
.text:080481D5
.text:080481D5 sub_80481D5     proc near        
.text:080481D5         mov     eax, 3
.text:080481DA         int     80h        ; LINUX - sys_read
.text:080481DC         retn
.text:080481DC sub_80481D5     endp
.text:080481DC
.text:080481DD
.text:080481DD ; =============== S U B R O U T I N E =======================================
.text:080481DD
.text:080481DD ; Attributes: noreturn
.text:080481DD
.text:080481DD sub_80481DD     proc near       
.text:080481DD            
.text:080481DD         mov     eax, 1
.text:080481E2         int     80h        ; LINUX - sys_exit
.text:080481E2 sub_80481DD     endp
After analysing the instructions, the address range from 0x080481e4 to 0x0804823d looks more like data. Undefine this range and convert it to string using IDA


.text:080481E4 aGratzThisIsThe db 'Gratz, this is the good password !',0Ah
.text:080481E4                 db 'Bad password, sorry !',0Ah
.text:080481E4                 db 'QUIT ! Executable is modified !',0Ah
.text:080481E4                 db 'Enter the password: '
Hardcoded bytes at 0x080482d1
.text:080482D1 unk_80482D1     db 0A5h 
.text:080482D2                 db 0CFh 
.text:080482D3                 db  9Dh 
.text:080482D4                 db 0B4h 
.text:080482D5                 db 0DDh 
.text:080482D6                 db  88h
.text:080482D7                 db 0B4h 
.text:080482D8                 db  95h
.text:080482D9                 db 0AFh
.text:080482DA                 db  95h 
.text:080482DB                 db 0AFh
.text:080482DC                 db  88h 
.text:080482DD                 db 0B4h
.text:080482DE                 db 0CFh
.text:080482DF                 db  97h
.text:080482E0                 db 0B9h
.text:080482E1                 db  85h
.text:080482E2                 db 0DDh 
.text:080482E3                 db    0
Below is the function of binary:
mov eax, 0x30
mov ebx, 0x5
mov ecx, 0x080480e2
int 0x80

; 0x080480e2   ; De-obfuscation routine
mov eax, 0x08048104
;loop
    cmp eax, 0x080482e8
    if equal:
        ret
    else:
        xor [eax], 0x08048fc1
        add eax, 4

mov eax, 0x30
mov ebx, 0x5
mov ecx, 0x08048194
int 0x80

; 0x08048194   ; Verification routine
xor eax, eax
mov ebx, 0x08048060
; loop
    cmp ebx, 0x080482e4
    if equal:        
        mov ebx, 0x080482e4
        cmp eax, [ebx]
        if equal:
            ret
        else:
            mov ebx, 1 
            mov ecx, 0x0804821d
            mov edx, 0x20
            call write  ; QUIT ! Executable is modified !
            call exit
    xor eax, [ebx]
    add ebx,4


mov eax, 0x30
mov ebx, 0x5
mov ecx, 0x08048104
int 0x80

; 0x08048104   ; Accept password routine
mov ebx, 1
mov ecx, "Enter the password: "
mov edx, 0x14
call write

mov ebx, 0
mov ecx, 0x08048251
mov edx, 0x80
call read
mov ebx, 0x08048251
add ebx, eax
dec ebx
mov [ebx], 0x0 ; NUL terminate
ret

mov eax, 0x30
mov ebx, 0x5
mov ecx, 0x08048138
int 0x80

; 0x08048138
mov eax, 0x08048251
; loop
    cmp byte ptr [eax], 0
    if equal:
        ret
    xor byte ptr [eax], 0x0fc
    inc eax

mov eax, 0x30   ; Verify password routine
mov ebx, 0x5
mov ecx, 0x08048149
int 0x80

; 0x08048149
mov eax, 0x08048251  ; User input after xor
mov ebx, 0x080482d1  ; Hard coded bytes
; loop
    mov cl, [eax]
    cmp cl, [ebx]
    jnz 0x08048162  ; Bad password, sorry !
    cmp cl, 0
    jz 0x0804817b  ; Gratz, this is the good password !
    inc eax
    inc ebx
I wrote a simple python script to find the valid password.
#!/usr/bin/env python

bytes = [0xa5, 0xcf, 0x9d, 0xb4, 0xdd, 0x88, 0xb4, 0x95, 0xaf, 0x95, 0xaf, 0x88, 0xb4, 0xcf, 0x97, 0xb9, 0x85, 0xdd]
key = 0xfc
password = ''

for i in bytes:
    password += chr(i ^ key)
print password
[ctf@renorobert chall]$ python ch13.py 
Y3aH!tHiSiStH3kEy!
[ctf@renorobert chall]$ ./ch13
Enter the password: Y3aH!tHiSiStH3kEy!
Gratz, this is the good password !
Using GDB
To analyse the binary under gdb pass the SIGTRAP to the process
[ctf@renorobert chall]$ gdb -q ./ch13
Reading symbols from /Windows/E/Desktop_Recent/Recent/chall/ch13...(no debugging symbols found)...done.
(gdb) handle SIGTRAP pass
SIGTRAP is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal        Stop Print Pass to program Description
SIGTRAP       Yes Yes Yes  Trace/breakpoint trap
Now the binary will execute normally as we run it outside debugger.

Using GDBserver
[ctf@renorobert chall]$ gdbserver --remote-debug 127.0.0.1:6666 ./ch13
Process ./ch13 created; pid = 4833
Listening on port 6666
Now use remote debugger option in IDA and attach to the process. Now after the de-obfuscation routine is called, we can inspect the memory and convert the raw bytes to instructions using IDA Analyze selected area option.


Its fun trying to analyse binaries of this kind!

Monday, March 18, 2013

ForbiddenBITS CTF 2013 - smelf 200 - [Team xbios]

For this challenge we got a 64-bit stripped executable. The binary implements a crypter, our aim is to understand the crypter and decrypt the cipher text bytes in the binary. We reversed the binary and rewrote the code in python
0x400657: mov    eax,DWORD PTR [rbp-0x14] ; eax = i
0x40065a: cdqe   
0x40065c: add    rax,QWORD PTR [rbp-0x28] ; rax = i + *argv[1]
0x400660: movzx  eax,BYTE PTR [rax]
0x400663: mov    esi,eax    ; esi = *rax ; current byte
0x400665: mov    eax,DWORD PTR [rbp-0x14]
0x400668: cdqe   
0x40066a: add    rax,0x1       
0x40066e: add    rax,QWORD PTR [rbp-0x28] ; rax = (i+1) + *argv[1]
0x400672: movzx  eax,BYTE PTR [rax]   ; eax = *rax ; fetch the next byte
0x400675: mov    edx,eax    
0x400677: sar    dl,0x7     ; dl = dl/2^7 
0x40067a: shr    dl,0x6     ; dl = dl/2^6 ; this will 0 out register
0x40067d: add    eax,edx     
0x40067f: and    eax,0x3    ; eax = eax & 0x3
0x400682: sub    al,dl       
0x400684: movsx  eax,al     
0x400687: shl    eax,0x3    ; eax = eax * 2^3
0x40068a: mov    edx,DWORD PTR [rbp-0x2c]   
0x40068d: mov    edi,edx    ; edi = 0x12345678
0x40068f: mov    ecx,eax    
0x400691: shr    edi,cl     ; edi = edi/ 2 ^ cl
0x400693: mov    eax,edi    
0x400695: xor    eax,esi    ; eax = eax ^ esi
#!/usr/bin/env python
# crypt.py

import sys

# we should decrypt this string
crypt = [0x4c, 0x10, 0x49, 0x00, 0x24, 0x09, 0x49, 0x36, 0x09, 0x05, 0x1e, 0x26, 0x25, 0x4b, 0x00, 0x74, 0x65, 0x41, 0x00, 0x1e, 0x2a, 0x4b, 0x00, 0x1e, 0x2a, 0x4b, 0x4c, 0x48]

string = sys.argv[1]
key = 0x12345678
cipher = []

for i in range(len(string)-1):
    val = (ord(string[i+1]) & 0x3) * (2**3)
    edi = key / (2 ** val)
    eax = (edi ^ ord(string[i]) ) & 0xff
    cipher.append(hex(eax))

print cipher
We couldn't figure out an exact way to reverse the cipher text though. The algorithm uses (i)th and (i+1)th byte of plain text to calculate the (i)th cipher byte. So we generated a list of possible values for each byte position. Here is the python code:
#!/usr/bin/env python
# sol.py

# we should decrypt this string
crypt = [0x4c, 0x10, 0x49, 0x00, 0x24, 0x09, 0x49, 0x36, 0x09, 0x05, 0x1e, 0x26, 0x25, 0x4b, 0x00, 0x74, 0x65, 0x41, 0x00, 0x1e, 0x2a, 0x4b, 0x00, 0x1e, 0x2a, 0x4b, 0x4c, 0x48]

key = 0x12345678

for i in range(len(crypt)):
    print "#" * 20
    for j in range(32,127):
        pos = []
        for k in range(32,127):
            val = (k & 0x3) * (2**3)
            edi = key / (2 ** val)
            eax = (edi ^ j ) & 0xff
            if eax == crypt[i]:
                pos.append(k)
        if len(pos) > 0:
            print j,pos 
[ctf@renorobert Forbidden]# python sol.py
####################
52 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124]
94 [35, 39, 43, 47, 51, 55, 59, 63, 67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123]
120 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
####################
36 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
70 [33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125]
104 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124]
####################
49 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124]
91 [35, 39, 43, 47, 51, 55, 59, 63, 67, 71, 75, 79, 83, 87, 91, 95, 99, 103, 107, 111, 115, 119, 123]
125 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
####################
52 [34, 38, 42, 46, 50, 54, 58, 62, 66, 70, 74, 78, 82, 86, 90, 94, 98, 102, 106, 110, 114, 118, 122, 126]
86 [33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125]
120 [32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124
==========================================================================================================
Then we used tree diagram to reach the flag. May not be the best solution but here is the approach:

[*] First element can be 52, 94 or 120
[*] Element 94 doesn't have an equivalent XOR 2nd byte ie. there is no 36,70,104. So 94 can be eleminated
[*] Element 52 maps to [36,104] and 120 maps to [70]. So 2 possible 1st byte values [52,120]
[*] Element 36 and 104 in 2nd byte position doesn't have an equivalent XOR byte ie. no 49,91 or 125. This leaves us with only one option [70], which is child of [120]
[*] Thus we have found the first two bytes as [120,70]

Entending this approach we found upto 26 bytes. Byte 27 was left with two options [52, 120] and byte 28 was left with 3 options [48,124] mapping to previous [52] and [90] mapping to [120]. These two bytes can be easily bruteforced or guessed.

This is wat we got from tree diagram [120 - 70 - 49 - 52 - 54 - 95 - 49 - 36 - 95 - 49 - 102 - 52 - 55 - 51 - 52 - 102 - 51 - 57 - 52 - 102 - 56 - 51 - 52 -102 - 56 - 51] . Final 2 bytes are [ 52 - 48]

Here is the final text we got xF146_1$_1f4734f394f834f8340. So the flag for the challenge is 1f4734f394f834f8340

Tuesday, November 13, 2012

Cscamp CTF Quals - Crypto 300 [Team xbios]

Challenge: We received a crypter and a cypher, the aim is to decrypt the cypher and get the original key that is Alphanum and 11 bytes length : Crypter : in attachment Cypher : 7f e7 ff ce 0 98 15 dd 88 fb 6e Also we have found that the crc-Xmodem of the plaintext (key) = 0x8124 PS : If you think you found the right key and doesn't work (a collision), please be sure that the CRC-XMODEM value is equal to 0x8124 before sending us request

We have the crypter which is a ELF 64-bit executable and 11 byte cipher text. First we tried analysing the output giving random inputs and also looked for one to one mapping. But this attempt failed.
[ctf@renorobert Crypto]# file crypt 
crypt: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, not stripped
[ctf@renorobert Crypto]# ./crypt qwerty
your encrypted key is : ce be 56 e6 8a 77 0 0 0 0 0 
[ctf@renorobert Crypto]# cat decipher.py
#!/usr/bin/env python
import subprocess

crypter = "./crypt"
data = ''
for i in range(32,127):
    var = chr(i)
    data = var+': '+subprocess.check_output((crypter,var))
    print data

[ctf@renorobert Crypto]# python decipher.py | grep 7f
S: your encrypted key is : 7f 0 0 0 0 0 0 0 0 0 0 
T: your encrypted key is : 7f 0 0 0 0 0 0 0 0 0 0 
m: your encrypted key is : 7f 0 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Crypto]# python decipher.py | grep e7
h: your encrypted key is : e7 0 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Crypto]# python decipher.py | grep ff
1: your encrypted key is : ff 0 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Crypto]# python decipher.py | grep ce
I: your encrypted key is : ce 0 0 0 0 0 0 0 0 0 0 
q: your encrypted key is : ce 0 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Crypto]# python decipher.py | grep ': 0'
s: your encrypted key is : 0 0 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Crypto]# python decipher.py | grep 98
[ctf@renorobert Crypto]# python decipher.py | grep 15
[ctf@renorobert Crypto]# python decipher.py | grep dd
Then we decided to look more into the binary to understand what's really happening. After some analysis and reversing we wrote the python version of the crypter.
#!/usr/bin/env python
#crypt.py
import sys

arg_2 = [0x01,0xab,0xbf,0x0e,0x1b,0xc9,0xe6,0x8c,0x4d,0xe5,0x56,0x44,0x2d,0x92,0xe4,0x9e,0xdb,0x50,0xe3,0xed,0x82,0xfa,0xda,0x79,0x1f,0x6e,0xa4,0xfc,0xc5,0xbe,0x66,0xb5,0x78,0xcc,0xc2,0xb3,0xd0,0xb0,0x5c,0xe2,0x9f,0x52,0xa1,0x72,0x35,0x24,0x8e,0xa7,0x5e,0xe8,0x5b,0xc0,0xb2,0x49,0xb6,0xfe,0x09,0x06,0x04,0x20,0x1d,0xf5,0x3f,0x51,0x76,0xd7,0xff,0xf2,0xc1,0x83,0x87,0x28,0x99,0xc4,0x90,0xbd,0xbb,0xd3,0xbc,0xea,0xa2,0xe0,0x81,0xa9,0xdd,0x86,0x94,0x74,0x8f,0x5d,0xd6,0x37,0x2f,0xd2,0x88,0xe7,0x62,0x6c,0x10,0xde,0x7b,0x47,0x17,0xa0,0x97,0x26,0x9a,0xee,0xef,0xa3,0x69,0x0b,0xfd,0x8a,0xac,0x61,0x84,0xb1,0xa8,0xb9,0x7a,0xc7,0x22,0x0a,0xf9,0x68,0xba,0x64,0x32,0x89,0x13,0xb8,0xa5,0x73,0x67,0x6f,0x7d,0x3c,0x48,0x30,0xae,0x2c,0xeb,0x38,0x19,0x75,0xaf,0x41,0x12,0xc8,0x70,0x1e,0x29,0x45,0x7e,0x9d,0x43,0x7f,0x65,0x3a,0x08,0xe1,0x2a,0x60,0xcf,0x36,0x31,0x5f,0x85,0x23,0x4e,0x2b,0x18,0xfb,0x5a,0x80,0x77,0x8b,0x7c,0xa6,0x93,0x34,0x46,0xcb,0xdc,0xe9,0xf0,0x0c,0x8d,0x42,0x14,0x59,0x40,0xd9,0xd4,0x4b,0x6a,0x11,0x16,0x6b,0x0d,0x4a,0xcd,0x53,0x54,0x96,0x07,0x9c,0xf8,0x55,0x71,0xf7,0xd8,0x4f,0x3b,0xce,0x39,0xec,0xd1,0x3e,0x03,0xf1,0x25,0xd5,0x58,0x2e,0x3d,0x00,0x05,0xaa,0xb7,0x98,0xdf,0x02,0x57,0x9b,0xb4,0x95,0x33,0x91,0x21,0x27,0x15,0x4c,0x1a,0x6d,0xf6,0xf3,0xca,0xc3,0x1c,0x0f,0xc6,0xf4,0xad,0x63]

def encrypt(arg_1,arg_2):
    match = 0
    var_2 = 0
    var_3 = 0
    while 1:
        if len(arg_1) > var_3:
            for var_4 in range(256):
                edx = arg_1[var_3]
                eax = arg_2[var_4]
                if eax == edx:
                    match = 1
                    var_5 = var_4
            if match == 1:
                comp = arg_1[var_3] ^ var_5
                arg_1[var_2] = comp
                var_2 += 1
                match  = 0
                var_3 += 1
                if comp == 0:   # Effect of string length calculation
                    return arg_1
            else:
                var_3 += 1
       else:
           return arg_1

arg_1 = []
plain = sys.argv[1]
for i in plain:
    arg_1.append(ord(i))
r1 = encrypt(arg_1, arg_2)
r2 = encrypt(r1, arg_2)
for k in r2:
    sys.stdout.write(hex(k)+': ')
print ' '
We can see that crypter performs two rounds of operation over the plain text. The collision mentioned is the effect of xor operation. Whenever the xor operation returned 0, the crypter returns with no further operation as string length calculation stops with null byte. Since the cipher was only 11 bytes long, we decided to grep through the results of cipher text for a given plain text.
[ctf@renorobert Brute]# python decipher.py T | grep e7
Th: your encrypted key is : 7f e7 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Brute]# python decipher.py S | grep e7
Sh: your encrypted key is : 7f e7 0 0 0 0 0 0 0 0 0 
[ctf@renorobert Brute]# python decipher.py Th | grep ff
Th1: your encrypted key is : 7f e7 ff 0 0 0 0 0 0 0 0 
[ctf@renorobert Brute]# python decipher.py Th1 | grep ce
Th1I: your encrypted key is : 7f e7 ff ce 0 0 0 0 0 0 0 
Th1q: your encrypted key is : 7f e7 ff ce 0 0 0 0 0 0 0
[ctf@renorobert Brute]# python decipher.py Th1I | grep 'ff ce 0'
Th1Is: your encrypted key is : 7f e7 ff ce 0 0 0 0 0 0 0 
Finally we got the following possible characters [['T','m','S'],'h','1',['I','q'],'s',['m','S','T'],'h','3','K','e',['y','Q']]. This can be easily bruteforced against the CRC-XMODEM value of 0x8124. But we didn't do that, the string "Th1IsTh3Key" made more sense to us. Checking its CRC-XMODEM value, we were right. So the key is Th1IsTh3Key
>>> hex(crc16.crc16xmodem('Th1IsTh3Key'))
'0x8124'

Friday, October 19, 2012

Hack You CTF - Reverse 300 [Team xbios]

We were given a binary which was packed. "strings" revealed something like "This file is packed with the LOL executable packer http://upx.sf.net". Unpacking with UPX didn't help. But strace gave the following info
[ctf@renorobert rev300]# strace ./task3.bin 
execve("./task3.bin", ["./task3.bin"], [/* 52 vars */]) = 0
[ Process PID=5086 runs in 32 bit mode. ]
getpid()                                = 5086
gettimeofday({1350641971, 320063}, NULL) = 0
unlink("/tmp/upxDHV4WSUAIXC")           = -1 ENOENT (No such file or directory)
open("/tmp/upxDHV4WSUAIXC", O_RDWR|O_CREAT|O_EXCL, 0700) = 3
ftruncate(3, 9036)                      = 0
old_mmap(NULL, 9036, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0xfffffffff770a000
old_mmap(0xf770d000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xfffffffff770d000
munmap(0xf770a000, 9036)                = 0
close(3)                                = 0
open("/tmp/upxDHV4WSUAIXC", O_RDONLY)   = 3
getpid()                                = 5086
access("/proc/5086/fd/3", R_OK|X_OK)    = 0
unlink("/tmp/upxDHV4WSUAIXC")           = 0
The binary unpacks itself into /tmp before gettting executed. So the idea was to grab a copy of it. Using objdump we found an entry point into the packed binary. By running the binary in gdb and setting necessary breakpoints, we got the copy of unpacked binary from /tmp before the file is unlinked.
[ctf@renorobert rev300]# objdump -f task3.bin 

task3.bin:     file format elf32-i386
architecture: i386, flags 0x00000102:
EXEC_P, D_PAGED
start address 0x00402273
Once the binary was unpacked, it nicely decompiled into a readable source. Here is the part of the source that we got
void __cdecl sub_8048617(int a1, int a2)
{
  int v2; 
  int v3; 
  if ( a1 != 3 )
  {
    printf("Usage: %s <user> <key>\n", *(_DWORD *)a2);
    exit(1);
  }
  if ( sub_8049A30(*(_DWORD *)(a2 + 4), "hackyou") )
  {
    v3 = *(_DWORD *)(a2 + 4);
    v2 = *(_DWORD *)(a2 + 8);
    if ( sub_8049A00(v2) != 14 )   // length of key checked here
      sub_80485C9();
    if ( *(_BYTE *)(v2 + 4) == 45 )   // key looks like ABCD-ABCD-ABCD
    {
      if ( *(_BYTE *)(v2 + 9) == 45 )
      {
        if ( !sub_804838C(v3, v2) )   //function call to check 1st part of key
          sub_80485C9();
        if ( !sub_804844B(v3, v2 + 5) )   //function call to check 2nd part of key
          sub_80485C9();
        if ( !sub_804850A(v3, v2 + 10) )  //function call to check 3rd part of key
          sub_80485C9();
        sub_80485F0();
      }
    }
    sub_80485C9();
  }
  printf("Err, something went wrong...\n");
  exit(2);
}
signed int __cdecl sub_804838C(int a1, int a2)  //first part of key is checked with this function
{
  int v2; 
  signed int i; 
  char v5[64];
  int v6; 
  int v7; 
  int v8; 
  int v9; 
  v6 = 23;
  v7 = 13;
  v8 = 34;
  v9 = 40;
  v2 = sub_8049A00(a1);
  sub_8048DC4(a1, v2, (int)v5, 64);
  for ( i = 0; i < 4; ++i )
  {
    if ( sub_804831C(v5[*(&v6 + i)]) != *(_BYTE *)(i + a2) ) //key is checked here
      return 0;
  }
  return 1337;
}
After this, the process is straight forward. With gdb and junk input as key, we started tracing through the function calls setting up breakpoints and changing $eip's.
//Check for first part of key
   0x8048418: call   0x804831c 
   0x804841d: add    $0x4,%esp
   0x8048420: mov    0xc(%ebp),%ecx
   0x8048423: mov    -0x54(%ebp),%edx
   0x8048426: add    %edx,%ecx
   0x8048428: movsbl (%ecx),%edx
   0x804842b: cmp    %edx,%eax //key is checked here
   0x804842d: je     0x804843d
   0x8048433: mov    $0x0,%eax
   0x8048438: jmp    0x8048449
   0x804843d: jmp    0x80483f1
   0x804843f: mov    $0x539,%eax
   0x8048444: jmp    0x8048449
   0x8048449: leave  
   0x804844a: ret    

(gdb) p $eax
$4 = 107
(gdb) p $eax
$5 = 101
(gdb) p $eax
$6 = 99
(gdb) p $eax
$9 = 99
This way, we followed the three function calls and extracted the final key kecc-hack-yo0u