📜 ⬆️ ⬇️

I was asked to hack the program at the interview. Part 2

This is a translation of the second part of the publication “I was asked to hack the program at an interview . The original text can be found here .

Foreword


Hi guys. If you do not know what “Part 2” means, please read Part 1 .
To begin, I would like to thank everyone who read the first part, because in the end I received a lot of excellent feedback.

I would also like to eliminate some misunderstandings:
  1. I no longer work for this company, I moved to Barcelona;
  2. I passed this interview almost a year ago;
  3. I hacked programs in the cloud ($ 5 tariff, yes, you guessed the company), so I do not think that using root @ 'a is a problem - I can recreate a new environment in a couple of seconds. As a result, I still switched to user eren @ , since gdb did not accept root-init files.
  4. Do not forget to read the end of the article - you will love it!

Go


This time we will work not with the door, but with a nuclear rocket .

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 will dump the entire binaries with intel asm syntax as a sample:
')
 eren@lisa:~$ objdump -M intel -D CrackTheNuke > staticDis eren@lisa:~$ 


We will need this file later. If you look at the staticDis file, you can find a complete dump with intel 's syntax.

Let's try something else this time: for starters, I will start the process, and then I will hook on it to the debugger.

 eren@lisa:~$ ./CrackTheNuke *** NUKE CONTROL SYSTEM *** PASSWORD: 


Now we can switch to another shell and start a debugger from it:

 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 you can enter any 16 characters in the previous window and return here. We are now in the scanf function, which rests in the glibc library ( crackme will call scanf 16 times, but we will save some time here).

In gdb you can type si (abbr. From single step ). Enter si until you reach the address: 0x80495ed . Or you can simply enter the command: b * 0x80495ed and press to get to the desired address.

In any case, we are now in place:

 0x80495ed <main+195>: 


Here we can see the comparison operation:

 0x80495ed <main+195>: cmp DWORD PTR [esp+0x1c],0x0 


In gdb, you can enter: p/x $esp to view the contents of $ esp .
You can also do some calculations using registers and addresses: p/x $esp+0x1c . Alternatively, view the contents of the address after the renaming: p/x *0xff811bac .

Here you can enter si , which will lead us to the moment when crackme has received our 16 characters and expects a line ending character \ n .

I advise you to put a breakpoint at 0x804962d : b * 0x804962d , if you do not want to wait long and painfully.

And now the fun begins:

 => 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 ever heard of rdtsc instructions? Its main task is counting the number of processor cycles. After calling rdtsc, the TSC counter will be placed in the edx and eax registers:

 0x8049636 <main+268>: test eax,eax 0x8049638 <main+270>: je 0x8049646 <g99> 


The same on C would look something like this:

 if(eax == 0) { goto 0x8049646 } 


Since eax is not 0, we’ll continue to dig. As you most likely noticed, the code below is full trash: we add 0xe to ebx , and then we subtract it. Looks like they are trying to confuse us.

 xor ebx,0xe add ebx,0xe sub ebx,0xe dec eax 


While eax is 0, we continue this cycle.
Set breakpoint: b * 0x8049646 and press c .
Ok, nothing interesting - go ahead.

 => 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 basis of the whole process.

Let's try to investigate all the functions that nkc1qpE2L6f6AyqaendA refers to : 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 


The qEWL8Jl0zdpmTbwhziDv -> fjDKIzPtGuE8ZdfSL8vq -> qEWL8Jl0zdpmTbwhziDv -> W0ElBw5Smo9TPiWOeK8c -> qEWL8Jl0zdpmTbwhziDv as follows: qEWL8Jl0zdpmTbwhziDv -> fjDKIzPtGuE8ZdfSL8vq -> qEWL8Jl0zdpmTbwhziDv -> W0ElBw5Smo9TPiWOeK8c -> qEWL8Jl0zdpmTbwhziDv .

After reviewing the first 10 lines of each function, could you find something unusual? Look carefully at the first lines of fjDKIzPtGuE8ZdfSL8vq and W0ElBw5Smo9TPiWOeK8c , they are absolutely meaningless.

I have never in my life (from a translator: in the original: life :) - most likely referring to a well-known mobile operator) did not meet with anything like this: call 0xb023:0x1c72c78c . And the thing is that both of these functions are encrypted and gdb tried to disassemble them.

So, qEWL8Jl0zdpmTbwhziDv is engaged in deciphering functions (therefore, it is its call that it faces).

I will try to change the program execution algorithm, replacing the encrypted functions with their decrypted matches, and remove the qEWL8Jl0zdpmTbwhziDv call.

Based on this, the new algorithm will look like this: fjDKIzPtGuE8ZdfSL8vq -> W0ElBw5Smo9TPiWOeK8c - that's all.

Dead End 1. Start


Working on this crackme , I tried to disable the TimeStampCounter or somehow control it. In this case, rdtsc is used to check the time interval between the execution of instructions. Accordingly, if you try to run the program through gdb , then this interval will be much longer than the same, but during normal operation of the code. Therefore, I tried to find a way to control the tsc counter, but, unfortunately, it is controlled by a processor - and therefore I cannot do anything from under the OS.
But still, I tried to write a module for the kernel that would knock down the counter, setting its value 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);= 


Unfortunately, this method did not work the way I needed, and I continued searching.

Dead End 1. End


I had an idea to stop the program at the moment when both functions will be in the decoded state. For example, 0x8048ab0 is a very good place, since this is the end of the function fjDKIzPtGuE8ZdfSL8vq .

Let's open .gdbinit and write:

 set disassembly-flavor intel set disassemble-next-line on handle SIGTRAP noprint pass nostop b * 0x8048ab0 


Restart the crackme and hook on gdb again. Enter 16 characters and press c .

 => 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) 


Voila Now we have a clean function fjDKIzPtGuE8ZdfSL8vq . But we still have a problem with the gdb - false assembly (available in English here ).

Let's save our function to a temporary file (parameters: filename, starting_address and ending_address):

 dump ihex memory fjDKIzPtGuE8ZdfSL8vq_dump 0x8048604 0x8048ab0 


Now we will do the same for the second function - we set breakpoint at the address: 0x08048e14 and create a dump:

 dump ihex memory W0ElBw5Smo9TPiWOeK8c_dump W0ElBw5Smo9TPiWOeK8c g999+3 


Now that we have both functions, let's try changing the program execution algorithm. To do this, clear the .gdbinit file and set a breakpoint: 0x80494db :

 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 


Well, now that we have changed the algorithm, everything is quite simple. Follow the instructions described in the first part of this article.

The characters we have entered are correlated (XOR) with some constants, after which the result is checked for correctness: Inputs ^ FirstConstants == SecondConstants , respectively: Inputs = SecondConstants ^ FirstConstants

And here is 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 


Let's go check:

 eren@lisa:~$ ./CrackTheNuke *** NUKE CONTROL SYSTEM *** PASSWORD: ?5*LE;%wGi1#qD$0 *** ACCESS GRANTED *** *** THE NUKE STOPPED *** eren@lisa:~$ 


Everything is working.

Conclusion


I would also like to tell you what happened after I was hired. On the very first day of my new job, they decided to change my department (I still can not understand why this company considers itself the best of the best in Turkey).

After that, I became a J2ee developer. I had to use eclipse , svn, and even an operating system called Windows * . But, as it turned out later, this was not the worst. Later they made me write css ...

But now I live in Barcelona and I have a wonderful life.

Source: https://habr.com/ru/post/238009/


All Articles