[Part2] I was asked to crack a program in a job interview !
Hello guys , (If you don't know what part2 means , please go back and read part 1.) First of all , i want to thank you all the people who read the part1.I got really good feedbacks after all.
But I'm sorry , i would like to correct some misunderstandings.
*My English is not that bad
*I wasn't expecting too much traffic from HN , Reddit and others.
*I'm not working for that company now , I moved to Barcelona.
*I passed the interviews almost 1 year ago.
*I cracked the programs on the cloud.($5 plan , yes you know the company.)
So , I don't really think that root@ is problem.I can redeploy another droplet in seconds.
I switched to eren@ user that's because gdb didn't accept root's init file.
*And finally , please read the end of the post.I'm sure you will like it.
Now , this time it's not about doors , we are going to crack the nuke.
eren@lisa:~$ ./CrackTheNuke
*** NUKE CONTROL SYSTEM ***
PASSWORD: giveMeNuke
*** ACCESS DENIED ***
PASSWORD: iwantanexplosion
*** ACCESS DENIED ***
PASSWORD: knockknockitsme
*** ACCESS DENIED ***
*** SYSTEM LOCKED ***
*** SHUTTING DOWN ***
eren@lisa:~$
I'll dump whole binary into intel asm syntax as reference.
eren@lisa:~$ objdump -M intel -D CrackTheNuke > staticDis
eren@lisa:~$
We'll need this file later.If you look at the staticDis file , you can see whole dump in intel syntax.
This time , we'll try something different.I'll execute the process first and later I'll attach the debugger to the process.
eren@lisa:~$ ./CrackTheNuke
*** NUKE CONTROL SYSTEM ***
PASSWORD:
Therefore in this case , I'll switch to another shell for debugging.
eren@lisa:~$ ps aux | grep Crack
eren 4741 0.0 0.0 1724 252 pts/0 S+ 14:54 0:00 ./CrackTheNuke
eren 4845 0.0 0.1 7832 832 pts/1 S+ 14:56 0:00 grep Crack
eren@lisa:~$ gdb --pid 4741
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Catchpoint 1 (syscall 'ptrace' [26])
Attaching to process 4741
Reading symbols from /home/eren/CrackTheNuke...(no debugging symbols found)...done.
Reading symbols from /lib32/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib32/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0xf7726430 in __kernel_vsyscall ()
=> 0xf7726430 <__kernel_vsyscall+16>: 5d pop ebp
(gdb)
Now type random 16 characters as input to the previous shell , and comeback here.You are in the scanf function which comes from glibc. (The crackme will call scanf 16 times.We save time here)
In gdb , You can type "si" which is an abbreviation for "single step".Type "si" once until you are here 0x80495ed
Or you can type "b * 0x80495ed" and "c" to stop in that address.
Anyway , we are now here
0x80495ed <main+195>:
Ok here , we have a comparison
0x80495ed <main+195>: cmp DWORD PTR [esp+0x1c],0x0
In gdb , You can type
p/x $esp
to examine what's inside of $esp
You can also do some calculations based on registers and addresses
p/x $esp+0x1c
or show the contents of the address by dereferencing
p/x *0xff811bac
You can type "si" here , the crackme has consumed the 16 chars that you have typed just before and now It is looking for "\n" char to understand if we press the enter key or not.
put another break point to 0x804962d "b * 0x804962d" if you don't want to wait.
Now this part is interesting.
=> 0x804962d <main+259>: push eax
0x804962e <main+260>: push ebx
0x804962f <main+261>: rdtsc
0x8049631 <main+263>: and eax,0xfffff
0x8049636 <main+268>: test eax,eax
0x8049638 <main+270>: je 0x8049646 <g99>
0x804963a <main+272>: xor ebx,0xe
0x804963d <main+275>: add ebx,0xe
0x8049640 <main+278>: sub ebx,0xe
0x8049643 <main+281>: dec eax
Have you heard about rdtsc instruction ?
It keeps cpu cycles.After calling rdtsc instruction ,The stamp counter will be stored in edx and eax. After rdtsc , we keep rightmost 20bits of the eax into eax.
0x8049636 <main+268>: test eax,eax
0x8049638 <main+270>: je 0x8049646 <g99>
Above asm lines were written in C , codes would be like below.
if(eax == 0)
{
goto 0x8049646
}
Since eax is not 0 , we will continue to investigate more. As you may see , below codes are kindda trash :).We add 0xe to ebx and later subtract it.Those are put there to get us away from the code :) It's typical psychological test :)
xor ebx,0xe
add ebx,0xe
sub ebx,0xe
dec eax
until eax is 0 , continue this loop :)
put a breakpoint "b * 0x8049646" there , and press "c"
Ok not so much info here , put another step into or continue until
=> 0x80494db <nkc1qpE2L6f6AyqaendA>: push ebp
0x80494dc <nkc1qpE2L6f6AyqaendA+1>: mov ebp,esp
0x80494de <nkc1qpE2L6f6AyqaendA+3>: sub esp,0x14
0x80494e1 <nkc1qpE2L6f6AyqaendA+6>: mov DWORD PTR [ebp-0x4],0x0
0x80494e8 <nkc1qpE2L6f6AyqaendA+13>: mov DWORD PTR [esp],0x0
0x80494ef <nkc1qpE2L6f6AyqaendA+20>: call 0x804944b <qEWL8Jl0zdpmTbwhziDv>
0x80494f4 <nkc1qpE2L6f6AyqaendA+25>: mov eax,DWORD PTR [ebp+0x8]
0x80494f7 <nkc1qpE2L6f6AyqaendA+28>: mov DWORD PTR [esp],eax
0x80494fa <nkc1qpE2L6f6AyqaendA+31>: call 0x8048604 <fjDKIzPtGuE8ZdfSL8vq>
0x80494ff <nkc1qpE2L6f6AyqaendA+36>: mov DWORD PTR [esp],0x2
0x8049506 <nkc1qpE2L6f6AyqaendA+43>: call 0x804944b <qEWL8Jl0zdpmTbwhziDv>
0x804950b <nkc1qpE2L6f6AyqaendA+48>: mov eax,DWORD PTR [ebp+0x8]
0x804950e <nkc1qpE2L6f6AyqaendA+51>: mov DWORD PTR [esp],eax
0x8049511 <nkc1qpE2L6f6AyqaendA+54>: call 0x8048ab1 <W0ElBw5Smo9TPiWOeK8c>
0x8049516 <nkc1qpE2L6f6AyqaendA+59>: mov DWORD PTR [ebp-0x4],eax
0x8049519 <nkc1qpE2L6f6AyqaendA+62>: mov DWORD PTR [esp],0x1
0x8049520 <nkc1qpE2L6f6AyqaendA+69>: call 0x804944b <qEWL8Jl0zdpmTbwhziDv>
0x8049525 <nkc1qpE2L6f6AyqaendA+74>: mov eax,DWORD PTR [ebp-0x4]
0x8049528 <nkc1qpE2L6f6AyqaendA+77>: leave
0x8049529 <nkc1qpE2L6f6AyqaendA+78>: ret
nkc1qpE2L6f6AyqaendA this function is the main workflow for the whole process.
Let's first investigate each function , we have 5 function call in nkc1qpE2L6f6AyqaendA.Those 3 functions are qEWL8Jl0zdpmTbwhziDv , fjDKIzPtGuE8ZdfSL8vq and W0ElBw5Smo9TPiWOeK8c.
(gdb) x/10i qEWL8Jl0zdpmTbwhziDv
0x804944b <qEWL8Jl0zdpmTbwhziDv>: push ebp
0x804944c <qEWL8Jl0zdpmTbwhziDv+1>: mov ebp,esp
0x804944e <qEWL8Jl0zdpmTbwhziDv+3>: mov eax,DWORD PTR [ebp+0x8]
0x8049451 <qEWL8Jl0zdpmTbwhziDv+6>: cmp eax,0x0
0x8049454 <qEWL8Jl0zdpmTbwhziDv+9>: je 0x80494b9 <hzdhp>
0x8049456 <qEWL8Jl0zdpmTbwhziDv+11>: cmp eax,0x1
0x8049459 <qEWL8Jl0zdpmTbwhziDv+14>: je 0x8049499 <qEWL8Jl0zdpmTbwhziDv+78>
0x804945b <qEWL8Jl0zdpmTbwhziDv+16>: call 0x8047b71
0x8049460 <qEWL8Jl0zdpmTbwhziDv+21>: add DWORD PTR [eax+0x48604bf],0x5eb9008
0x804946a <qEWL8Jl0zdpmTbwhziDv+31>: add DWORD PTR [eax-0x4608ea13],0x8048ab1
(gdb) x/10i fjDKIzPtGuE8ZdfSL8vq
0x8048604 <fjDKIzPtGuE8ZdfSL8vq>: call 0xb027:0xaf72c78c
0x804860b <fjDKIzPtGuE8ZdfSL8vq+7>: cmp esi,DWORD PTR ds:0xe4dfbbf1
0x8048611 <fjDKIzPtGuE8ZdfSL8vq+13>: (bad)
0x8048612 <fjDKIzPtGuE8ZdfSL8vq+14>: and al,BYTE PTR [ebp+edi*2-0x8]
0x8048616 <fjDKIzPtGuE8ZdfSL8vq+18>: push ebx
0x8048617 <fjDKIzPtGuE8ZdfSL8vq+19>: push esi
0x8048618 <fjDKIzPtGuE8ZdfSL8vq+20>: inc edx
0x8048619 <fjDKIzPtGuE8ZdfSL8vq+21>: mov WORD PTR [ebp+0x76],ss
0x804861c <fjDKIzPtGuE8ZdfSL8vq+24>: xchg edx,eax
0x804861d <fjDKIzPtGuE8ZdfSL8vq+25>: mov al,ds:0x45fd3fbb
(gd
(gdb) x/10i W0ElBw5Smo9TPiWOeK8c
0x8048ab1 <W0ElBw5Smo9TPiWOeK8c>: call 0xb023:0x1c72c78c
0x8048ab8 <W0ElBw5Smo9TPiWOeK8c+7>: cmp esi,DWORD PTR ds:0xe4dfbbf1
0x8048abe <W0ElBw5Smo9TPiWOeK8c+13>: jmp 0xf86e358
0x8048ac3 <W0ElBw5Smo9TPiWOeK8c+18>: xchg ax,ax
0x8048ac5 <W0ElBw5Smo9TPiWOeK8c+20>: out dx,eax
0x8048ac6 <W0ElBw5Smo9TPiWOeK8c+21>: dec ebp
0x8048ac7 <W0ElBw5Smo9TPiWOeK8c+22>: xchg edi,eax
0x8048ac8 <W0ElBw5Smo9TPiWOeK8c+23>: popa
0x8048ac9 <W0ElBw5Smo9TPiWOeK8c+24>: test DWORD PTR [ecx-0x7e],esp
0x8048acc <W0ElBw5Smo9TPiWOeK8c+27>: test DWORD PTR [edi],esi
So the workflow is in function nkc1qpE2L6f6AyqaendA ,
we call qEWL8Jl0zdpmTbwhziDv -> fjDKIzPtGuE8ZdfSL8vq -> qEWL8Jl0zdpmTbwhziDv -> W0ElBw5Smo9TPiWOeK8c -> qEWL8Jl0zdpmTbwhziDv.
As we examined the first 10 lines of each function.Have you felt something unusual ?
Especially , If you look at the first lines of each function , you may figure out that first lines of fjDKIzPtGuE8ZdfSL8vq and W0ElBw5Smo9TPiWOeK8c are quite stupid :)
I havent seen such a code "call 0xb023:0x1c72c78c" in my entire life :).It's because those 2 functions are totally encrypted and gdb tried to disassemble those encrypted bytes :)
So qEWL8Jl0zdpmTbwhziDv actually decrypts the functions.Therefore , it is called before them.
I am going to change whole workflow ,i will swap encrypted functions with plain ones and remove qEWL8Jl0zdpmTbwhziDv function from the workflow.
Therefore , new workflow will be fjDKIzPtGuE8ZdfSL8vq -> W0ElBw5Smo9TPiWOeK8c and that's it.
DeadEnd 1 Starts
But before doing all these stuff , i would like tell you my another dead-end.It's quite important for me. While i was working on this crackme , I looked for a way to disable TimeStampCounter or somehow controll it.Because crackme calls "rdtsc" instruction 2 times , substract from each other and calculate the time between instructions that have been executed. If you run the program under gdb , the time between instructions take much more time than it is executed by just itself.Therefore , I first searched a way to controll tsc counter.Tsc counter gets its value through CPU.It's out of my ability to change its state with OS.But i wrote a kernel module to update it to 0.
#include <linux/module.h> // included for all kernel modules
#include <linux/kernel.h> // included for KERN_INFO
#include <linux/init.h> // included for __init and __exit macros
#include <linux/kthread.h> // for threads
#include <linux/sched.h> // for task_struct
#include <linux/time.h> // for using jiffies
#include <linux/timer.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("m00dy");
MODULE_DESCRIPTION("A Fake rdtsc emulation");
static struct task_struct *thread1;
int thread_fn(){
uint32_t hi,lo;
unsigned long j0,j1;
int delay = HZ / 250;
hi=0; lo=0xb;
printk(KERN_INFO "In thread1");
j0 = jiffies;
j1 = j0 + delay;
asm volatile("wrmsr"::"c"(0x10),"a"(lo),"d"(hi));
while(1){
if(time_before(jiffies,j1))
schedule();
else
{
j1 = jiffies + delay;
asm volatile("wrmsr"::"c"(0x10),"a"(lo),"d"(hi));
}
}
}
static int __init hello_init(void)
{
char our_thread[8]="thread1";
printk(KERN_INFO "in init");
thread1 = kthread_create(thread_fn,NULL,our_thread);
if((thread1))
{
printk(KERN_INFO "in if");
wake_up_process(thread1);
}
return 0;
}
static void __exit hello_cleanup(void)
{
printk(KERN_INFO "Fake RDTSC end \n");
}
module_init(hello_init);
module_exit(hello_cleanup);
I lost the makefile but if you want to compile it , you can use helloworld makefile template for kernel module.You can easily find it on google.
Anyway , that code didnt work as i expected.So i tried something else.
DeadEnd 1 Ends
My strategy will be to stop program where those 2 encrypted functions are in plain state.
For example , 0x8048ab0 is in quite good position.Because it's end of the fjDKIzPtGuE8ZdfSL8vq.
Now open .gdbinit file again.And put these things.
set disassembly-flavor intel
set disassemble-next-line on
handle SIGTRAP noprint pass nostop
b * 0x8048ab0
Re open the crackme , attach the gdb again.Put 16chars and type "c". The program should stop
=> 0xf7706430 <__kernel_vsyscall+16>: 5d pop ebp
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x08048ab1 in W0ElBw5Smo9TPiWOeK8c ()
=> 0x08048ab1 <W0ElBw5Smo9TPiWOeK8c+0>: 9a 8c c7 72 1c 23 b0 call 0xb023:0x1c72c78c
(gdb) x/10i fjDKIzPtGuE8ZdfSL8vq
0x8048604 <fjDKIzPtGuE8ZdfSL8vq>: push ebp
0x8048605 <fjDKIzPtGuE8ZdfSL8vq+1>: mov ebp,esp
0x8048607 <fjDKIzPtGuE8ZdfSL8vq+3>: call 0x8047b08
0x804860c <fjDKIzPtGuE8ZdfSL8vq+8>: xor eax,0x20ec8390
0x8048611 <fjDKIzPtGuE8ZdfSL8vq+13>: call 0x8047b08
0x8048616 <fjDKIzPtGuE8ZdfSL8vq+18>: xor eax,0x32ff45c6
0x804861b <fjDKIzPtGuE8ZdfSL8vq+23>: call 0x8047b08
0x8048620 <fjDKIzPtGuE8ZdfSL8vq+28>: xor eax,0xdafe45c6
0x8048625 <fjDKIzPtGuE8ZdfSL8vq+33>: call 0x8047b08
0x804862a <fjDKIzPtGuE8ZdfSL8vq+38>: xor eax,0xdbfd45c6
(gdb)
Ok we have at least plain form of the function fjDKIzPtGuE8ZdfSL8vq :) There's still a trick there.Gdb still have problems.This technique called false assembly.Please read this for more info.
Now we need to dump the plain function to a file.Paramethers are filename startAddr and endAddr.
in this case , i typed
dump ihex memory fjDKIzPtGuE8ZdfSL8vq_dump 0x8048604 0x8048ab0
We dumped the entire function into fjDKIzPtGuE8ZdfSL8vq_dump file as plain.
Now , we need to do this for another encrypted function.
put a breakpoint 0x08048e14 here , wait until program crashes and then type
dump ihex memory W0ElBw5Smo9TPiWOeK8c_dump W0ElBw5Smo9TPiWOeK8c g999+3
Let's get back to the surgery.
We now have plain forms of both encrypted functions.We can now change the workflow.
Ok now , clean the .gdbinit file and put a breakpoint in 0x80494db
(If you struggle here , please look at nkc1qpE2L6f6AyqaendA function.)
set disassembly-flavor intel
set disassemble-next-line on
break * 0x80494ef
commands
set($eip) = 0x80494f4
continue
end
break * 0x80494fa
commands
restore fjDKIzPtGuE8ZdfSL8vq_dump
restore W0ElBw5Smo9TPiWOeK8c_dump
continue
end
break * 0x08049506
commands
set($eip) = 0x804950b
continue
end
break * 0x8049520
commands
set($eip) = 0x8049525
continue
end
We changed the workflow , replaced the encrypted functions with plain ones.Now this part is easy.The main algorithm is same as part 1.
Inputs are xored with other constants and we compare the outputs with other constants
Inputs ^ FirstConstants == SecondConstants
Therefore , Inputs = SecondConstants ^ FirstConstants
Here is the our key generator.
#!/usr/bin/python
firstConst = [0x32,0xda,0xdb,0x1,0xf3,0x77,0x4c,0x57,0xbe,0x49,0xec,0x5f,0xab,0x7f,0xed,0x9f]
secondConst = [0x0d,0xef,0xf1,0x4d,0xb6,0x4c,0x69,0x20,0xf9,0x20,0xdd,0x7c,0xda,0x3b,0xc9,0xaf]
ret =""
for x in range(16):
ret+=chr(firstConst[x] ^ secondConst[x])
print ret
Here we go.
eren@lisa:~$ ./CrackTheNuke
*** NUKE CONTROL SYSTEM ***
PASSWORD: ?5*LE;%wGi1#qD$0
*** ACCESS GRANTED ***
*** THE NUKE STOPPED ***
eren@lisa:~$
I'll tell you what happened after i signed the contract
In my first day of the new job , They changed my department.(I still dont know why and the company still markets itself like best of bests in Turkey.)
I became J2ee developer.I was using eclipse , committing svn , using a operating system called "Windows *".They even asked me to write some css.
Now , i live in Barcelona and have a great life.
bb
(If you want to have the crackme , shoot me an email.)
(I can also give you the plain dumps of the encrypted functions in ihex format.)
(Typos & comments are always welcome.)