2

I am doing a challenge for a CTF and I have to reverse an encryption scheme to find a flag. However it would be easier to just try all inputs but they are too much.

There is an instruction that when reached means we have guessed the current letter. How can I check in a script if that breakpoint is reached before the program ends and how much times.

I have tried using the gdb python interface but i find it not very well documented. I have tried frida but i cannot hook to an address but to a function. And r2pipe cannot send text to stdin easily.

1

2 Answers 2

0

You can start gdb and initialize it with a python script using -x command line flag. This can be launched using subprocess in another python instance. This can give you a way to bruteforce.

Some code. The driver file driver.py

import subprocess
import sys
rax = sys.argv[1]
d = subprocess.Popen("gdb -q -ex 'py rax = " + str(rax) + "' -x ./gdb_attach.py ", shell=True, stdout=subprocess.PIPE).stdout.read().strip()
print "Breakpoint hit ::: ", "HIT" in d
print d

Python script evaluated in gdb gdb_attach.py with some predefined example offsets. Note that my gdb was built with python3 support.

import gdb

class MyBreakpoint(gdb.Breakpoint):
    def stop (self):
        print("HIT")
        return True

gdb.execute('file ./x')
# gdb.execute("set environment LD_PRELOAD /home/sudhakar/tools/preeny/x86_64-linux-gnu/desleep.so")
MyBreakpoint("*0x40050b")
gdb.execute("run")
gdb.execute('set $rax=0x%x' % rax)
gdb.execute("continue")
gdb.execute('quit')

The binary is a simple yes/no check.

#include <stdio.h>

int dum(){
    return 0;
    }
int main(int argc, char **argv)
{
    if(dum()) puts("Yeaa!");
    else puts("No!");
    return 0;
}

The offsets look like this.

[0x00400400]> pdf @ main
┌ (fcn) main 58
│   main (int argc, char **argv, char **envp);
│           ; var char **local_10h @ rbp-0x10
│           ; var int local_4h @ rbp-0x4
│           ; arg int argc @ rdi
│           ; arg char **argv @ rsi
│           ; DATA XREF from entry0 (0x40041d)
│           0x004004f2      55             push rbp
│           0x004004f3      4889e5         mov rbp, rsp
│           0x004004f6      4883ec10       sub rsp, 0x10
│           0x004004fa      897dfc         mov dword [local_4h], edi   ; argc
│           0x004004fd      488975f0       mov qword [local_10h], rsi  ; argv
│           0x00400501      b800000000     mov eax, 0
│           0x00400506      e8dcffffff     call sym.dum
│           0x0040050b      85c0           test eax, eax
│       ┌─< 0x0040050d      740c           je 0x40051b
│       │   0x0040050f      bfb4054000     mov edi, str.Yeaa           ; 0x4005b4 ; "Yeaa!" ; const char *s
│       │   0x00400514      e8d7feffff     call sym.imp.puts           ; int puts(const char *s)
│      ┌──< 0x00400519      eb0a           jmp 0x400525
│      ││   ; CODE XREF from main (0x40050d)
│      │└─> 0x0040051b      bfba054000     mov edi, 0x4005ba           ; const char *s
│      │    0x00400520      e8cbfeffff     call sym.imp.puts           ; int puts(const char *s)
│      │    ; CODE XREF from main (0x400519)
│      └──> 0x00400525      b800000000     mov eax, 0
│           0x0040052a      c9             leave
└           0x0040052b      c3             ret

The driver file launches a gdb instance with gdb_attach.py. Additional python variables can be passed using -ex flag. gdb_attach.py sets a breakpoint at an offset and changes some value. You can print values when a bp is hit and parse them in the main driver script. This is quite hackish but it does the job.

Here's how it looks

$ python driver.py 0
Breakpoint hit ::: True
Breakpoint 1 at 0x40050b
HIT
Breakpoint 1, 0x000000000040050b in main ()
No!
[Inferior 1 (process 3874) exited normally]
$ python driver.py 1
Breakpoint hit ::: True
Breakpoint 1 at 0x40050b
HIT
Breakpoint 1, 0x000000000040050b in main ()
Yeaa!
[Inferior 1 (process 3947) exited normally]

See how I was able to print/detect hit breakpoints and change the flow of the binary using another python script. Hope this helps.

1
  • I find gdb python api to be frankly very bad. I selected your answer because I ended up doing something similar. I tested frida vs r2pipe vs angr. (I will write something soon). Meanwhile: TLDR; frida crashed with 32bit binaries in 64bit machines (great api otherwise) and i am having probelms with angr. I ended up useing r2pipe. The only thing that is less intuitive is using stdin but it was a better experience overall.
    – letFunny
    Nov 10, 2018 at 21:26
1

Easy stupid way: patch the instruction with an invalid one, if the program crashes you know the instruction has been hit

2
  • 1
    This assumes the program itself is valid (has no invalid instruction/doesn't cause segfault/etc.)
    – user202729
    Nov 3, 2018 at 9:53
  • Besides, the program may checksum itself to see if it's modified.
    – user202729
    Nov 3, 2018 at 9:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.