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

4 comments :

  1. Hi, I would like to thank you for your awesome posts , I see that you're using a pintool named exectrace.so , I walked through the net and didn't spot it , is it your own pintool , or is it already publically available . regards

    ReplyDelete
  2. Thanks. Its a custom pintool and not publically available

    ReplyDelete
  3. Do you accept blog-post requests from visitors? If yes, could you please check CVE-2015-3636 in detail, as there is almost NO useful use-after-free (not talking about heap chunks, object based) ? If no, please let us know your email address, I have just couple questions :)

    ReplyDelete
  4. I haven't done any blog-post as request from visitors before :) if you have any specific queries, mail me at renorobert@gmail.com

    ReplyDelete