Tuesday, April 12, 2011

IDAPython and CTF Task

Few month ago I read post “IDA + Python = Love” in “Hacker” journal and been a pist off, because it is translate from HexBlog and didn't told about another cool IDAPython feature – Appcall, which appear in IDA 5.6. Here is user guide.
Appcall is a mechanism used to call functions inside the debugged program from the debugger or your script as if it were a built-in function.
So you don't need a ctypes for simple operations.


Today I will show how you can use this technics in different CTF tasks with IDA 6.0 demo. 



RuCTF2009 Quals Reverse 100


Every CTF quals we have interesting reverse task with Brute Force. Let's get reverse 100 from RuCTF quals 2009.

It was pretty simple binary with checking string function.


First of all start debugger and open python command line.
  1. Initialize Appcall object - find a function by name and confront prototype.
  2. Find String pointer with IDC LocByName function and patch them by PutDataList.
  3. Initialize permutation object and start the script. 
import string
from idautils import PutDataList
from idc import LocByName
from itertools import product

brute = Appcall.proto("brute_f", "int __cdecl brute_f();")

def patchString( val ):
 PutDataList( LocByName('string'), map( lambda(i):ord(i), val))

for i in xrange(3):
    for str in product( string.letters + string.digits, repeat=i):
        patchString(str)
        if brute() == 1:
          print ''.join(str)

 In console menu you can see results:
aF nh oY p4 rV sg D5 FW Gf HH Iy Tv UG Zi 18 2k 3Z


RuCTF2009 Quals Reverse 200


 
Another Example, It's necessary to call some function with different parameters. Look at reverse 200 in RuCTF quals 2009. We have a hex string 0408151623426C12 and dll. Find a upper function in Graph mode.




If you have a Hex-Rays(or IDA 5.5) you can decompile it and get a function prototype. Here is it
int __fastcall func(char *, char *, char *);
Presence of calling convention is obligatory.






Like in last example
  1. initializing Appcall object
  2. creating input and output buffers. 
  3. calling function

test = Appcall.proto("func", " int __fastcall func(char *, char *, char *);")
bufin = Appcall.buffer( "\x04\x08\x15\x16\x23\x42\x6C\x12")
buffout1 = Appcall.buffer ( "\x00" * 15 )
buffout2 = Appcall.buffer ( "\x00" * 15 )
test( bufin, buffout1, buffout2 )
print buffout1.value.encode('hex')

Result: 69da2403d2416d3c8042625839d400 



Codegate 2011 Quals Issue 500


Since I starting interesting boot code in Windows, I use bochs for any researches. Support of bochs in IDA start from 5.4 version. IDA 5.5 can works only with bochs 2.3.7, because can't get register information from new versions. IDA 6.0 with IDA 5.5 plugins of bochs works fine with 2.4.5.

I didn't make this task in CTF time, but I have a one interesting decision method. You can read detailed write up on Leet More blog.

First of all lets make bochsrc file for issue500.bin.
A already has one. This example works fine in 2.4 and 2.3. For you purpose only modify string as you need
 
ata0-master: type=disk, mode=flat, translation=auto, path="$CODEGATE\iss500\issue500.bin", cylinders=6500, heads=255, spt=63, biosdetect=auto, model="test"

Next step is starting IDA with bochs. Unfortunately I don't have full IDA version upper then 5.5. And can't use Appcall. IDA 6.0 demo send me this screen.



So I will talk about IDA 5.5 in this section and if you have newer version PLEASE check Appcall for 16bit code. I hope it will works fine.


Starting IDA. Then i main menu choose Debugger->Run->Local Bochs Debugger.
In next window choose full path to *.bxrc file.
Next step is "Debug options->Set speciefic options"


Set full path to bochsdbg.exe, and choose Disk image.
That's all preferences which we need. Start debugger and wait until IDA create db. Then bochs told you about bad disk geometry - push continue. And then you image will ask you a password. Look at the call stack and find "debug#" segment. Put bp after "int" and push enter.

Now you are in input pass function and lower a pass checker function. At the picture you can see this simple function, which called for each simbol. DX started from 0 and after all symbols must be 0x2002.
You can write algorithm on python or C, but it cool - use debugger for brute.

Please check it in newer version of IDA and give me know.



RuCTF 2011 Quals Reverse 300


Really crazy task with brute. I will show you script for my first idea about this task. Shortly we have a binary, which want 10 symbols string which satisfy claims of sub_40181A(brute_f). This function check input string with table started from 0x403182. Indexing in dict and so on makes me crazy so I make a script which reverse function execution. It means that we get a last value - 0xD4, find it in dictionary, calculate offset and search it again.

start = "D4"
outstr = ""
import string
strOld = "23456789TJQKAhscd "
start_offset = LocByName("dict")
for i in xrange(0xa):
    offset = FindBinary( LocByName("dict"),SEARCH_DOWN, start) - start_offset
    print "VA %x RVA %x" % (offset + start_offset, offset)
    for i in xrange(0x13):
        val = (offset - ( i >> 1 )) / ( 2 * 0x13 )
        if (val * ( 0x13 * 2) + i * 2) == offset:
            print hex(i) + " '" + strOld[i] + "' " + hex(ord(strOld[i])) + " --> " + hex( val )
            start = "%x" % val
            outstr += strOld[i]
            break
print outstr

I get a string like h2 h2h2 h2. reverse it to 2h 2h2h 2h and put in program input - get a 50%.
Then if you want to check possible variants you need a construction like this. (val_4d = 0x40317E)
brute = Appcall.proto("brute_f", "int __cdecl brute_f();")
PutDataList( LocByName('string'), b)
brute()
if (Dword(LocByName("val_4d"))) == 0xd4:
    print "OK"

Thank you for attention. Have a nice CTFs with IDApython.
Read more...