Ingenieria inversa con Radare

«El objetivo es aprender ingeniería inversa con el Framework Radare2».

 

Primero, debemos instalar radare, las instrucciones de instalación se encuentranhttps://github.com/radare/radare2

Si clonamos directamente:

git clone https://github.com/radare/radare2
cd radare2/sys
chmod +x ./install.sh
sudo ./install.sh

Documentación oficial de radare: https://radare.gitbooks.io/radare2book/content/
Información sobre arquitecturas e ingenieria inversa: https://www.aldeid.com/wiki/Category:Reverse-Engineering

Vamos a trabajar sobre la arquitectura x86, con sintaxis intel, debajo podrán encontrar, una imagen con los códigos.

Underc0de - Tabla de codigos Intel

El binario sobre el que trabajaremos es conocido IOLI crackme 0x00 (http://www113.zippyshare.com/v/UTjKTJfG/file.html )

El archivo comprimido contiene un README.TXT que da pistas sobre los niveles, para este nivel utilizaremos la ayuda que nos brinda. Indica que los «strings son nuestras amigas«.

Hay diversas formas de resolver el ejercicio.


Leyendo el readme.txt

El primer método será utilizando strings.

strings crackme0x00

La salida es:

/lib/ld-linux.so.2
__gmon_start__
libc.so.6
printf
strcmp
scanf
_IO_stdin_used
__libc_start_main
GLIBC_2.0
PTRh
IOLI Crackme Level 0x00
Password:
250382 #esta es la contraseña del primer nivel
Invalid Password!
Password OK 🙂
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
GCC: (GNU) 3.4.6 (Gentoo 3.4.6-r2, ssp-3.4.6-1.0, pie-8.7.10)
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rel.dyn
.rel.plt
.init
.text
.fini
.rodata
.eh_frame
.ctors
.dtors
.jcr
.dynamic
.got
.got.plt
.data
.bss
.comment
crtstuff.c
__CTOR_LIST__
__DTOR_LIST__
__JCR_LIST__
completed.1
__do_global_dtors_aux
frame_dummy
__CTOR_END__
__DTOR_END__
__FRAME_END__
__JCR_END__
__do_global_ctors_aux
crackme0x00.c
_GLOBAL_OFFSET_TABLE_
__init_array_end
__init_array_start
_DYNAMIC
data_start
__libc_csu_fini
_start
__gmon_start__
_Jv_RegisterClasses
_fp_hw
_fini
__libc_start_main@@GLIBC_2.0
_IO_stdin_used
scanf@@GLIBC_2.0
__data_start
__dso_handle
__libc_csu_init
printf@@GLIBC_2.0
__bss_start
_end
_edata
strcmp@@GLIBC_2.0
__i686.get_pc_thunk.bx
main
_init

Con listar los strings del binario, conseguimos la contraseña válida.


Rabin2 de Radare .

Con el comando rabin2 llamamos al modulo, con el flag -z buscamos strings en el binario.

Obteniendo el siguiente resultado:

└──╼ $rabin2 -z crackme0x00
vaddr=0x08048568 paddr=0x00000568 ordinal=000 sz=25 len=24 section=.rodata type=ascii string=IOLI Crackme Level 0x00\n
vaddr=0x08048581 paddr=0x00000581 ordinal=001 sz=11 len=10 section=.rodata type=ascii string=Password:
vaddr=0x0804858f paddr=0x0000058f ordinal=002 sz=7 len=6 section=.rodata type=ascii string=250382
vaddr=0x08048596 paddr=0x00000596 ordinal=003 sz=19 len=18 section=.rodata type=ascii string=Invalid Password!\n
vaddr=0x080485a9 paddr=0x000005a9 ordinal=004 sz=16 len=15 section=.rodata type=ascii string=Password OK :)\n

De nuevo, obtenemos la contraseña en texto plano a traves de un analisis estático del binario.


Radare2.

Utilizando exclusivamente radare2, trateremos de obtener la contraseña en texto plano sin debuggear el software y luego veremos como modificar el flujo de ejecucción para que acepte cualquier contraseña como valida.

Ejecutamos radare y analizar completamente el binario:

radare2 crackme0x00 -AA

Dentro de radare con el comando ‘S‘ (sin comillas simples) listamos las secciones del binario. Para más información pueden dirigirse a la documentación oficial de Radare.
El formato del output que da:

  • Numero de sección
  • Offset de inicio
  • Permisos de la seccion
  • Virtual adress (dirección virtual)
  • Size (tamaño)
  • Virtual size (tamaño virtual)
  • Nombre de la sección
[00] . 0x00000154 -r-- va=0x08048154 sz=0x0013 vsz=0x0013 .interp
[01] . 0x00000168 -r-- va=0x08048168 sz=0x0020 vsz=0x0020 .note.ABI_tag
[02] . 0x00000188 -r-- va=0x08048188 sz=0x0030 vsz=0x0030 .hash
[03] . 0x000001b8 -r-- va=0x080481b8 sz=0x0020 vsz=0x0020 .gnu.hash
[04] . 0x000001d8 -r-- va=0x080481d8 sz=0x0070 vsz=0x0070 .dynsym
[05] . 0x00000248 -r-- va=0x08048248 sz=0x0059 vsz=0x0059 .dynstr
[06] . 0x000002a2 -r-- va=0x080482a2 sz=0x000e vsz=0x000e .gnu.version
[07] . 0x000002b0 -r-- va=0x080482b0 sz=0x0020 vsz=0x0020 .gnu.version_r
[08] . 0x000002d0 -r-- va=0x080482d0 sz=0x0008 vsz=0x0008 .rel.dyn
[09] . 0x000002d8 -r-- va=0x080482d8 sz=0x0020 vsz=0x0020 .rel.plt
[10] . 0x000002f8 -r-x va=0x080482f8 sz=0x0017 vsz=0x0017 .init
[11] . 0x00000310 -r-x va=0x08048310 sz=0x0050 vsz=0x0050 .plt
[12] * 0x00000360 -r-x va=0x08048360 sz=0x01e4 vsz=0x01e4 .text
[13] . 0x00000544 -r-x va=0x08048544 sz=0x001a vsz=0x001a .fini
[14] . 0x00000560 -r-- va=0x08048560 sz=0x0059 vsz=0x0059 .rodata
[15] . 0x000005bc -r-- va=0x080485bc sz=0x0004 vsz=0x0004 .eh_frame
[16] . 0x00000f0c -rw- va=0x08049f0c sz=0x0008 vsz=0x0008 .ctors
[17] . 0x00000f14 -rw- va=0x08049f14 sz=0x0008 vsz=0x0008 .dtors
[18] . 0x00000f1c -rw- va=0x08049f1c sz=0x0004 vsz=0x0004 .jcr
[19] . 0x00000f20 -rw- va=0x08049f20 sz=0x00d0 vsz=0x00d0 .dynamic
[20] . 0x00000ff0 -rw- va=0x08049ff0 sz=0x0004 vsz=0x0004 .got
[21] . 0x00000ff4 -rw- va=0x08049ff4 sz=0x001c vsz=0x001c .got.plt
[22] . 0x00001010 -rw- va=0x0804a010 sz=0x000c vsz=0x000c .data
[23] . 0x0000101c -rw- va=0x0804a01c sz=0x0004 vsz=0x0004 .bss
[24] . 0x0000101c ---- va=0x00000000 sz=0x01b9 vsz=0x01b9 .comment
[25] . 0x000011d5 ---- va=0x00000000 sz=0x00db vsz=0x00db .shstrtab
[26] . 0x00001738 ---- va=0x00000000 sz=0x0420 vsz=0x0420 .symtab
[27] . 0x00001b58 ---- va=0x00000000 sz=0x0219 vsz=0x0219 .strtab
[28] * 0x00000000 mr-x va=0x08048000 sz=0x05c0 vsz=0x05c0 LOAD0
[29] . 0x00000f0c mrw- va=0x08049f0c sz=0x0110 vsz=0x0114 LOAD1
[30] . 0x00000000 mrw- va=0x08048000 sz=0x0034 vsz=0x0034 ehdr
[31] . 0x00100000 -rwx va=0x00100000 sz=0xf0000 vsz=0xf0000 esil.ram

Esta información es bastante útil (aunque para esta parte del curso no la usaremos).
Ahora, procedemos a desensamblar la función main.
Antes de eso, ¿por que desensamblamos esa función y no otra?
Como hemos visto en el output de rabin2, como en todos los crackmes hay una zona de chico bueno y chico malo, interesa saber en qué función está localizada la zona de «chico bueno», para ello usaremos el comando /Password que devuelve:

Searching 8 bytes from 0x08048000 to 0x0804a020: 50 61 73 73 77 6f 72 64
Searching 8 bytes in [0x8048000-0x804a020]
hits: 3
0x08048581 hit0_0 .kme Level 0x00Password: %s250382Inv.
0x0804859e hit0_1 .250382Invalid Password!Password OK :.
0x080485a9 hit0_2 .alid Password!Password OK :). #esta es la linea que nos interesa

Una vez que obtenemos la dirección donde esta la zona de ‘chico bueno‘ pedimos a r2 más información sobre esa dirección con el comando axt 0x080485a9

data 0x8048480 mov dword [esp], str.Password_OK_:__n in main; const char * format

Una vez entendido por qué de desensamblar el main, utilizaremos el comando pdf (print disassembled function).

[0x08048360]> pdf@main
            ;-- main:
/ (fcn) main 127
|   main ();
|           ; var int local_18h @ ebp-0x18
|           ; var int local_4h @ esp+0x4
|              ; DATA XREF from 0x08048377 (entry0)
|           0x08048414      55             push ebp
|           0x08048415      89e5           mov ebp, esp
|           0x08048417      83ec28         sub esp, 0x28               ; '('
|           0x0804841a      83e4f0         and esp, 0xfffffff0
|           0x0804841d      b800000000     mov eax, 0
|           0x08048422      83c00f         add eax, 0xf
|           0x08048425      83c00f         add eax, 0xf
|           0x08048428      c1e804         shr eax, 4
|           0x0804842b      c1e004         shl eax, 4
|           0x0804842e      29c4           sub esp, eax
|           0x08048430      c70424688504.  mov dword [esp], str.IOLI_Crackme_Level_0x00_n ; [0x8048568:4]=0x494c4f49 ; "IOLI Crackme Level 0x00\n" ; const char * format
|           0x08048437      e804ffffff     call sym.imp.printf         ; int printf(const char *format)
|           0x0804843c      c70424818504.  mov dword [esp], str.Password: ; hit0_0 ; [0x8048581:4]=0x73736150 ; "Password: " ; const char * format
|           0x08048443      e8f8feffff     call sym.imp.printf         ; int printf(const char *format)
|           0x08048448      8d45e8         lea eax, dword [local_18h]
|           0x0804844b      89442404       mov dword [local_4h], eax
|           0x0804844f      c704248c8504.  mov dword [esp], 0x804858c  ; [0x804858c:4]=0x32007325 ; "%s" ; const char * format
|           0x08048456      e8d5feffff     call sym.imp.scanf          ; int scanf(const char *format)
|           0x0804845b      8d45e8         lea eax, dword [local_18h]
|           0x0804845e      c74424048f85.  mov dword [local_4h], str.250382 ; [0x804858f:4]=0x33303532 ; "250382"
|           0x08048466      890424         mov dword [esp], eax        ; const char * s2
|           0x08048469      e8e2feffff     call sym.imp.strcmp         ; int strcmp(const char *s1, const char *s2)
|           0x0804846e      85c0           test eax, eax
|       ,=< 0x08048470      740e           je 0x8048480
|       |   0x08048472      c70424968504.  mov dword [esp], str.Invalid_Password__n ; [0x8048596:4]=0x61766e49 ; "Invalid Password!\n" ; const char * format
|       |   0x08048479      e8c2feffff     call sym.imp.printf         ; int printf(const char *format)
|      ,==< 0x0804847e      eb0c           jmp 0x804848c
|      |`-> 0x08048480      c70424a98504.  mov dword [esp], str.Password_OK_:__n ; hit0_2 ; [0x80485a9:4]=0x73736150 ; "Password OK :)\n" ; const char * format
|      |    0x08048487      e8b4feffff     call sym.imp.printf         ; int printf(const char *format)
|      |       ; JMP XREF from 0x0804847e (main)
|      `--> 0x0804848c      b800000000     mov eax, 0
|           0x08048491      c9             leave
\           0x08048492      c3             ret

De nuevo, volvemos a encontrar la contraseña en texto plano.

Al llegar a este punto, viene lo divertido, modificar el flujo de ejecucción.


Modificando el flujo de ejecución.

A simple vista el programa compara dos registros (0x08048469, 0x0804846e) uno es la contraseña que valida al ejercicio y el otro, lo que nosotros hayamos introducido.
La parte que interesa está entre la direccián 0x0804846e y 0x08048480, donde está el string «Password OK «.A grandes rasgos lo que el software hace a partir de la direccion  0x0804846e es lo siguiente:

  • Compara la contraseña introducida con la valida.
  • Si son iguales salta a la direccion 0x08048480
  • Caso contrario, llega hasta la direccion 0x0804847e, que ejecuta un salto incondicional a 0x0804848c  y termina con la ejecucción.

Ahora vamos a modificar ese «si es equivalente…» por un salto incondicional
Tenemos que entender el significado de la siguiente línea, mas concretamente de la segunda columna
0x08048470      740e           je 0x8048480

74 = je (jump if equal)
0e = bytes to jump (1 instruccion == 1 byte)

El objetivo es que la linea anterior se convierta en:
0x08048470      eb0e           jmp 0x8048480

Reabrimos el archivo en modo escritura con el comando oo+

[0x08048360]> oo+
File crackme0x00 reopened in read-write mode

A continuación nos movemos a la dirección donde se encuentra el salto con el comando s 0x08048470 e imprimimos las siguientes 8 instrucciones con el comando pd 8.

Si todo ha salido bien, debería aparecer lo siguiente

[0x08048470]> pd 8
|       ,=< 0x08048470      740e           je 0x8048480
|       |   0x08048472      c70424968504.  mov dword [esp], str.Invalid_Password__n ; [0x8048596:4]=0x61766e49 ; "Invalid Password!\n" ; const char * format
|       |   0x08048479      e8c2feffff     call sym.imp.printf         ; int printf(const char *format)
|      ,==< 0x0804847e      eb0c           jmp 0x804848c
|      |`-> 0x08048480      c70424a98504.  mov dword [esp], str.Password_OK_:__n ; hit0_2 ; [0x80485a9:4]=0x73736150 ; "Password OK :)\n" ; const char * format
|      |    0x08048487      e8b4feffff     call sym.imp.printf         ; int printf(const char *format)
|      |       ; JMP XREF from 0x0804847e (main)
|      `--> 0x0804848c      b800000000     mov eax, 0
|           0x08048491      c9             leave

Para modificar la condicion, utilizaremos el comando wx seguido de la nueva instruccion wx 0xeb.

Listamos nuevamente 8 instrucciones y observamos como el flujo de ejecucción se ha modificado

[0x08048470]> pd 8
|       ,=< 0x08048470      eb0e           jmp 0x8048480
|       |   0x08048472      c70424968504.  mov dword [esp], str.Invalid_Password__n ; [0x8048596:4]=0x61766e49 ; "Invalid Password!\n" ; const char * format
|       |   0x08048479      e8c2feffff     call sym.imp.printf         ; int printf(const char *format)
|      ,==< 0x0804847e      eb0c           jmp 0x804848c
|      |`-> 0x08048480      c70424a98504.  mov dword [esp], str.Password_OK_:__n ; hit0_2 ; [0x80485a9:4]=0x73736150 ; "Password OK :)\n" ; const char * format
|      |    0x08048487      e8b4feffff     call sym.imp.printf         ; int printf(const char *format)
|      |       ; JMP XREF from 0x0804847e (main)
|      `--> 0x0804848c      b800000000     mov eax, 0
|           0x08048491      c9             leave

Agradecemos a Sadfud por compartir el artículo de Analisis de Malware con Radare


Posts Relacionados

Comments

comments

Deja una respuesta

Tu email no será publicado. Los campos requeridos estan marcados con *
Puedes usar tags HTML y los atributos: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>