Hace un tiempo dimos los primeros pasos en el mundo de la ingenierÃa inversa, como está organizada la pila, como ejecutar una shellcode, como escribir nuestras shellcodes y ejecución de una shell mediante shellcode. Para realizar todas las tareas enteriores utilizabamos GDB, un magnÃfico software para depurar nuestros porgramas pero la tarea de reversing se volvÃa un tanto pesada, esta vez utilizaremos Radare, enfocada desde los cimientos al reversing.
Radare es un software realmente potente, este se compone de varios elementos:
- radare2: Editor hexadecimal y debugger
- rabin2: Extractor de info de los ejecutables binarios
- rasm2: Ensamblador, desensamblador desde la CLI
- rahash2: Generador de hashes
- radiff2: Utilidad para buscar diferencias utilizando varios algoritmos
- rafind2: Buscador de patrones en un fichero
- ragg2: Compilador de binarios
- rarun2: Nos permite correr el programa en diferentes entornos, variables de entorno, argumentos, directorios…
Hay un libro online que nos puede resultar realmente útil.
En la propia web se recomienda instalar desde los repos ya que asà obtendremos siempre la última versión.
Instalamos los bindings(librerias de integración) de varios lenguajes de programación, Valabind, SWIG, Ctypes, NodeJS, Dlang, Go:
ln -s /usr/bin/valac-0.24 /usr/bin/valac
cd ..
git clone https://github.com/radare/valabind
cd valabind
make
make install PREFIX=/usr
Ahora para python, nodejs, ruby:
git clone https://github.com/radare/radare2-bindings
cd radare2-bindings
./configure –prefix=/usr
cd python
make
make install
Testeamos la instalación:
Instalamos el editor hexadecimal ired:
sdb: base de datos clave-valor similar a redis, utilizada para operaciones internas de radare.
Ahora que ya tenemos todo lo necesario procedemos con la programación de un pequeño crackme:
#include<stdio.h>
int main(void){
char str1[20];
printf("Crackme 0x00 Coded by Kr0m\n");
printf("Introduzca password: ");
scanf("%s", str1);
if (strcmp(str1, "666-666") == 0){
printf("Ohu yeyesss Password Correcto!\n");
} else {
printf("ERROR: Password incorrecto!\n");
}
}
Compilamos el software:
Lo probamos:
Crackme 0x00 Coded by Kr0m
Introduzca password: asd
ERROR: Password incorrecto!
Crackme 0x00 Coded by Kr0m
Introduzca password: 666-666
Ohu yeyesss Password Correcto!
Radare nos permite dumpear las cadenas de un binario:
vaddr=0x004007e8 paddr=0x000007e8 ordinal=000 sz=27 len=26 section=.rodata type=a string=Crackme 0x00 Coded by Kr0m
vaddr=0x00400803 paddr=0x00000803 ordinal=001 sz=22 len=21 section=.rodata type=a string=Introduzca password:
vaddr=0x0040081c paddr=0x0000081c ordinal=002 sz=8 len=7 section=.rodata type=a string=666-666
vaddr=0x00400828 paddr=0x00000828 ordinal=003 sz=31 len=30 section=.rodata type=a string=Ohu yeyesss Password Correcto!
vaddr=0x00400847 paddr=0x00000847 ordinal=004 sz=28 len=27 section=.rodata type=a string=ERROR: Password incorrecto!
NOTA: La vieja escuela lo harÃa mediante string :)
Abrimos el binario en modo writeble, vamos al main del programa y mostramos las instrucciones desensambladas:
[0x004005c0]> s sym.main
[0x004006ad]> pd
0x004006c4 bfe8074000 mov edi, str.Crackme_0x00_Coded_by_Kr0m ; "Crackme 0x00 Coded by Kr0m" @ 0x4007e8
0x004006c9 e882feffff call sym.imp.puts
0x00400550(unk) ; sym.imp.puts
0x004006ce bf03084000 mov edi, str.Introduzca_password: ; "Introduzca password: " @ 0x400803
0x004006d3 b800000000 mov eax, 0
0x004006d8 e893feffff call sym.imp.printf
0x00400570() ; sym.imp.printf
0x004006dd 488d45e0 lea rax, qword [rbp - 0x20]
0x004006e1 4889c6 mov rsi, rax
0x004006e4 bf19084000 mov edi, 0x400819 ; "%s" @ 0x400819
0x004006e9 b800000000 mov eax, 0
0x004006ee e8bdfeffff call sym.imp.__isoc99_scanf
0x004005b0() ; sym.imp.__isoc99_scanf
0x004006f3 488d45e0 lea rax, qword [rbp - 0x20]
0x004006f7 be1c084000 mov esi, str.666_666 ; "666-666" @ 0x40081c
0x004006fc 4889c7 mov rdi, rax
0x004006ff e88cfeffff call sym.imp.strcmp
0x00400590() ; sym.imp.strcmp
0x00400704 85c0 test eax, eax
,=< 0x00400706 750c jne 0x400714
| 0x00400708 bf28084000 mov edi, str.Ohu_yeyesss_Password_Correcto_ ; "Ohu yeyesss Password Correcto!" @ 0x400828
| 0x0040070d e83efeffff call sym.imp.puts
| 0x00400550() ; sym.imp.puts
,==< 0x00400712 eb0a jmp 0x40071e
|`-> 0x00400714 bf47084000 mov edi, str.ERROR:_Password_incorrecto_ ; "ERROR: Password incorrecto!" @ 0x400847
| 0x00400719 e832feffff call sym.imp.puts
| 0x00400550() ; sym.imp.puts
`--> 0x0040071e 488b55f8 mov rdx, qword [rbp - 8]
0x00400722 64483314252. xor rdx, qword fs:[0x28]
,===< 0x0040072b 7405 je 0x400732
| 0x0040072d e82efeffff call sym.imp.__stack_chk_fail
| 0x00400560() ; sym.imp.__stack_chk_fail
`---> 0x00400732 c9 leave
Nos interesa modificar el salto condicional: jne 0x400714 para que salte de forma incondicional a la parte del pass correcto: jmp 0x400708, para ello nos movemos a la posición de memoria donde está el salto:
[0x004006ad]> s 0x00400706
Podemos hacer uso de esta chuleta de comandos rápidos, en nuestro caso queremos escribir assembler “a pelo”
[0x00400706]> wa jmp 0x00400708
[0x00400706]> pd
,=< 0x00400706 750c jne 0x400714
,=< 0x00400706 eb00 jmp 0x400708
[0x00400706]> q
Rejecutamos y Baaannnggg!!
Crackme 0x00 Coded by Kr0m
Introduzca password: asd
Ohu yeyesss Password Correcto!
Como veis ha sido realmente sencillo, tan solo modificando una pequeña parte del código hemos conseguido desviar el flujo del programa hacia donde nos interesa ;)