Skip to main content

CTF Challenge Reverse Engineering Delloite Greece ಠ_ಠ (day 30)

Image result for reverse engineering meme

Yesterday, Me and my classmate decided to do a group hacking study of the past CTF challenges. One of them suggests we play the Greek hacking challenge - Finals by Deloitte greece

I say "challenge accepted"

So I decided in this post I will talk about how to solve one of the RE challenges

Description:

easy peasy challenge 200pts (desc : Hint: There's a flag in that binary  Flag format: CTF{32-hex})



first, let's determine what kind of file that is given to us. Okay, its a 64 bit ELF file


The program doesn't give us any prompt or further information (that's kinda depressing tho)

let's load it in radare2 and try to list all the functions





There is only one function that caught my attention which is main and the rest of the function is just loaded from C++ library.

move to the main function:

~# s main
~# VV (enter a visual mode so we have a much nicer representation of the program flow)



let's try to make sense of the first stages of the code.

tips: to all of the reader who just starting RE, I suggest you guys don't try to understand each of the assembly lines one by one because eventually you will burn out yourself. It is better if you try to focus on the big pictures of the program by study the call function and the logic of the program

there are four functions used in this stage:

  • call sym std::__cxx11::basic_string
use to stores and manipulates sequences of char-like objects (this is basically use to initialize a variable) => not interesting

  • call sym std::basic_istream
 this will be used to get our input and stored in the function => keep eye on this

  • call sym std::__cxx11::basic_string<char, std::char_traits<char>,  std::allocator<char> >::length() 
this will be used to check the length of our string => interesting and keep eye on this

  • call sym std::vector<char, std::allocator<char> >::size() const
std::vector is a templated class that represents dynamic arrays. while the size() is just checking the length of the arrays => not interesting

after the program called all the functions and do some operation with our input it comparing the register rbx and rax and if it's wrong it will exit code

I assume that one if this register is the result operation that happens in our input. It probably has a connection with the length() function.


lets put a breakpoint at the comparison:


aha! so out input length is compared with the value 37 so all we have to do is to create a string with length of 37 to pass the first stage

I'm gonna use this string as the input, for now:

CTF{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA} (since this is the format of the flag)

now we are going to stage 2:

in this phase, we greet with the length check for our string and after that, it compares again the register rbx and rax. As you can see, register rbx is come from var_28h


let's find out what is inside this two registers:


hmmm, looks like the register is compared again with the length of the expected input and since the value is still 0 it will jump to the assembly code below


and as you can see before the jump condition it comparing the 1-byte value from (var_21h)


 if it's not the same it will go to the left statement(false) which is going to jump to the exit function whereas if we get it right it will increment the value of the previous variable var_28h by one and jump again to the beginning of stage 2.


so what is the value of var_21h that is compared to al register? let's find out by putting a breakpoint at the comparison



let's examine the var_21h memory, to list all of the local variable type "afvd"



and type "ps" to examine the memory, hmmm take a look at the first char it looks similar to our input try to continue couple of time and examine the memory of var_21h in each break point


ahhhh interesting it looks like our input is compared with the flag character one by one.

you can loop the radare2 again-again after it covers all 37 characters of the flag but that's now what hacker's right?

a better approach is to create a python script that will loop every char

luckily, radare is equipped with r2pipe library that enables us to create a python script.


ok so let me explain the code one by one

1. r = r2pipe.open(filename = 'chall',flags=["-d","-e","dbg.profile=chall.rr2"])

this is basically just telling us to create an object of r2pipe to load the binary with debugging mode and we need to pass the "-e dbg.profile=chall.rr2" since our value will be put in cstream right? so in order for radare to automatically input into stdin we have to create a rr2 file, that looks like this.


2.

r.cmd("aaa")
r.cmd("s main")

#put breakpoint at the comparison
print r.cmd("db 0x55555555536d")

 the following code is pretty explanatory is just let you analyze the function and put a breakpoint (at the comparison)

3.
for x in range(37):
        r.cmd("dc")
        print r.cmd("afvd")
        data = r.cmdj("psj @0x7fffffffdacf")
        char_flag = data['string'][0]
        flag += char_flag.encode()
        r.cmd("dr rax=\'" + char_flag.encode()+ "\'")

print flag




after that all you have to do is just to create a loop with the iteration of 37 and dump the memory at the var_21h(0x7fffffffdacf). cmdj means that I return the result of the memory in json so I have a better flexibility to get the first char.

data = r.cmdj("psj @0x7fffffffdacf") will return

{"string":"C\u00e0[UUUU\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0080QUUUU\u0000\u0000`\u00db\u00ff\u00ff\u00ff\u007f\u0000\u0000\u00e0[UUUU\u0000\u0000\u0097k\u000c\u00f7\u00ff\u007f\u0000\u0000\u0090\u00ff\u00ff\u00ff\u00ff\u00ff\u00ff\u00ffh\u00db\u00ff\u00ff\u00ff\u007f\u0000\u0000\u0090\u00ff\u00ff\u00ff\u0001\u0000\u0000\u0000eRUUUU\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0083\u00de\u0018$\u00fb\u008a\u00c9\u001e\u0080QUUUU\u0000\u0000`\u00db\u00ff\u00ff\u00ff\u007f\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0083\u00de\u00f8&\u00ae\u00df\u009cK\u0083\u00deFEI\u00ce\u009cK\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u00003W\u00de\u00f7\u00ff\u007f\u0000\u0000\u00b8R\u00dc\u00f7\u00ff\u007f\u0000\u0000\u00b6\u00d7R\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0080QUUUU\u0000\u0000`\u00db\u00ff\u00ff\u00ff\u007f\u0000\u0000\u00aaQUUUU\u0000\u0000X\u00db\u00ff\u00ff\u00ff\u007f\u0000\u0000\u001c\u0000\u0000\u0000\u0000\u0000\u0000","offset":140737488345695,"section":"[stack]","length":256,"type":"ascii"}

so we only need the string index and get the first char

r.cmd("dr rax=\'" + char_flag.encode()+ "\'")

after we got the character I assign the first char to the rax register which is our input so we simply change register to contain the right char so the script don't have to start from all over again.





Note:
you may be wondering why I have to put this piece of code

print r.cmd("afvd") it's because the var_21h memory might move somewhere when we load our script it is better to check it and change the memory inside the "psj" code:


data = r.cmdj("psj @0x7fffffffdacf")









and also remember to check the breakpoint value of this code before you run the program:

print r.cmd("db 0x55555555536d")

okay so it already set its time run the code and we got the flag


the following is the full code of the program:

import r2pipe
import json

#open the binary and turn off the ASLR mode so the memory stay the same
r = r2pipe.open(filename = 'chall',flags=["-d","-e","dbg.profile=chall.rr2"])
flag = ""

r.cmd("aaa")
r.cmd("s main")

#put breakpoint at the comparison, it is better to check the breakpoint before you run it
print r.cmd("db 0x55555555536d")

for x in range(37):
    r.cmd("dc")
    print r.cmd("afvd")
    data = r.cmdj("psj @0x7fffffffdacf") #make sure you have the right memory location
    char_flag = data['string'][0]
    flag += char_flag.encode()
    r.cmd("dr rax=\'" + char_flag.encode()+ "\'")
   
print flag

for chall.rr2:

#!/usr/bin/rarun2
stdin="CTF{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}"
aslr=no


Kudos to Cristos Servos

thanks for giving me a chance to finish this CTF challenge I have a lot of fun

Image result for thanks meme

Comments

Popular posts from this blog

Having fun analyzing nginx log to find malicious attacker in the net (ง'̀-'́)ง (day 37)

  What makes you sleepless at night? is it because of a ghost or scary stories? is it because you have an important meeting tomorrow? or is it because you have an exam? For me, what keeps me up all night is that I keep thinking about what happens to a website that I just created, is it safe from an attacker (certainly not) or did I missing some security adjustments that lead to vulnerability? well I'm not the best secure programmer in the world, I'm still learning and there is a big possibility that I can make a mistake but for me, a mistake can be a valuable investment to myself or yourself to be better so from this idea, I want to know more about what attackers casually do when attacking a website. Here in this post, I'm going to show you how I analyzed attack to the website that I have permission to design and also some interesting findings that I could get from the analysis Background: All of this analysis comes from the traffic that is targeted to th...

Utilize Pwntools for crafting ROP chain :') (day 69)

who doesn't like pwntools? it is a very versatile tool and can be customized according to our need using the python script but did you need to know that pwntools itself can help us to automatically craft a rop chain for us? so in this post, I will show you how to make rop chain less painful and make pwntools do all the heavy lifting. To demonstrate this I will use the binary challenge callme 64 bit from ropemporium link: https://ropemporium.com/challenge/callme.html Crashing the app: Like any other exploitation process, we need to crash the program by generating a long string pattern to determine the offset. based on the information from the above figure we can see that we required to provide 40 bytes of offset Fun stuff: now this where the fun stuff began write the following python script: as in the guideline of the challenged said we need to chain the function call by first to call the callme_one function, callme_two function and then callme_three funct...

Bypassing stack canaries protection :') (day 51)

In my previous blogs, I show you guys how to bypass some common protection usually used in Linux binary such as NX and ASLR but this time we are going to take it to the next level we are going to talk about protection employ in the modern Linux OS which is "The Canaries" and how to bypass it. note: this post was not originally mined it was inspired by the following resources https://ctf-wiki.github.io/ctf-wiki/pwn/linux/mitigation/canary/ (Credit goes to the author) we are going to start this post about what is stack canaries and types of different implementation of it then move to the implementation about how to bypass this protection. We are going to focus on "leak canaries" technique What is stack canary: In layman terms, canaries are just another protection mechanism to prevent stack overflow implemented by appending 4/8 bytes value (depend on the architecture) into the stack when a function is entered. When the function is at the end of its exec...