m00dy's place

[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.)

comments powered by Disqus