Ich wurde gebeten, das Programm für ein Interview zu hacken. Teil 2

Dies ist eine Übersetzung des zweiten Teils der Veröffentlichung "Ich wurde gebeten, das Programm für ein Interview zu hacken . " Den Originaltext finden Sie hier .

Vorwort


Hallo Leute. Wenn Sie nicht wissen, was "Teil 2" bedeutet, lesen Sie bitte Teil 1 .
Zunächst möchte ich mich bei allen bedanken, die den ersten Teil gelesen haben, denn am Ende habe ich viele hervorragende Rückmeldungen erhalten.

Außerdem möchte ich einige Missverständnisse beseitigen:
  1. Ich arbeite nicht mehr für diese Firma, sondern bin nach Barcelona gezogen.
  2. Ich habe dieses Interview vor fast einem Jahr gemacht.
  3. Ich habe die Programme in der Cloud geknackt (5-Dollar-Tarif, ja, Sie haben das Unternehmen erraten), daher glaube ich nicht, dass die Verwendung von root @ 'a ein Problem darstellt - ich kann in wenigen Sekunden eine neue Umgebung erstellen. Am Ende bin ich noch auf den Benutzer eren @ umgestiegen , da gdb keine root-int-Dateien akzeptiert hat.
  4. Vergessen Sie nicht, das Ende des Artikels zu lesen - Sie werden es lieben!

Lass uns gehen


Dieses Mal werden wir nicht mit der Tür arbeiten, sondern mit einer Atomrakete .

eren@lisa:~$ ./CrackTheNuke 
        *** NUKE CONTROL SYSTEM  ***
PASSWORD: giveMeNuke
        *** ACCESS DENIED ***
PASSWORD: iwantanexplosion
        *** ACCESS DENIED ***
PASSWORD: knockknockitsme 
        *** ACCESS DENIED ***
        *** SYSTEM LOCKED ***
        *** SHUTTING DOWN ***
eren@lisa:~$

Ich werde die gesamte Binärdatei mit der Intel ASM- Syntax als Beispiel sichern :

eren@lisa:~$ objdump -M intel -D CrackTheNuke > staticDis 
eren@lisa:~$


Wir werden diese Datei später brauchen. Wenn Sie in die staticDis- Datei schauen , finden Sie einen vollständigen Speicherauszug mit Intel -Syntax.

Lassen Sie uns diesmal etwas anderes ausprobieren: Zuerst starte ich den Prozess, und danach greife ich zu einem Debugger.

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


Jetzt können wir zu einer anderen Shell wechseln und den Debugger ausführen:

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


Jetzt können Sie im vorherigen Fenster 16 beliebige Zeichen eingeben und hierher zurückkehren. Wir befinden uns jetzt in der Funktion scanf , die sich in der glibc- Bibliothek befindet ( crackme ruft scanf 16 Mal auf, aber wir sparen hier etwas Zeit).

In gdb können Sie si (Abk. From single step ) eingeben . Geben Sie si ein, bis Sie die Adresse 0x80495ed erreichen . Oder Sie geben einfach den Befehl ein: b * 0x80495edund drücken с, um zur gewünschten Adresse zu gelangen.

In jedem Fall sind wir jetzt vor Ort:

0x80495ed :


Hier sehen wir die Vergleichsoperation:

0x80495ed :    cmp    DWORD PTR [esp+0x1c],0x0


In gdb können Sie Folgendes eingeben:, p/x $espum den Inhalt von $ esp anzuzeigen .
Sie können auch einige Berechnungen Register und Adresse mit: p/x $esp+0x1c. Oder sehen Sie den Inhalt der Adresse nach razimenovaniya: p/x *0xff811bac.

Hier können Sie si eingeben , was uns zu dem Moment führt, in dem crackme unsere 16 Zeichen erhalten hat und das Zeilenendezeichen erwartet \ n .

Ich rate Ihnen, einen Haltepunkt bei 0x804962d zu setzen : b * 0x804962dwenn Sie nicht lange und schmerzhaft warten möchten.

Und jetzt beginnt der Spaß:

=> 0x804962d :   push   eax
   0x804962e :  push   ebx
   0x804962f :  rdtsc  
   0x8049631 :  and    eax,0xfffff
   0x8049636 :  test   eax,eax
   0x8049638 :  je     0x8049646 
   0x804963a :  xor    ebx,0xe
   0x804963d :  add    ebx,0xe
   0x8049640 :  sub    ebx,0xe
   0x8049643 :  dec    eax


Hast du jemals von dem rdtsc- Handbuch gehört ? Ihre Hauptaufgabe besteht darin, die Anzahl der Prozessorzyklen zu zählen. Nach dem Aufruf von rdtsc wird der TSC-Zähler in die Register edx und eax gestellt :

0x8049636 :        test   eax,eax
0x8049638 :        je     0x8049646 


Das gleiche in C würde ungefähr so ​​aussehen:

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


Da eax nicht 0 ist, werden wir weiter graben. Wie Sie höchstwahrscheinlich bemerkt haben, ist der folgende Code ein vollständiger Papierkorb: Wir addieren 0xe zu ebx und subtrahieren ihn dann. Es scheint, dass sie versuchen, uns zu verwirren.

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


Während eax 0 ist, setzen wir diesen Zyklus fort.
Setzen Sie einen Haltepunkt: b * 0x8049646und drücken Sie c .
Okay, nichts interessantes - mach weiter.

=> 0x80494db :   push   ebp
   0x80494dc :    mov    ebp,esp
   0x80494de :    sub    esp,0x14
   0x80494e1 :    mov    DWORD PTR [ebp-0x4],0x0
   0x80494e8 :   mov    DWORD PTR [esp],0x0
   0x80494ef :   call   0x804944b 
   0x80494f4 :   mov    eax,DWORD PTR [ebp+0x8]
   0x80494f7 :   mov    DWORD PTR [esp],eax
   0x80494fa :   call   0x8048604 
   0x80494ff :   mov    DWORD PTR [esp],0x2
   0x8049506 :   call   0x804944b 
   0x804950b :   mov    eax,DWORD PTR [ebp+0x8]
   0x804950e :   mov    DWORD PTR [esp],eax
   0x8049511 :   call   0x8048ab1 
   0x8049516 :   mov    DWORD PTR [ebp-0x4],eax
   0x8049519 :   mov    DWORD PTR [esp],0x1
   0x8049520 :   call   0x804944b 
   0x8049525 :   mov    eax,DWORD PTR [ebp-0x4]
   0x8049528 :   leave  
   0x8049529 :   ret

nkc1qpE2L6f6AyqaendA- Diese Funktion ist die Basis des gesamten Prozesses.

Lassen Sie uns versuchen , alle Möglichkeiten zu erforschen, auf die zugegriffen werden nkc1qpE2L6f6AyqaendA : qEWL8Jl0zdpmTbwhziDv , fjDKIzPtGuE8ZdfSL8vq und W0ElBw5Smo9TPiWOeK8c :

(gdb) x/10i qEWL8Jl0zdpmTbwhziDv
   0x804944b :  push   ebp
   0x804944c :    mov    ebp,esp
   0x804944e :    mov    eax,DWORD PTR [ebp+0x8]
   0x8049451 :    cmp    eax,0x0
   0x8049454 :    je     0x80494b9 
   0x8049456 :   cmp    eax,0x1
   0x8049459 :   je     0x8049499 
   0x804945b :   call   0x8047b71
   0x8049460 :   add    DWORD PTR [eax+0x48604bf],0x5eb9008
   0x804946a :   add    DWORD PTR [eax-0x4608ea13],0x8048ab1
(gdb) x/10i fjDKIzPtGuE8ZdfSL8vq
   0x8048604 :  call   0xb027:0xaf72c78c
   0x804860b :    cmp    esi,DWORD PTR ds:0xe4dfbbf1
   0x8048611 :   (bad)  
   0x8048612 :   and    al,BYTE PTR [ebp+edi*2-0x8]
   0x8048616 :   push   ebx
   0x8048617 :   push   esi
   0x8048618 :   inc    edx
   0x8048619 :   mov    WORD PTR [ebp+0x76],ss
   0x804861c :   xchg   edx,eax
   0x804861d :   mov    al,ds:0x45fd3fbb
(gd
(gdb) x/10i W0ElBw5Smo9TPiWOeK8c
   0x8048ab1 :  call   0xb023:0x1c72c78c
   0x8048ab8 :    cmp    esi,DWORD PTR ds:0xe4dfbbf1
   0x8048abe :   jmp    0xf86e358
   0x8048ac3 :   xchg   ax,ax
   0x8048ac5 :   out    dx,eax
   0x8048ac6 :   dec    ebp
   0x8048ac7 :   xchg   edi,eax
   0x8048ac8 :   popa   
   0x8048ac9 :   test   DWORD PTR [ecx-0x7e],esp
   0x8048acc :   test   DWORD PTR [edi],esi


Как мы можем увидеть, основной алгоритм находится внутри функции nkc1qpE2L6f6AyqaendA , а цепочка вызовов выглядит следующим образом: qEWL8Jl0zdpmTbwhziDv -> fjDKIzPtGuE8ZdfSL8vq -> qEWL8Jl0zdpmTbwhziDv -> W0ElBw5Smo9TPiWOeK8c -> qEWL8Jl0zdpmTbwhziDv.

Просмотрев первые 10 строк каждой функции, смогли ли вы найти нечто необычное? Посмотрите внимательно на первые строки fjDKIzPtGuE8ZdfSL8vq и W0ElBw5Smo9TPiWOeK8c , они абсолютно бессмысленны.

Я ни разу в жизни (от переводчика: в оригинале: life:) — вероятнее всего отсыл к небезизвестному мобильному оператору) не встречался с чем-либо подобным: call 0xb023:0x1c72c78c. А все дело в том, что обе эти функции зашифрованы и gdb попытался их дизассемблить.

Итак, qEWL8Jl0zdpmTbwhziDvbefasst sich mit der Entschlüsselung von Funktionen (daher steht ihr Aufruf vor ihnen).

Ich werde versuchen, den Programmausführungsalgorithmus zu ändern, indem ich die verschlüsselten Funktionen durch ihre entschlüsselten Entsprechungen ersetze und den qEWL8Jl0zdpmTbwhziDv- Aufruf entferne .

Auf dieser Basis sieht der neue Algorithmus folgendermaßen aus: fjDKIzPtGuE8ZdfSL8vq -> W0ElBw5Smo9TPiWOeK8c- und das wars.

Sackgasse 1. Starten


Während der Arbeit an diesem Crackme habe ich versucht, den TimeStampCounter zu deaktivieren oder irgendwie zu steuern. In diesem Fall wird mit rdtsc das Zeitintervall zwischen der Ausführung von Befehlen überprüft. Wenn Sie versuchen, das Programm über gdb auszuführen , ist dieses Intervall dementsprechend viel größer als das gleiche, jedoch mit normaler Code-Operation. Daher habe ich versucht, einen Weg zu finden, um den tsc- Zähler zu steuern , aber leider wird er vom Prozessor gesteuert - und deshalb kann ich unter dem Betriebssystem nichts tun.
Trotzdem habe ich versucht, ein Modul für den Kernel zu schreiben, das den Zähler erreicht, indem der Wert auf 0 gesetzt wird:

#include     // included for all kernel modules
#include     // included for KERN_INFO
#include       // included for __init and __exit macros
#include   // for threads
#include   // for task_struct
#include    // for using jiffies 
#include 
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);=


Leider funktionierte diese Methode nicht so, wie ich es brauchte, und ich fuhr mit der Suche fort.

Sackgasse 1. Ende


Ich hatte die Idee, das Programm zu einem Zeitpunkt zu stoppen, an dem beide Funktionen entschlüsselt sind. Zum Beispiel ist 0x8048ab0 ein sehr guter Ort, da dies das Ende der Funktion fjDKIzPtGuE8ZdfSL8vq ist .

Öffnen wir .gdbinit und schreiben:

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


Wir starten crackme neu und haken wieder gdb ein . Geben Sie 16 Zeichen ein und drücken Sie c .

=> 0xf7706430 <__kernel_vsyscall+16>:  5d  pop    ebp
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x08048ab1 in W0ElBw5Smo9TPiWOeK8c ()
=> 0x08048ab1 : 9a 8c c7 72 1c 23 b0  call   0xb023:0x1c72c78c
(gdb) x/10i fjDKIzPtGuE8ZdfSL8vq
   0x8048604 :  push   ebp
   0x8048605 :    mov    ebp,esp
   0x8048607 :    call   0x8047b08
   0x804860c :    xor    eax,0x20ec8390
   0x8048611 :   call   0x8047b08
   0x8048616 :   xor    eax,0x32ff45c6
   0x804861b :   call   0x8047b08
   0x8048620 :   xor    eax,0xdafe45c6
   0x8048625 :   call   0x8047b08
   0x804862a :   xor    eax,0xdbfd45c6
(gdb)


Voila. Jetzt haben wir eine saubere Funktion fjDKIzPtGuE8ZdfSL8vq . Wir haben aber immer noch ein Problem mit gdb - false assembly ( hier in englischer Sprache verfügbar ).

Speichern wir unsere Funktion in einer temporären Datei (Parameter: Dateiname, Startadresse und Endadresse):

dump ihex memory fjDKIzPtGuE8ZdfSL8vq_dump 0x8048604 0x8048ab0


Jetzt machen wir dasselbe für die zweite Funktion - setzen Sie den Haltepunkt auf die Adresse: 0x08048e14 und erstellen Sie einen Dump:

dump ihex memory W0ElBw5Smo9TPiWOeK8c_dump W0ElBw5Smo9TPiWOeK8c g999+3


Nachdem wir nun beide Funktionen haben, wollen wir versuchen, den Programmausführungsalgorithmus zu ändern. Um dies zu tun, löschen wir die Datei .gdbinit und einen Haltepunkt: 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


Nun, da wir den Algorithmus geändert haben, ist alles ganz einfach. Befolgen Sie die Anweisungen im ersten Teil dieses Artikels.

Die Charaktere , die wir die CORF (XOR) c durch bestimmte Konstanten eingeführt haben, dann wird das Ergebnis bestätigt: Inputs ^ FirstConstants == SecondConstantsjeweils: Inputs = SecondConstants ^ FirstConstants

Und hier ist unser Schlüssel - 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


Lass uns nachsehen:

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


Alles arbeitet.

Fazit


Ich möchte Ihnen auch mitteilen, was nach meiner Einstellung passiert ist. Am allerersten Tag meines neuen Jobs haben sie beschlossen, meine Abteilung zu wechseln (ich kann immer noch nicht verstehen, warum dieses Unternehmen sich als das Beste der Besten in der Türkei betrachtet).

Danach wurde ich J2ee- Entwickler. Ich musste Eclipse , SVN und sogar ein Betriebssystem namens Windows * verwenden . Aber wie sich später herausstellte, war es nicht das Schlimmste. Später ließen sie mich CSS schreiben ...

Aber jetzt lebe ich in Barcelona und ich habe ein wundervolles Leben.

Jetzt auch beliebt: