reversing by example - wiki.jaxhax.org · demystifying the "black box". ... reversing is...
Post on 29-Sep-2020
8 Views
Preview:
TRANSCRIPT
Reversing By Example
Travis Phillips
What This Talk is...
● This talk will walk through a few reversing challenges.
– The first few are built by me
– The rest were found on crackmes.de
● This talk is to demo:– The mindset I used when walking through
these.
– Some of the tools and how I used them.
– Some of the techinques used to provide solutions.
What This Talk is Not?
● It isn't a reverse engineering class.● LD_PRELOAD while useful, won't be
covered.● Not a primer to assembly.
– covered bsides jax 2014 – slides online
● Not a gdb tutorial.– covered jaxLUG 2015 – slides online
What is Reversing?
● Engineering– Idea
– Produce specs
– Build
– Finished Product
● Reverse Engineering is taking this process backwards.
Why Reverse Engineer?
● VERY IN DEMAND IN THE SECURITY FIELD!
● Demystifying the "Black Box".● Malware.● Unknown Item and Use.● Item is not longer supported, but you still
need it to work.● Reversing is an Art that falls in line with
debugging.
Warnings About Reversing● REVERSE ENGINEERING IS ILLEGAL IF
YOU AREN'T ALLOWED TO DO SO BY THE IP OWNER!!!
● You will break things sometimes... Just accept this risk or don't do it.
● You may Possibly lose: – Sleep
– Free time
– Your social life.
– Fragments of your sanity.
Skills Need● [Dis]Assembly and debugging is good● Curious Mindset● Documentation skills● Don't be afraid to break things...
– It will happen... Accept it and be ready to fix things or replace them.
First Example: simple_password● Written by me.● A simple find the password crackme
First Example: simple_password● Let's just give it a test run...
What Did We See and Learn● The banner was printed on both the help
and attempted run.● Not quite right was printed. It seems the
password is not password.
First Example: simple_password● Next, Let's check strings
What Did We See and Learn● We did see some strings of interest in this
command.
First Example: simple_password● Let's Attempt that possible password
What Did We See and Learn● Some times devs hard code passwords.● If left as a normal string, finding them is
trivial.● If you are a dev: Please don't hard code
passwords!● We win the challenge.
Second Example: less_simple_password
● Written by me.● Like simple_password. But with some minor
changes to make it less simple.– Hide password string better!
Second Example: less_simple_password
● Let's just test it out.
What Did We See and Learn● Seems to be about the same as the first
example.● Testing isn't the password.
Second Example: less_simple_password
● Let's see what strings yields.
What Did We See and Learn● Password doesn't seem to be visible via
strings this time :-(● We'll need to try something else.
LTRACE● Ltrace is a linux tool that is used to show
calls to c library functions and the parameters passed to it.
● This is a useful tool for reversing!
What Did We See and Learn● Ltrace revealed a call to strcmp and showed
it's arguments!● One argument was my supplied password,
the other was the real password!
Second Example: less_simple_password
● Let's see what happens if we make strcmp happy :-)
What Did We See and Learn● While we found a way to hide the string. It
was still found out.● Take away: HARD CODED PASSWORDS
ARE A BAD IDEA!
How Did We Hide The String?● Not using the string. Instead use some sort
of math or algo to generate the string at runtime. This still isn't secure however.
How Did We Hide The String?
Third Example: josamont's j333
● Found on crackmes.de● Written by josamont● Written in x86 assembly● Stripped
Third Example: josamont's j333
● Let's check strings.
What Did We See and Learn● Not many strings or junk data here.
Assembly usually makes a good clean binary.
● Strings of interest:– 2793246581
– velvet
Third Example: josamont's j333
● Let's Test Those strings out.
What Did We See and Learn● Neither of them worked.● However we did observe something
interesting.– When typing velvet and enter, when the
program ended there was an extra prompt.
– When entering the number, some of it was sent to the command line after the program exited along with an enter keystroke
– This means it is probably using read and reading a finite amount of characters.
Third Example: josamont's j333
● Since this was written in x86 asm, let's try strace since he is probably using syscalls.
What Did We See and Learn● Read() syscall only reads 6 bytes. The g and
enter was extra and ignored by the program and got passed to the command line.
● That said, the password should be 5 or 6 characters, depending on if the new line from enter counts as part of the password.
What Did We See and Learn
What Did We See and Learn
Third Example: josamont's j333
● Since we have that unknown logic, Let's open the binary in Hopper and see if we can't find it.
Third Example: josamont's j333
What's at 0x80480ea
● Set EAX to 4– This sets up the write() syscall
● Set EBX to 1– EBX is used to set the FD for write, 1 is
stdout
● Int 0x80 – Tells the kernel to execute the syscall.
● This is basically a write syscall. Assumes ECX and EDX are set before the call.
Third Example: josamont's j333
● Rename it to improve our ability to read the code.
Third Example: josamont's j333● We can start to line asm up to our flowchart
Third Example: josamont's j333● … and update the flowchart ;-)
Third Example: josamont's j333 – Done Son!
Fourth Example: ascii's kgm1
● From crackmes.de.● X86, written in C● Goal is To build a Keygen for this one
Fourth Example: ascii's kgm1
● Let's think about the goal for a min.● We need to build a keygen, so we need:
– To find the keygen algo
– Find a few good serials for known good dataset.
– Understand the keygen algo
– Replicate the keygen algo in our program.
● May or may not be in that order.
Fourth Example: ascii's kgm1
● Let's look at the first run again. This one asked for a serial only, no username.
● No other user data seeds the generation of the serial.
● Could be static or possible math/random generated.
Fourth Example: ascii's kgm1
● Let's look at strings.
● Not much of interest here...
● Low possibility of being static.
● Static keygens are boring anyways!
Fourth Example: ascii's kgm1
● Let's ltrace it.● Not much
interesting here either.
● We did learn it uses fgets() to read in 16 bytes.
● Seems we will have to disassemble.
Fourth Example: ascii's kgm1
● This binary seems to be stripped, so we get this boiler plate code. The last argument pushed to the stack just before the call to __libc_start_main, is main(). Let's rename it.
Fourth Example: ascii's kgm1
● Click the address, hit 'N' to rename it. After it is renamed, hit enter to follow it. This is the code before any jump runs (jne in this case).
What Did We See and Learn● We see some boiler plate stack
management at the beginning.● We see a few calls to printf(), fgets(), and
strlen(). Let's just go ahead and flow chart those.
– Printf() for the banner
– Printf() for “Key: ”
– Fgets() to collect the data from user
– Strlen() to get the length of the user data
● Let's flowchart that really quickly.
What Did We See and Learn
What's at 0x80484cf?● Sadness and despair...● Prints invalid key and exits with a value of 1.● We can rename this area PrintInvalidKey
and update the flowchart.
What Did We See and Learn
What Did We See and Learn● The major take away from that code
segment is that the key must be 10 character long, including the newline!
– Actual text would be 9 text characters.
– If this isn't met, it will reject the key right away!
● Let's continue with the code after that jne jump assuming we didn't follow it (e.g. our key is 9 alpha long). What does the program do next.
Fourth Example: ascii's kgm1
● The following code till the next jump instruction. Which seems to be a loop.
● The instruction at 0x08048478 referenced esp+arg_10, I renamed that to userInput for readability
Fourth Example: Loop Breakdown
● The jne points to the third instruction in the code sample.
● This means the first two instructions are initializers, setting edx to 1, and ecx to userInput pointer.
Fourth Example: Loop Breakdown
● Next we enter the loop, we set al to whatever [edx + 7] equals... This seems like an illegal address... Let's double check this disassembled correctly in gdb.
Fourth Example: Loop Breakdown
● Ah... The plot thickens, It was wrong!– It wasn't [edx+7],
– It was [edx+0x8049707]. A POINTER!
Fourth Example: Loop Breakdown
● A quick bug report to the dev of Hopper and two days later we have a new release of hopper! Looks much better! Thanks Vincent!
Fourth Example: Loop Breakdown
● Next add 1 to edx● See if edx = 9, if not, continue to loop.
– So it seems that edx is an iterator for the loop. Which should loop through the first 8 chars in the userInput and leave 9 alone.
Fourth Example: Loop Breakdown
● To simplify in mock-up code:I = 0
Xorkey = [0x45, 0x36, 0xab, 0xc8, 0xcc, 0x11, 0xe3, 0x7a]
While i < 8 {
userinput[i] = userinput[i] ^ xorkey(i)
I++
}
Fourth Example: Add The Loop into the FlowChart
Fourth Example: Next Loop Breakdown
● We have another loop that follows.● Initalize:
– Set ECX to 0
– Set EDX to 0
– Set EBX to userInput Pointer
Fourth Example: Next Loop Breakdown
● EDX seems to be a counter again, counting up to 8 from zero.
● The loop seems to step through the XOR'd userInput Buffer byte by byte and adding them up and keeping the total in ECX.
Fourth Example: Next Loop Breakdown
● Mock up code– Int I = 0
– Int total = 0
– While I < 8 {● total = total + userInput[i]
– }
Fourth Example: Next Loop Breakdown
Fourth Example: ascii's kgm1
● Keep in mind ECX has the total sum from the last loop
● The first instruction moves the 9th character from the user string into EDX.
● Compare EDX and ECX. If they aren't equal, we jump to PrintInvalidKey.
Fourth Example: ascii's kgm1
Fourth Example: ascii's kgm1
● Keep in mind EDX has the total sum/9th char from the user key.
● We use LEA to set EAX to total-97.● Compare EAX to 25. If EAX is over 25, it will
jump to PrintInvalidKey. There for the total needs to be 122 or lower (97 + 25). But it we will also want it to be above zero as well.
Fourth Example: ascii's kgm1
Fourth Example: ascii's kgm1
● If we clear that check it looks like we are on our way to winning.
● Prints “Good Key!” and set EAX to zero and jumps to the return at the end of main.
– This is basically the same as return 0;
Fourth Example: ascii's kgm1
Fourth Example: ascii's kgm1
Fourth Example: ascii's kgm1
● So the key must:– Be 9 characters
– The total of the first 8 after being xor'd by the key must equal the ninth character
– The total must be greater than 97
– The total must be less than 122
● Because of this design, we can't memory fish a key (that is, run it in a debugger and grab it from the compare).
Fourth Example: ascii's kgm1
● We will have to generate a key that makes the requirements happy.
– Doing this by hand is tricky.
● The solution is a brute force generator.● While true loop
– Generate character string
– Xor and total it.
– Check that it is above and below limits
– If so, append it as the 9th char
– If not loop again.
Fourth Example: ascii's kgm1
Fourth Example: ascii's kgm1 Keygen
Questions?
Slides, Example Binaries, useful link, and solutions available on:
wiki.jaxhax.org
top related