Posts match “ reversing ” tag:

CTFCiberSeg17 - Solución Reversing I

Published on:
Tags: reversing

Este era un reto muy sencillo, simplemente había que ver el código ensamblador de la función "main" y ya tenías el flag:

objdump -M intel -S 48b366dada6f5ef8898058e7713b4ad3
00000000004004b6 <main>:
  4004b6:   55                      push   rbp
  4004b7:   48 89 e5                mov    rbp,rsp
  4004ba:   89 7d cc                mov    DWORD PTR [rbp-0x34],edi
  4004bd:   48 89 75 c0             mov    QWORD PTR [rbp-0x40],rsi
  4004c1:   c6 45 d0 66             mov    BYTE PTR [rbp-0x30],0x66
  4004c5:   c6 45 d1 6c             mov    BYTE PTR [rbp-0x2f],0x6c
  4004c9:   c6 45 d2 61             mov    BYTE PTR [rbp-0x2e],0x61
  4004cd:   c6 45 d3 67             mov    BYTE PTR [rbp-0x2d],0x67
  4004d1:   c6 45 d4 7b             mov    BYTE PTR [rbp-0x2c],0x7b
  4004d5:   c6 45 d5 73             mov    BYTE PTR [rbp-0x2b],0x73
  4004d9:   c6 45 d6 31             mov    BYTE PTR [rbp-0x2a],0x31
  4004dd:   c6 45 d7 5f             mov    BYTE PTR [rbp-0x29],0x5f
  4004e1:   c6 45 d8 6c             mov    BYTE PTR [rbp-0x28],0x6c
  4004e5:   c6 45 d9 30             mov    BYTE PTR [rbp-0x27],0x30
  4004e9:   c6 45 da 5f             mov    BYTE PTR [rbp-0x26],0x5f
  4004ed:   c6 45 db 68             mov    BYTE PTR [rbp-0x25],0x68
  4004f1:   c6 45 dc 34             mov    BYTE PTR [rbp-0x24],0x34
  4004f5:   c6 45 dd 35             mov    BYTE PTR [rbp-0x23],0x35
  4004f9:   c6 45 de 5f             mov    BYTE PTR [rbp-0x22],0x5f
  4004fd:   c6 45 df 68             mov    BYTE PTR [rbp-0x21],0x68
  400501:   c6 45 e0 33             mov    BYTE PTR [rbp-0x20],0x33
  400505:   c6 45 e1 63             mov    BYTE PTR [rbp-0x1f],0x63
  400509:   c6 45 e2 68             mov    BYTE PTR [rbp-0x1e],0x68
  40050d:   c6 45 e3 30             mov    BYTE PTR [rbp-0x1d],0x30
  400511:   c6 45 e4 5f             mov    BYTE PTR [rbp-0x1c],0x5f
  400515:   c6 45 e5 63             mov    BYTE PTR [rbp-0x1b],0x63
  400519:   c6 45 e6 30             mov    BYTE PTR [rbp-0x1a],0x30
  40051d:   c6 45 e7 6e             mov    BYTE PTR [rbp-0x19],0x6e
  400521:   c6 45 e8 5f             mov    BYTE PTR [rbp-0x18],0x5f
  400525:   c6 45 e9 72             mov    BYTE PTR [rbp-0x17],0x72
  400529:   c6 45 ea 34             mov    BYTE PTR [rbp-0x16],0x34
  40052d:   c6 45 eb 64             mov    BYTE PTR [rbp-0x15],0x64
  400531:   c6 45 ec 34             mov    BYTE PTR [rbp-0x14],0x34
  400535:   c6 45 ed 72             mov    BYTE PTR [rbp-0x13],0x72
  400539:   c6 45 ee 33             mov    BYTE PTR [rbp-0x12],0x33
  40053d:   c6 45 ef 5f             mov    BYTE PTR [rbp-0x11],0x5f
  400541:   c6 45 f0 68             mov    BYTE PTR [rbp-0x10],0x68
  400545:   c6 45 f1 31             mov    BYTE PTR [rbp-0xf],0x31
  400549:   c6 45 f2 67             mov    BYTE PTR [rbp-0xe],0x67
  40054d:   c6 45 f3 68             mov    BYTE PTR [rbp-0xd],0x68
  400551:   c6 45 f4 5f             mov    BYTE PTR [rbp-0xc],0x5f
  400555:   c6 45 f5 66             mov    BYTE PTR [rbp-0xb],0x66
  400559:   c6 45 f6 31             mov    BYTE PTR [rbp-0xa],0x31
  40055d:   c6 45 f7 76             mov    BYTE PTR [rbp-0x9],0x76
  400561:   c6 45 f8 33             mov    BYTE PTR [rbp-0x8],0x33
  400565:   c6 45 f9 7d             mov    BYTE PTR [rbp-0x7],0x7d
  400569:   c6 45 fa 00             mov    BYTE PTR [rbp-0x6],0x0
  40056d:   5d                      pop    rbp
  40056e:   c3                      ret    
  40056f:   90                      nop

Cogemos los valores hexadecimales y tenemos:
666c61677b73315f6c305f6834355f68336368305f63306e5f7234643472335f683167685f663176337d
flag{s1_l0_h45_h3ch0_c0n_r4d4r3_h1gh_f1v3}

CTFCiberSeg17 - Solución Reversing III

Published on:
Tags: reversing

Datos del ejecutable que nos proporcionan:

file d1c53e27b0580fdf3e9addffc06359a5 
d1c53e27b0580fdf3e9addffc06359a5: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=53abe7e0d73dd74756eedf50a52b01a5937e1c73, not stripped

Si lo ejecutamos vemos que nos pide una clave para solucionarlo:

./d1c53e27b0580fdf3e9addffc06359a5 
[USAGE] ./d1c53e27b0580fdf3e9addffc06359a5 <password>

Si metemos una contraseña cualquiera:

./d1c53e27b0580fdf3e9addffc06359a5 djkfl
Incorrect password!

Con strings se pueden ver las cadenas:

strings d1c53e27b0580fdf3e9addffc06359a5
Incorrect password!
[USAGE] %s <password>

Pero no hay ninguna cadena de password correcta o algo parecido.
Echemos un vistazo a las funciones más significativas del binario:

objdump -t d1c53e27b0580fdf3e9addffc06359a5 

d1c53e27b0580fdf3e9addffc06359a5:     file format elf32-i386

SYMBOL TABLE:

00000000 l    df *ABS*  00000000              main.c
00000000 l    df *ABS*  00000000              crtstuff.c
08048527 g     F .text  00000062              two
08048589 g     F .text  00000062              one
080484bb g     F .text  0000006c              main
080485eb g     F .text  00000063              check_pass
0804864e g     F .text  0000001b              usage

Parece que check_pass es nuestro candidato ideal ejecutar con el debugger a ver que nos dice.
Desde la función main se hace una llamada a check_pass:

0x080484eb <+48>:    mov    eax,DWORD PTR [eax+0x4]
0x080484ee <+51>: add    eax,0x4
0x080484f1 <+54>: mov    eax,DWORD PTR [eax]
0x080484f3 <+56>: sub    esp,0xc
0x080484f6 <+59>: push   eax
0x080484f7 <+60>: call   0x80485eb <check_pass>

Veamos a analizar que ocurre en check_pass:

disas check_pass
Dump of assembler code for function check_pass:
   0x080485eb <+0>:   push   ebp
   0x080485ec <+1>:   mov    ebp,esp
   0x080485ee <+3>:   sub    esp,0x18
   0x080485f1 <+6>:   sub    esp,0xc
   0x080485f4 <+9>:   push   DWORD PTR [ebp+0x8]
   0x080485f7 <+12>:  call   0x8048390 <strlen@plt>             <--- Calcula la longitud de la password
   0x080485fc <+17>:  add    esp,0x10
   0x080485ff <+20>:  cmp    eax,0x5                          <--- La compara con 5
   0x08048602 <+23>:  ja     0x804860b <check_pass+32>      <--- Tiene que ser mayor igual que 5. Si no sale de la función y envía el mensaje "Incorrect password!"
   0x08048604 <+25>:  mov    eax,0x1
   0x08048609 <+30>:  jmp    0x804864c <check_pass+97>
   0x0804860b <+32>:  sub    esp,0xc
   0x0804860e <+35>:  push   DWORD PTR [ebp+0x8]
   0x08048611 <+38>:  call   0x8048390 <strlen@plt>         <--- Comprueba otra vez la longitud de la cadena
   0x08048616 <+43>:  add    esp,0x10
   0x08048619 <+46>:  mov    DWORD PTR [ebp-0x14],eax
   0x0804861c <+49>:  mov    DWORD PTR [ebp-0x10],0x0
   0x08048623 <+56>:  mov    DWORD PTR [ebp-0xc],0x0
   0x0804862a <+63>:  jmp    0x8048641 <check_pass+86>      <--- Entra en un bucle con i=longitud de la cadena
   0x0804862c <+65>:  mov    edx,DWORD PTR [ebp-0xc]          
   0x0804862f <+68>:  mov    eax,DWORD PTR [ebp+0x8]
   0x08048632 <+71>:  add    eax,edx                          <--- Lo que hace básicamente es sumar el valor ascii de los caracteres de la cadena y los guarda en eax
   0x08048634 <+73>:  movzx  eax,BYTE PTR [eax]
   0x08048637 <+76>:  movsx  eax,al
   0x0804863a <+79>:  add    DWORD PTR [ebp-0x10],eax
   0x0804863d <+82>:  add    DWORD PTR [ebp-0xc],0x1
   0x08048641 <+86>:  mov    eax,DWORD PTR [ebp-0xc]
   0x08048644 <+89>:  cmp    eax,DWORD PTR [ebp-0x14]         <--- Comprueba la condición de salida del bucle
   0x08048647 <+92>:  jl     0x804862c <check_pass+65>      <--- Sigue en el bucle si es menor que la longitud, si no sale
   0x08048649 <+94>:  mov    eax,DWORD PTR [ebp-0x10]
   0x0804864c <+97>:  leave  
   0x0804864d <+98>:  ret    
End of assembler dump.

Volvemos a la función "main" a ver que hace con ese valor devuelto en eax:

   0x080484f7 <+60>: call   0x80485eb <check_pass>
   0x080484fc <+65>:  add    esp,0x10
   0x080484ff <+68>:  test   eax,eax                <--- Hace un AND consigo mismo
   0x08048501 <+70>:  jne    0x804850a <main+79>    <--- Salta si no es igual a sí mismo? WTF!!!! Siempre va a saltar entonces.
   0x08048503 <+72>:  call   0x8048589 <one>
   0x08048508 <+77>:  jmp    0x804851a <main+95>
   0x0804850a <+79>:  sub    esp,0xc
   0x0804850d <+82>:  push   0x8048700
   0x08048512 <+87>:  call   0x8048370 <puts@plt>
   0x08048517 <+92>:  add    esp,0x10
   0x0804851a <+95>:  mov    eax,0x0
   0x0804851f <+100>: mov    ecx,DWORD PTR [ebp-0x4]
   0x08048522 <+103>: leave  
   0x08048523 <+104>: lea    esp,[ecx-0x4]
   0x08048526 <+107>: ret    
End of assembler dump.

Mmm... parece que es una pista falsa, ese valor no se usa para nada y el salto(JNE) siempre se va a realizar pues compara el valor consigo mismo. Parece que no quieren que lleguemos a llamar a la función "one". Habrá que manipular los flags o llamar a la función "one" desde el ret de "check_pass". Para esto último ponemos un breakpoint en ret de "check_pass" y modificamos el valor de $esp por el de la dirección de "one":

br *0x0804864d

Ahora ejecutamos hasta el breakpoint y ponemos el valor de "one" en $esp

r AAAAAA
Starting program: /home/h4x0r/d1c53e27b0580fdf3e9addffc06359a5 AAAAAA
Breakpoint 2, 0x0804864d in check_pass ()
set {int}0xbffff29c = 0x8048589

Seguimos con la ejecucción del programa y nos devuelve una página web antes de provocar un "Segmentation fault":

>>> c
Continuing.
https://www.youtube.com/watch?v=dQw4w9WgXcQb
Program received signal SIGSEGV, Segmentation fault.
0xbffff540 in ?? ()

Estos tíos son unos cachondos, es el video de Rick Astley "Never Gonna Give You Up". Otra pista falsa, pero vamos por el buen camino. ¡Como molan los 80! :)
Había otra función del binario llamada "two", ¿a la segunda será la vencida?. Hacemos lo mismo que antes pero con la dirección de "two" (0x08048527):

>>> set {int}0xbffff29c = 0x08048527
>>> c
Continuing.
https://www.youtube.com/watch?v=PmHyI5vFlGob
Program received signal SIGSEGV, Segmentation fault.
0xbffff540 in ?? ()

¡Sorpresa! Otro video con la flag que buscábamos:

flag{ee1784da5ebc7941f9478e21d36a3e1b} 

He echado de menos una "tercera" función para que se cumpliera el dicho ;-)

That's all folks!

AlexCTF2017 - RE2 C++ is awesome

Published on:
Tags: reversing
Enunciado

They say C++ is complex, prove them wrong!

Solución

Datos del ejecutable que nos proporcionan:

file ./re2 
./re2: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=08fba98083e7c1f7171fd17c82befdfe1dcbcc82, stripped

Si lo ejecutamos nos encontramos con el clásico archivo que hay que pasarle un valor para averiguar el flag:

~/Documents/AlexCTF/reversing# ./re2 
Usage: ./re2 flag

Lo primero de todo es empezar con strings a ver si nos da alguna pista de por donde encaminarnos para resolver el reto.

strings ./re2 
[]A\A]A^A_
L3t_ME_T3ll_Y0u_S0m3th1ng_1mp0rtant_A_{FL4G}_W0nt_b3_3X4ctly_th4t_345y_t0_c4ptur3_H0wev3r_1T_w1ll_b3_C00l_1F_Y0u_g0t_1t
Better luck next time
You should have the flag by now
Usage: 
 flag
;*3$"
zPLR
GCC: (GNU) 6.1.1 20160721 (Red Hat 6.1.1-4)
GCC: (GNU) 6.2.1 20160916 (Red Hat 6.2.1-2)

El punto de entrada según IDA está en la posición 0x0000000000400A60, donde colocaremos un breakpoint. Si seguimos la ejecucción vemos que ejecuta las librerias del sistema para cargar el ejecutable en memoria (__libc_start_main) y al final del todo llama al punto de entrada real de nuestro programa (0x0000000000400B89):

0x00007ffff71c023e __libc_start_main+126 call   rbp
.....
0x0000000000400dec ? call   0x400960
...
0x0000000000400960 ? sub    rsp,0x8
0x0000000000400964 ? mov    rax,QWORD PTR [rip+0x201685]        # 0x601ff0
0x000000000040096b ? test   rax,rax
0x000000000040096e ? je     0x400972
0x0000000000400970 ? call   rax
.....

En vez de pasar por todo este proceso tedioso, se puede ir directamente al strings y ver en IDA desde dónde se llama a la string "Usage" por ejemplo y partir de ahí hacía arriba para ver el punto de entrada y poner un breakpoint ahí:

br *0x0000000000400B89

A partír de aquí lo primero que nos encontramos es con una comprobación del número de argumentos que hemos pasado al programa. En este caso el número de argumento deben ser dos, que corresponden al nombre del programa en sí (argumento 1) y el texto que introduzcamos como intento de flag (argumento 2).

0x0000000000400b92 ? mov    DWORD PTR [rbp-0x64],edi       <--- rdi contiene el número de parámetros que se le han pasado al programa (2)
0x0000000000400b95 ? mov    QWORD PTR [rbp-0x70],rsi
0x0000000000400b99 ? cmp    DWORD PTR [rbp-0x64],0x2    <--- Aquí comprueba que le pases un argumento el programa. Si no es igual a 2 sigue al mensaje de usage (0x400b9f)
0x0000000000400b9d ? je     0x400bd7
0x0000000000400b9f ? mov    rax,QWORD PTR [rbp-0x70]
0x0000000000400ba3 ? mov    rbx,QWORD PTR [rax]
0x0000000000400ba6 ? mov    esi,0x400f09                <--- Offset de  "Usage: "
0x0000000000400bab ? mov    edi,0x602140                <--- Offset de std::cout
0x0000000000400bb0 ? call   0x4009d0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>

Siguiendo la ejecución del programa, reserva espacio y coloca nuestro argumento en memoria. Una vez hecho esto tenemos el bucle principal del programa. Lo que va a hacer en este bucle básicamente es ir comparando la cadena introducida por nosotros con los caracteres del flag. Lo importante a destacar es que los caracteres del flag no va a estar correlativos, sino se hubieran podido ver con el comando strings, sino que van a estar camuflados dentro de la memoria y vamos a acceder a ellos utilizando un array de offsets. Veamos un ejemplo de manera práctica:

Caracteres de nuestro flag desordenados en memoria:

x/64xw 0x400e58
0x400e58:   0x5f74334c  0x545f454d  0x5f6c6c33  0x5f753059
0x400e68:   0x336d3053  0x6e316874  0x6d315f67  0x74723070
0x400e78:   0x5f746e61  0x467b5f41  0x7d47344c  0x6e30575f
0x400e88:   0x33625f74  0x3458335f  0x796c7463  0x3468745f
0x400e98:   0x34335f74  0x745f7935  0x34635f30  0x72757470
0x400ea8:   0x30485f33  0x33766577  0x54315f72  0x6c31775f
0x400eb8:   0x33625f6c  0x3030435f  0x46315f6c  0x7530595f
0x400ec8:   0x7430675f  0x0074315f  0x74746542  0x6c207265
0x400ed8:   0x206b6375  0x7478656e  0x6d697420  0x00000a65
0x400ee8:   0x20756f59  0x756f6873  0x6820646c  0x20657661
0x400ef8:   0x20656874  0x67616c66  0x20796220  0x0a776f6e
0x400f08:   0x61735500  0x203a6567  0x6c662000  0x000a6761
0x400f18:   0x3b031b01  0x00000074  0x0000000d  0xfffffa68
0x400f28:   0x000000c0  0xfffffb48  0x00000090  0xfffffc3e
0x400f38:   0x000000e8  0xfffffc5b  0x00000108  0xfffffc71
0x400f48:   0x00000148  0xfffffdd2  0x000001f0  0xfffffe10

Matriz de offsets en memoria:

0x6020c0:  0x00000024  0x00000000  0x00000005  0x00000036
0x6020d0:   0x00000065  0x00000007  0x00000027  0x00000026
0x6020e0:   0x0000002d  0x00000001  0x00000003  0x00000000
0x6020f0:   0x0000000d  0x00000056  0x00000001  0x00000003
0x602100:   0x00000065  0x00000003  0x0000002d  0x00000016
0x602110:   0x00000002  0x00000015  0x00000003  0x00000065
0x602120:   0x00000000  0x00000029  0x00000044  0x00000044
0x602130:   0x00000001  0x00000044  0x0000002b  0x00000000

Si queremos acceder al primer caracter del flag (indice=0), primero habrá que irse a la posición del array de offsets(0x6020c0) + indice(0), eso corresponde al valor 0x24. Este valor será nuestro offset para hallar el primer carácter del flag en memoria, en este caso 0x400e58 + 0x24 = 0x400e7c, que corresponde casualmente a la letra "A". Y como nuestro formato de flag es ALEXCTF{loquesea} tiene pinta de que vamos bien encaminados. Si seguimos el mismo procedimiento, los siguientes caracteres serían: 4c 45 58 43 54 46 7b 57 33 5f 4c 30 76 33 5f 43 5f 57 31 74 68 5f 43 4c 34 35 35 33 35 7d. Que se corresponde con el flag que buscábamos: ALEXCTF{W3_L0v3_C_W1th_CL45535}

Aquí se puede ver el mismo procedimiento en código ensamblador:

0x0000000000400c24 ? lea    rax,[rbp-0x50]         <--- Inicio del bucle
0x0000000000400c28 ? mov    rdi,rax
0x0000000000400c2b ? call   0x4009f0            <--- Función que nos da la posición del siguiente carácter de la cadena introducida por nosotros como segundo parámetro
0x0000000000400c30 ? mov    QWORD PTR [rbp-0x20],rax
0x0000000000400c34 ? lea    rdx,[rbp-0x20]
0x0000000000400c38 ? lea    rax,[rbp-0x60]
0x0000000000400c3c ? mov    rsi,rdx
0x0000000000400c3f ? mov    rdi,rax             
0x0000000000400c42 ? call   0x400d3d            <--- Función que nos determina si ya hemos leído todos los caracteres de la cadena introducida por nosotros (índice del bucle)
0x0000000000400c47 ? test   al,al               <--- Si es 0, ya hemos leído todo y salimos.
0x0000000000400c49 ? je     0x400c95            <--- Salto fuera del bucle si es igual a 0
0x0000000000400c4b ? lea    rax,[rbp-0x60]
0x0000000000400c4f ? mov    rdi,rax
0x0000000000400c52 ? call   0x400d9a            <--- Función que nos devuelve la posición de memoria del siguiente cáracter introducido por nosotros
0x0000000000400c57 ? movzx  edx,BYTE PTR [rax]  <--- Mueve a edx el siguiente caracter de la cadena introducida por nosotros
0x0000000000400c5a ? mov    rcx,QWORD PTR [rip+0x20143f]        <--- Posición de memoria donde están guardados los caracteres del flag
0x0000000000400c61 ? mov    eax,DWORD PTR [rbp-0x14]    <--- Recupera el índice del bucle que se guarda en rbp-0x14 (variable local)
0x0000000000400c64 ? cdqe   
0x0000000000400c66 ? mov    eax,DWORD PTR [rax*4+0x6020c0]  <--- Matriz de offset en memoria con los valores que vamos a ir tomando para leer los caracteres del flag
0x0000000000400c6d ? cdqe   
0x0000000000400c6f ? add    rax,rcx             <--- Posición correspondiente al siguiente caracter de la flag, calculado según la posición fija y el offset tomado
0x0000000000400c72 ? movzx  eax,BYTE PTR [rax]
0x0000000000400c75 ? cmp    dl,al
0x0000000000400c77 ? setne  al
0x0000000000400c7a ? test   al,al
0x0000000000400c7c ? je     0x400c83
0x0000000000400c7e ? call   0x400b56
0x0000000000400c83 ? add    DWORD PTR [rbp-0x14],0x1    <--- Aumenta en uno el índice
0x0000000000400c87 ? lea    rax,[rbp-0x60]
0x0000000000400c8b ? mov    rdi,rax
0x0000000000400c8e ? call   0x400d7a            <--- Función que aumenta en 1 la dirección de la cadena introducida por nosotros (para apuntar al siguiente caracter)
0x0000000000400c93 ? jmp    0x400c24            <--- Vuelve al inicio del bucle

0x0000000000400c95 ? call   0x400b73
0x0000000000400c9a ? mov    ebx,0x0
0x0000000000400c9f ? lea    rax,[rbp-0x50]

Eso es todo y recordad que estas posiciones de memoria serán diferentes en nuestro ordenador. Si queréis ver todo el código completo en ensamblador, objdump es vuestro amigo:

objdump -M intel -S ./re2

Fwhibbit CTF 2017 - Mayday Mayday

Published on:
Tags: reversing

Enunciado

Mayday Mayday

Points: 150
Country: South Africa

Attatchment: https://mega.nz/#!HpYxUIIZ!TjDhMDCvazuay1Cats4zObHuRmixGhVa7Sy0-5hnLTg

Description: Hi aspirant, we lost all our carrots, for this reason we need your skills so please... try to steal the private bank of carrots for us. The time begins...NOW!

Solución

La verdad es que no sé para que he publicado este writeup porque es el clásico ejemplo de crackme sencillo y sin mucha sustancia. Pero luego un amigo me ha convencido para ponerlo aunque sea sólo para configurar las opciones del gdb-peda :-)
Para quien no lo sepa, 'peda' es un script de python que te ayuda a no "quemarte tanto las pestañas" en gdb. Te aporta un entorno visual más amigable y funcionalidades extra no incluídas en gdb. Para quien quiera conocer más:
https://github.com/longld/peda
De todas formas yo suelo usar 'voltron', que a diferencia de peda te permite redirigir la salida a otros terminales:
https://github.com/snare/voltron

Estas son las opciones que suelo usar en .gdbinit:

set history save
set confirm off
set verbose off
set print pretty on
set print array off
set print array-indexes on
set python print-stack full
set disassembly-flavor intel
source ~/peda/peda.py

Comencemos por lo básico respecto al binario:

file fwhibbit-150 
fwhibbit-150: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=bd3ef4bb0664dc621aefcbda1f311aee8a832a9e, not stripped

Tenemos un binario que al ejecutarse nos pide una contraseña... Sí, es lo que estáis pensando, lo único que vamos a hacer es poner un breakpoint después de introducir nuestro chorizo correspondiente de AAAA's y ver con que cadena compara la nuestra. Así de sencillo, tendremos la contraseña con tan sólo pasar de hexadecimal a ascii el contenido de memoria.

Lo primero es poner el breakpoint después de introducir nuestra contraseña, para ello buscaremos alguna función tipo scanf:

   0x00000000004008eb <+510>:    lea    rax,[rbp-0x90]
   0x00000000004008f2 <+517>: mov    rsi,rax
   0x00000000004008f5 <+520>: mov    edi,0x400e9d
   0x00000000004008fa <+525>: mov    eax,0x0
=> 0x00000000004008ff <+530>:  call   0x4005f0 <__isoc99_scanf@plt>
   0x0000000000400904 <+535>: lea    rdx,[rbp-0x90]
   0x000000000040090b <+542>: lea    rax,[rbp-0xe0]
   0x0000000000400912 <+549>: mov    rsi,rdx
   0x0000000000400915 <+552>: mov    rdi,rax
=> 0x0000000000400918 <+555>:  call   0x4005d0 <strcmp@plt>
   0x000000000040091d <+560>: test   eax,eax

Bueno, viendo el ensamblador nos saltamos ese breakpoint vamos directamente a lo que nos interesa, la función strcmp:

br *0x0000000000400918

La función strcmp recibe como parámetros el origen (registro $rsi) y el destino (registro $rdi), por lo tanto con mirar esas posciones de memoria tendremos por un lado nuestra entrada en la posición que apunta $rsi y por otro lado la contraseña que está en la posición que apunta $rdi:

x/32xw $rsi
0x7fffffffe0f0: 0x41414141  0x00000000  0xf7de7a44  0x00007fff

x/32xw $rdi
0x7fffffffe0a0: 0x6c6c756e  0x00000000  0xffffe001  0x00007fff

Pasando '0x6c6c756e' a ascii: 'llun' ya tenemos la contraseña. Ahora ejecutamos y nos da la flag:

./fwhibbit-150 
||====================================================================||
||//$//////////////////////////////////////////////////////////////$//||
||(100)================| RESERVE BANK OF FWHIBBIT |==============(100)||
||//$//        ~         '------========--------'                //$//||
||<< /        /$/              // ____ //                         / >>||
||>>|        //L//            // ///..) //              XXXX       |<<||
||<<|        // //           || <||  >}  ||                        |>>||
||>>|         /$/            ||  $$ --/  ||          XXXXXXXXX     |<<||
||<<|     Free to Use        *||  ||_/  //*                        |>>||
||>>|                         *||/___|_//*                         |<<||
||<</      Rating: E     _____/ FWHIBBIT /________    XX XXXXX     />>||
||//$/                 ~|  REPUBLIC OF FWHIBBIT   |~              /$//||
||(100)===================  ONE HUNDRED CARROTS =================(100)||
||//$//////////////////////////////////////////////////////////////$//||
||====================================================================||
Fwhibbit Control Access
Enter our password:
null
You Win
fwhibbit{fwhibbit_reversing_rul3s}

Un reto ideal para lammers como nosotros :-)

Fwhibbit CTF 2017 - How Many Rabbits

Published on:
Tags: reversing

Enunciado

How Many Rabbits

Points: 200

Country: Morroco

Attatchment: https://mega.nz/#!N4dDVSYY!mcH-FyRD9cwCuL8i3OFy_1zrA55djoLk9s2Qd7-hPuo

Description: We need the information in this binary password protected...can you help us?

Solución

Este binario es muy parecido al de 150 puntos, pero aquí añadía la dificultad de encontrar el 'entrypoint' del programa para poder insertar un breakpoint en ese punto para iniciar el reversing. Veamos como se hace con gdb:

gdb-peda$ set stop-on-solib-events 1
gdb-peda$ info target
Symbols from "/root/Documents/FwhibbitCTF/Reversing/rabbits-200".
Local exec file:
    `/home/pericodelospalotes/FwhibbitCTF/Reversing/rabbits-200', file type elf64-x86-64.
    Entry point: 0x555555554de0

Aquí vemos guardar en el stack frame algunos valores sospechosos

 b31:  mov    ecx,0x6f77                   <== Cadena 'ow'
 b36:   mov    eax,0x6867                   <== Cadena 'hg'
 b3b:   mov    edx,0x7774                   <== Cadena 'wt'
 b40:   mov    esi,0x7774                   <== Cadena 'wt'
 b45:   sub    rsp,0x220
 b4c:   mov    WORD PTR [rsp+0x30],cx
 b51:   mov    WORD PTR [rsp+0x10],ax
 b56:   lea    rbx,[rsp+0x120]
 b5e:   mov    WORD PTR [rsp+0x20],dx
 b63:   mov    WORD PTR [rsp+0xf0],si
 b6b:   mov    DWORD PTR [rsp+0x50],0x
 b73:   mov    DWORD PTR [rsp+0x60],0x357431    <== Cadena '5t1'
 b7b:   mov    BYTE PTR [rsp+0x12],0x0
 b80:   mov    DWORD PTR [rsp+0x70],0x77745f    <== Cadena 'wt_'
 b88:   mov    DWORD PTR [rsp+0x80],0x79746e    <== Cadena 'ytn'
 b93:   mov    BYTE PTR [rsp+0x22],0x0
 b98:   mov    DWORD PTR [rsp+0x90],0x725f30    <== Cadena 'r_0'
 ba3:   mov    DWORD PTR [rsp+0xa0],0x72615f    <== Cadena 'ra_'
 bae:   mov    BYTE PTR [rsp+0x32],0x0
 bb3:   mov    DWORD PTR [rsp+0xb0],0x746e65    <== Cadena 'tne'
 bbe:   mov    DWORD PTR [rsp+0xf2],0x746e65    <== Cadena 'tne'
 bc9:   call    sub_F10             <== Función que hace el dibujo en ASCII del conejo

Si seguimos la ejecución vemos como llama a una función en C++ que recoge nuestro valor por pantalla (¿cin?) y más abajo la llamada a strcmp para comparar las cadenas.
La cadena con la que la va a cotejar se construye dinámicamente con los valores almacenados previamente. Al final $rsi va a apuntar a la cadena completa reconstruída:

=> 0x555555554c0b:  call   0x555555554ad0 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>
   0x555555554c10:  mov    eax,DWORD PTR [rsp+0x50]
   0x555555554c14:  lea    rsi,[rsp+0xf0]
   0x555555554c1c:  mov    rdi,rbx
   0x555555554c1f:  mov    DWORD PTR [rsp+0xf5],eax
   0x555555554c26:  movzx  eax,WORD PTR [rsp+0x30]
   0x555555554c2b:  mov    WORD PTR [rsp+0xf8],ax
   0x555555554c33:  movzx  eax,BYTE PTR [rsp+0x32]
   0x555555554c38:  mov    BYTE PTR [rsp+0xfa],al
=> 0x555555554c3f:   call   0x555555554af0 <strcmp@plt>
   0x555555554c44:  test   eax,eax

Esto es los valores que van cogiendo los registros hasta completar la palabra:

RAX: 0x6f77 ('wo')
RAX: 0x745f79 ('y_t')
RSI: 0x7fffffffe050 --> 0x746e657774 ('twent')
....
RSI: 0x7fffffffe050 ("twenty_two")
RDI: 0x7fffffffe080 --> 0x41414141 ('AAAA')

Y aquí como se ve en memoria:

gdb-peda$ x/32xw $rsi
0x7fffffffe050: 0x6e657774  0x745f7974  0x00006f77  0x00000000

Ejecutamos y nos devuelve la flag:

./rabbits-200 

        .--``..---.                
         .````--:ohdmNms/`         
          -:/+++/-.:smNd+          
       ```..--:ohmNNdhh.           
     `-. `.``.-+sosshd.         :. 
   -os--/sosdmmNNMMNy         .+// 
  :h+.+hNNMMMNNNMMNm/      `/yNN.` 
 .do/oNNMMMMMmohs+:`    .+hNMMMM-` 
 `yohNhNNNMh-           dosNMMMmo- 
  -mN+hMMMy             .smNMNdd/+`
   yN.hMMh               +NMMNmhds:
   +N//m+                 .osshyho 
  ..smhh                           
   ::oNmy-                         
      .//yhs/:`                    
          :ymNN/                   
         .-+shdho.                 
             `.--..` '''   

 one rabbit, two rabbit... 
 > twenty_two

fwhibbit{Tw3nty_tw0_r4bb1t5_ar3_en0ugh} 

Fwhibbit CTF 2017 - Bomb

Published on:
Tags: reversing

Enunciado

Points: 500

Country: Kazakhstan

Attatchment: https://mega.nz/#!1tsFgIAR!JhnKO62d5jAcGvXM4pYsxbF5mMEyNz07UggP_e8lAEM

Description: An evil rabbit has installed a nuclear bomb in the building and only a competitor like you, can defuse it and avoid its self-destruction. Be patient but please..DEFUSE THE BOMB!

Solución

En este caso nos daba un binario que te pedía un pin para desactivar una bomba (Jack Bauer powa):

file bomb-500 
bomb-500: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=a3990f9a0782759b97fc39aa38540656adc497c2, stripped

./bomb-500 

        .--``..---.                
         .````--:ohdmNms/`         
          -:/+++/-.:smNd+          
       ```..--:ohmNNdhh.           
     `-. `.``.-+sosshd.         :. 
   -os--/sosdmmNNMMNy         .+// 
  :h+.+hNNMMMNNNMMNm/      `/yNN.` 
 .do/oNNMMMMMmohs+:`    .+hNMMMM-` 
 `yohNhNNNMh-           dosNMMMmo- 
  -mN+hMMMy             .smNMNdd/+`
   yN.hMMh               +NMMNmhds:
   +N//m+                 .osshyho 
  ..smhh                           
   ::oNmy-                         
      .//yhs/:`                    
          :ymNN/                   
         .-+shdho.                 
             `.--..` '''   
   _______ ___ 
  | 7 | 8 | 9 |
  |___|___|___|
  | 4 | 5 | 6 |
  |___|___|___|
  | 1 | 2 | 3 |
  |___|___|___|
  | 0 | enter |
  |___|_______|
  Deactivation Code 
  > 1111 
BOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOM!
       _.-^^---....,,--       
   _--                  --_   
  <                        >) 
  |                         | 
   ._                   _./  
      ```--. . , ; .--'''     
            | |   |           
         .-=||  | |=-.        
         `-=#$%&%$#=-'        
            | ;  :|           
   _____.,-#%&$@%#&#~,._____  

Con un strings no te mostraba nada interesante, había que ponerse a bucear en el desensamblado para ver si encontrabamos algo. Lo primero es averiguar el punto de entrada:

>>> set stop-on-solib-events 1
>>> info target
Symbols from "/root/Documents/FwhibbitCTF/reversing/bomb-500".
Local exec file:
    `/home/pericodelospalotes/FwhibbitCTF/reversing/bomb-500', file type elf64-x86-64.
    Entry point: 0x5555555555d0

A partir de ese punto de entrada llegamos a la función main:

br *0x000055555555591e

Esta es la primera parte del código donde metemos el pin:

191e:  push   rbp
191f:   mov    rbp,rsp
1922:   push   r12
1924:   push   rbx
1925:   sub    rsp,0xd0
192c:   mov    DWORD PTR [rbp-0xd4],edi
1932:   mov    QWORD PTR [rbp-0xe0],rsi
1939:   call   1700 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x150>  <== Función que pinta el dibujito ASCII
193e:   lea    rsi,[rip+0x102b]        # 2970 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x13c0>
1945:   lea    rdi,[rip+0x202914]        # 204260 <_ZSt4cout@@GLIBCXX_3.4>
194c:   call   1470 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
1951:   mov    rdx,rax
1954:   mov    rax,QWORD PTR [rip+0x20269d]        # 203ff8 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x202a48>
195b:   mov    rsi,rax
195e:   mov    rdi,rdx
1961:   call   1510 <_ZNSolsEPFRSoS_E@plt>
1966:   lea    rsi,[rip+0x1018]        # 2985 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x13d5>
196d:   mov    rdi,rax
1970:   call   1470 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
1975:   lea    rax,[rbp-0x90]
197c:   mov    rsi,rax
197f:   lea    rdi,[rip+0x2027ba]        # 204140 <_ZSt3cin@@GLIBCXX_3.4>
1986:   call   14d0 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>   <== Función que lee el nº pin y lo almacena
198b:   lea    rax,[rbp-0x90]
1992:   mov    rdi,rax
1995:   call   1480 <strlen@plt>  <== Función que comprueba el tamaño del pin
199a:   cmp    rax,0x8            <== El PIN tiene que tener 8 números
199e:   je     19a5 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x3f5>

Básicamente lo que hace todo esto es reservar memoria, guardar el pin introducido en memoria y comprobar que tenga 8 dígitos. En este caso he puesto de pin 11111111 que lo almacena en esta posición de memoria:

0x7fffffffe0f0:    0x31313131  0x31313131

Una vez superado que el pin sea de 8 dígitos, hay un bucle en el que recorre cada dígito del pin para comprobar que sea un número y no cualquier otro carácter inválido:

19a5:  mov    DWORD PTR [rbp-0x14],0x0
19ac:   cmp    DWORD PTR [rbp-0x14],0x7     <== Inicio del bucle (0..7)
19b0:   jg     19df <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x42f>  <== Si es mayor que 7 se sale del bucle
19b2:   mov    eax,DWORD PTR [rbp-0x14]
19b5:   cdqe   
19b7:   movzx  eax,BYTE PTR [rbp+rax*1-0x90]    <== Coge el siguiente dígito del pin (eax=pin[i])
19bf:   cmp    al,0x39          <== Comprueba que el valor ASCII no sea mayor que 0x39 ('9')
19c1:   jg     19d4 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x424> <== Nos manda al dibujito de la bomba si no se cumple la condición anterior
19c3:   mov    eax,DWORD PTR [rbp-0x14]     
19c6:   cdqe   
19c8:   movzx  eax,BYTE PTR [rbp+rax*1-0x90]    <== Vuelve a coger el mismo pin[i]
19d0:   cmp    al,0x2f          <== Comprueba que el valor ASCII sea mayor que 0x2f ('/')
19d2:   jg     19d9 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x429>
19d4:   call   184b <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x29b>  <== Nos manda al dibujito de la bomba y sale del programa
19d9:   add    DWORD PTR [rbp-0x14],0x1 <== i=i+1
19dd:   jmp    19ac <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x3fc>

Una vez hechas las comprobaciones vamos a la pimera parte del meollo. Lo primero que hace es reservar memoria para copiar un patrón de caracteres. Este patrón de caracteres (le llamaremos Patron1) se usa para ofuscar (con operaciones lógicas) los valores del pin correcto.

19f4:  call   1560 <_ZNSaIcEC1Ev@plt>
19f9:   lea    rdx,[rbp-0x82]
1a00:   lea    rax,[rbp-0xb0]
1a07:   lea    rsi,[rip+0xf7c]        # 298a <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x13da>    <== Patron1 de caracteres que nos va a servir para ofuscar el pin (al menos 4 de los dígitos).
1a0e:   mov    rdi,rax
1a11:   call   1550 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_@plt>    <== Hace una copia del patron1 en otra posición de memoria donde va a ir guardando el resultado de las operaciones.
1a16:   lea    rax,[rbp-0x82]
1a1d:   mov    rdi,rax
1a20:   call   14e0 <_ZNSaIcED1Ev@plt>

Tendríamos el patron1 original y una copia del mismo, que vamos a usar para operar con nuestro pin introducido, en la siguientes posiciones de memoria respectivamente (va a ocupar 0x12 bytes):

>>> x/32xw 0x000055555555698a
0x55555555698a: 0x83b5fcf7  0x8983a781  0x9ebffdbd  0xf49aa6fa
0x55555555699a: 0x0000a284  0xa8bb0000  0xcad784ea  0xa9ee8080
>>> x/32xw 0x55555576b440
0x55555576b440: 0x83b5fcf7  0x8983a781  0x9ebffdbd  0xf49aa6fa
0x55555576b450: 0x0000a284  0x00000000  0x0001fbb1  0x00000000

Veamos las operaciones que hace el algoritmo en este bucle entre el pin introducido por nosotros y el patron1:

1a25:  mov    QWORD PTR [rbp-0x20],0x0     <== i=0 (Asignación inicial, esta instrucción está fuera del bucle
1a2d:   lea    rax,[rbp-0xb0]           <== Inicio del bucle. Sitio ideal para poner un breakpoint.
1a34:   mov    rdi,rax              <== Mueve la posición de memoria de la copia del patron1 a $rdi
1a37:   call   1420 <_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6lengthEv@plt> <== Calcula tamaño de copia_patron1
1a3c:   cmp    rax,QWORD PTR [rbp-0x20]     <== Comprueba si i=18 (longitud de copia_patron1)
1a40:   seta   al
1a43:   test   al,al
1a45:   je     1a97 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x4e7>  <== Sale del bucle si i=18
1a47:   mov    rdx,QWORD PTR [rbp-0x20]     <== Vuelve a traer valor de i
1a4b:   lea    rax,[rbp-0xb0]           <== Valor de la posición de memoria que apunta a la copia del patron1
1a52:   mov    rsi,rdx              <== Arg1=i
1a55:   mov    rdi,rax              <== Arg2=&copia_patron1
1a58:   call   1590 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt>   <== Calcula la posición de memoria de la copia del patron1 + i. Devuelve &copia_patron1+i. Es la forma de recorrer el patron1 byte a byte
1a5d:   mov    rbx,rax              <== Guarda en $rbx &copia_patron1+i. $rbx=&copia_patron+i
1a60:   movzx  r12d,BYTE PTR [rbx]      <== Guarda en r12 el valor copia_patron[i] 
1a64:   mov    rax,QWORD PTR [rbp-0x30]     <== Trae la posición de memoria de nuestro pin introducido (&pin_introducido)
1a68:   mov    rdi,rax
1a6b:   call   1480 <strlen@plt>      <== Trae la longitud del pin (long=8)
1a70:   mov    rcx,rax              <== $rcx=8
1a73:   mov    rax,QWORD PTR [rbp-0x20]     <== $rax=i
1a77:   mov    edx,0x0              <== Deja vacio $edx para recoger el resto de la siguiente división
1a7c:   div    rcx              <== $rax=$rax/$rcx, el resto en $edx. j = i mod 8
1a7f:   mov    rax,QWORD PTR [rbp-0x30]     <== $rax = &pin_introducido
1a83:   add    rax,rdx              <== &pin_introducido=&pin_introducido+j
1a86:   movzx  eax,BYTE PTR [rax]       <== Trae el valor pin[j] a $eax
1a89:   not    eax              <== Not lógico del valor de pin[j]
1a8b:   xor    eax,r12d             <== Xor lógico entre Not(pin[j]) y patron[i]. NuevoValor= Not(pin[j]) XOR patron[i]
1a8e:   mov    BYTE PTR [rbx],al        <== Guarda nuevo valor en &copia_patron1+i. patron[i]=NuevoValor
1a90:   add    QWORD PTR [rbp-0x20],0x1     <== i=i+1
1a95:   jmp    1a2d <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x47d>

Ya se que están un poco liosos los comentarios en el código, pero básicamente lo que hace este bucle es ofuscar el pin introducido en el patron1 realizando las siguientes operaciones:

for (i=0; i < 18; i = i + 1) {
    j = i mod 8;
    copia_patron1[i] = NOT(pin[j]) XOR copia_patron1[i];
}

Así quedaría el patron1 después de realizar el bucle anterior con el pin 12345678:

0x55555576b440:    0x4b783539  0x44486d49  0x56723473  0x39516c32
0x55555576b450: 0x00006b4a  0x00000000  0x00000031  0x00000000

Lo que sigue a continuación en el código es una serie de comprobaciones para ver si ciertos valores del pin introducido son correctos. Concretamente comprueba cuatro dígitos del pin, las posiciones 0x1, 0x8, 0xb, 0xe del patron1_codificado. Estas posiciones corresponden con los dígitos que se encuentran en las posiciones 1,0,3 y 6 del pin respectivamente. Tener en cuenta que la posición de cada dígito del pin se calcula haciendo el mod 8 con las posiciones del patron_codificado:

1a97:  48 8d 85 50 ff ff ff    lea    rax,[rbp-0xb0]
1a9e:   48 89 c7                mov    rdi,rax
1aa1:   e8 8a fa ff ff          call   1530 <_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv@plt>
1aa6:   48 89 45 c8             mov    QWORD PTR [rbp-0x38],rax
1aaa:   48 8b 45 c8             mov    rax,QWORD PTR [rbp-0x38]
1aae:   48 83 c0 01             add    rax,0x1          <== Comprueba la posición 0x1 del patron1_codificado
1ab2:   0f b6 00                movzx  eax,BYTE PTR [rax]
1ab5:   3c 35                   cmp    al,0x35          <== La posicón 1 (0x1 mod 8) del pin está codificado con este valor 
1ab7:   75 2d                   jne    1ae6 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x536>
1ab9:   48 8b 45 c8             mov    rax,QWORD PTR [rbp-0x38]
1abd:   48 83 c0 08             add    rax,0x8          <== Comprueba la posición 0x8 del patron1_codificado
1ac1:   0f b6 00                movzx  eax,BYTE PTR [rax]
1ac4:   3c 73                   cmp    al,0x73          <== La posicón 0 (0x8 mod 8) del pin está codificado con este valor
1ac6:   75 1e                   jne    1ae6 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x536>
1ac8:   48 8b 45 c8             mov    rax,QWORD PTR [rbp-0x38]
1acc:   48 83 c0 0b             add    rax,0xb          <== Comprueba la posición 0xb del patron1_codificado
1ad0:   0f b6 00                movzx  eax,BYTE PTR [rax]
1ad3:   3c 56                   cmp    al,0x56          <== La posicón 3 (0xb mod 8) del pin está codificado con este valor
1ad5:   75 0f                   jne    1ae6 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x536>
1ad7:   48 8b 45 c8             mov    rax,QWORD PTR [rbp-0x38]
1adb:   48 83 c0 0e             add    rax,0xe          <== Comprueba la posición 0xe del patron1_codificado
1adf:   0f b6 00                movzx  eax,BYTE PTR [rax]
1ae2:   3c 51                   cmp    al,0x51          <== La posicón 6 (0xe mod 8) del pin está codificado con este valor
1ae4:   74 05                   je     1aeb <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x53b>
1ae6:   e8 60 fd ff ff          call   184b <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x29b>

De esta manera ya podemos saber esos cuatro valores del pin haciendo las operaciones inversas del bucle anterior, por ejemplo para la posición 1, el valor codificado tiene que ser 0x35. Por lo tanto:

patron_original[1]=0xfc; pin[1] = not(0xffffff35 XOR 0xfc) = 0x36 (6)
patron_original[8]=0xbd; pin[0] = not(0xffffff73 XOR 0xbc) = 0x31 (1)
patron_original[11]=0x9e; pin[3] = not(0xffffff56 XOR 0x9e) = 0x37 (7)
patron_original[14]=0x9a; pin[6] = not(0xffffff51 XOR 0x9a) = 0x34 (4)

Ya tenemos los siguientes valores del pin: 16X7XX4X

Aquí se podría haber hecho fuerza bruta con un diccionario de los cuatro dígitos que faltan y pasarlo al programa, pero yo decidí seguir adelante y ver como se calculaban el resto de dígitos del pin que faltaban.
Lo que vemos a continuación es algo muy parecido a lo que hemos visto en el primer bucle que codificaba nuestro pin con el patron1. La diferencia es que este bucle codifica el patron1_codificado de 0x12 bytes(18) con un patron2 de 0x21 bytes(33). Aquí se puede ver en el código como se accede al patron2 y se hace una copia.

1aeb:  lea    rax,[rbp-0x81]
1af2:   mov    rdi,rax
1af5:   call   1560 <_ZNSaIcEC1Ev@plt>
1afa:   lea    rdx,[rbp-0x81]
1b01:   lea    rax,[rbp-0xd0]
1b08:   lea    rsi,[rip+0xe91]        # 29a0 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x13f0>    <== Patron2 de caracteres que nos va a servir para codificar otra vez el patron1_codificado.
1b0f:   mov    rdi,rax
1b12:   call   1550 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEC1EPKcRKS3_@plt>    <== Hace una copia del patron2 en otra posición de memoria, done va ir guando el resultado de las operaciones.  
1b17:   lea    rax,[rbp-0x81]
1b1e:   mov    rdi,rax
1b21:   call   14e0 <_ZNSaIcED1Ev@plt>

Luego tendríamos un bucle muy similar al primero pero con diferentes valores de i y de j al ser patron2 y patron1_codificado de diferente tamaños, en este caso j=i mod 18.

1b26:  mov    QWORD PTR [rbp-0x28],0x0
1b2e:   lea    rax,[rbp-0xd0]
1b35:   mov    rdi,rax
1b38:   call   1420 <_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6lengthEv@plt>
1b3d:   cmp    rax,QWORD PTR [rbp-0x28]
1b41:   seta   al
1b44:   test   al,al
1b46:   je     1b98 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x5e8>
1b48:   mov    rdx,QWORD PTR [rbp-0x28]
1b4c:   lea    rax,[rbp-0xd0]
1b53:   mov    rsi,rdx
1b56:   mov    rdi,rax
1b59:   call   1590 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt>
1b5e:   mov    rbx,rax
1b61:   movzx  r12d,BYTE PTR [rbx]
1b65:   mov    rax,QWORD PTR [rbp-0x38]
1b69:   mov    rdi,rax
1b6c:   call   1480 <strlen@plt>
1b71:   mov    rcx,rax
1b74:   mov    rax,QWORD PTR [rbp-0x28]
1b78:   mov    edx,0x0
1b7d:   div    rcx
1b80:   mov    rax,QWORD PTR [rbp-0x38]
1b84:   add    rax,rdx
1b87:   movzx  eax,BYTE PTR [rax]
1b8a:   not    eax
1b8c:   xor    eax,r12d
1b8f:   mov    BYTE PTR [rbx],al
1b91:   add    QWORD PTR [rbp-0x28],0x1
1b96:   jmp    1b2e <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x57e>

No pongo anotaciones en el código porque es idéntico al anterior y además así practicais vosotros para ir viendo lo que hace ;-). Básicamente hace esto:

for (i=0; i < 33; i = i + 1) {
    j = i mod 18;
    copia_patron2[i] = NOT(patron1_codificado[j]) XOR copia_patron2[i];
}

Igual que en el primer patrón, al salir de este segundo bucle se van a realizar una serie de comprobaciones para saber si el pin introducido es correcto. De esta manera podemos calcular, si vamos haciendo las operaciones inversas, los cuatro valores restantes que nos quedan por saber del pin (posicones 2,4,5 y 7). En este código se muestra esas comprobaciones que con la práctica anterior podéis seguir sin ayuda:

1bb3:  48 8d 85 30 ff ff ff    lea    rax,[rbp-0xd0]
1bba:   be 1d 00 00 00          mov    esi,0x1d     <== Comprueba la posición 0x1d del patron2_codificado
1bbf:   48 89 c7                mov    rdi,rax
1bc2:   e8 c9 f9 ff ff          call   1590 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt>
1bc7:   0f b6 00                movzx  eax,BYTE PTR [rax]
1bca:   3c 69                   cmp    al,0x69      
1bcc:   75 51                   jne    1c1f <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x66f>
1bce:   48 8d 85 30 ff ff ff    lea    rax,[rbp-0xd0]
1bd5:   be 07 00 00 00          mov    esi,0x7      <== Comprueba la posición 0x7 del patron2_codificado
1bda:   48 89 c7                mov    rdi,rax
1bdd:   e8 ae f9 ff ff          call   1590 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt>
1be2:   0f b6 00                movzx  eax,BYTE PTR [rax]
1be5:   3c 31                   cmp    al,0x31
1be7:   75 36                   jne    1c1f <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x66f>
1be9:   48 8d 85 30 ff ff ff    lea    rax,[rbp-0xd0]
1bf0:   be 1e 00 00 00          mov    esi,0x1e     <== Comprueba la posición 0x1e del patron1_codificado
1bf5:   48 89 c7                mov    rdi,rax
1bf8:   e8 93 f9 ff ff          call   1590 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt>
1bfd:   0f b6 00                movzx  eax,BYTE PTR [rax]
1c00:   3c 68                   cmp    al,0x68
1c02:   75 1b                   jne    1c1f <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x66f>
1c04:   48 8d 85 30 ff ff ff    lea    rax,[rbp-0xd0]
1c0b:   be 1f 00 00 00          mov    esi,0x1f     <== Comprueba la posición 0x1f del patron1_codificado
1c10:   48 89 c7                mov    rdi,rax
1c13:   e8 78 f9 ff ff          call   1590 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt>
1c18:   0f b6 00                movzx  eax,BYTE PTR [rax]
1c1b:   3c 77                   cmp    al,0x77      
1c1d:   74 07                   je     1c26 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x676>
1c1f:   b8 01 00 00 00          mov    eax,0x1
1c24:   eb 05                   jmp    1c2b <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x67b>
1c26:   b8 00 00 00 00          mov    eax,0x0
1c2b:   84 c0                   test   al,al
1c2d:   74 05                   je     1c34 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE11_M_capacityEm@plt+0x684>

Si revertimos las operaciones para esas posiciones, el PIN final sería: 16274248.

 Deactivation Code 
  > 16274248

fwhibbit{d34c71v473d_r4bb17_b0mb}

¡¡Buen trabajo Bauer!!

Fwhibbit CTF 2017 - Crazy Serial

Published on:
Tags: reversing

Enunciado

Crazy Serial

Points: 350

Country: Vongo - Kinshasa

Attatchment: https://mega.nz/#!QpFSVYqI!85ekG2b5MwHW8BXxGvcUrkg_Liluz2M27c8xCeo4ZaA

Description: Serial serial serials, I have nightmares with serials!!! Dear soldier, we need you to find the crazy serial for the rabbit team, the future of the team depends on you...GO GO GO!

Solución

Nos dan un binario que al ejecutarlo nos pide una dirección de correo y un número de serial:

./crazy_serial-350   
 Enter your Mail
 > aa@a 
 Enter Serial
 > 12345678
  Wrong Serial

Si hacemos un string sobre el binario nos puede ayudar para ver por donde empezar a buscar en el código ensamblador:

strings crazy_serial-350
 Wrong Serial
 Enter your Mail
 Enter Serial
  fwhibbit{

Lo primero de todo es averiguar el punto de entrada del binario, para poner un breakpoint en el inicio de la función principal:

>>> set stop-on-solib-events 1
>>> info target
Symbols from "/root/Documents/FwhibbitCTF/reversing/crazy_serial-350".
Local exec file:
    `/root/Documents/FwhibbitCTF/reversing/crazy_serial-350', file type elf64-x86-64.
    Entry point: 0x555555554e00
br *0x000055555555505c

Echemos un vistazo al código a ver que encontramos:

1075:  call   f30                      <== Saca por pantalla el dibujo en ASCII del conejito playboy       
107a:   lea    rdi,[rip+0x817]          
1081:   mov    eax,0x0
1086:   call   ce0 <printf@plt>         <== Saca por pantalla la cadena "Enter your Mail"
108b:       lea    rax,[rbp-0x230]          <== Guarda la posición de memoria donde va a recoger el valor de Mail
1092:   mov    rsi,rax          <== Carga $rsi con $rbp-0x230. $rsi se usa como origen en las operaciones con cadenas
1095:   lea    rdi,[rip+0x201024]       <== Carga $rdi con el valor donde está la función _ZSt3cin que nos va a leer de consola
109c:   call   d70                      <== Llama a la función para recoger y poner en memoria el valor de Mail (cin?)

Una vez ejecutado lo anterior, vamos a tener el valor de nuestro Mail introducido (en este caso AA@A) en la siguiente posición de memoria:

>>> x/32xw $rbp-0x230
0x7fffffffdf40: 0x41404141  0x00000000  0x00000000  0x00000000

Si seguimos viendo el código, nos va a pedir el valor del serial y lo va a guardar en memoria:

10a1:  lea    rdi,[rip+0x805]        
10a8:   mov    eax,0x0
10ad:   call   ce0 <printf@plt>       <== Saca por pantalla la cadena "Enter Serial"
10b2:   lea    rax,[rbp-0x430]      <== Guarda la posición de memoria donde va a recoger el valor del Serial
10b9:   mov    rsi,rax
10bc:   lea    rdi,[rip+0x200ffd]        # 2020c0 <_ZSt3cin@@GLIBCXX_3.4>
10c3:   call   d70 <_ZStrsIcSt11char_traitsIcEERSt13basic_istreamIT_T0_ES6_PS3_@plt>

Si miramos la posición de memoria $rbp-0x430 es donde estará almacenado nuestro valor introducido como serial (en este caso 11111111):

>>> x/32xw $rbp-0x430
0x7fffffffdd40: 0x31313131  0x31313131  0xf7a7fb00  0x00007fff

Veamos ahora que comprobaciones hace con los valores introducidos. La primera comprobación la hace con Mail, va a recorrer la cadena con un bucle para comprobar si hay una '@' en la cadena.

10c8:  mov    DWORD PTR [rbp-0x18],0x0     <== i=0
10cf:   mov    eax,DWORD PTR [rbp-0x18]     <== Inicio del bucle
10d2:   movsxd rbx,eax
10d5:   lea    rax,[rbp-0x230]
10dc:   mov    rdi,rax
10df:   call   d50 <strlen@plt>           <== Calcula la longitud de la cadena de Mail introducida
10e4:   cmp    rbx,rax              <== Si ha llegado al último caracter de la cadena sale
10e7:   jae    1104 <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt+0x324>
10e9:   mov    eax,DWORD PTR [rbp-0x18]
10ec:   cdqe   
10ee:   movzx  eax,BYTE PTR [rbp+rax*1-0x230]   <== Coge el caracter[i] de Mail
10f6:   cmp    al,0x40              <== Comprueba que el caracter[i] de Mail sea igual a @
10f8:   jne    10fe <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt+0x31e>
10fa:   mov    BYTE PTR [rbp-0x11],0x1      <== Si hay una @ se guarda un 1 en var_11(=$rbp-0x11) -- Centinela
10fe:   add    DWORD PTR [rbp-0x18],0x1     <== i++
1102:   jmp    10cf <_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEixEm@plt+0x2ef>     <== Vuelve al principio del bucle

También comprueba que Mail sea mayor que 3 caracteres:

1104:  movzx  eax,BYTE PTR [rbp-0x11]  <== Recoge valor del centinela
1108:   xor    eax,0x1          <== Comprueba si centinela es 1 (si hay una @)
110b:   test   al,al                
110d:   jne    1124             <== Sale si el centinela=0
110f:   lea    rax,[rbp-0x230]      <== Vuelve a coger el valor de &Mail
1116:   mov    rdi,rax
1119:   call   d50 <strlen@plt>       <== Calcula la longitud de Mail
111e:   cmp    rax,0x3          <== Comprueba si tiene más de 3 caracteres
1122:   ja     1129             <== Sigue la ejecucción si lenght(Mail)>3
1124:   call   100f             <== Función que llama a salir del programa si no cumple alguna de las condiciones

Una vez superado el Mail, empieza la locura de comprobaciones con el Serial, hay una ristra enorme de comprobaciones y creo que no voy a poner todas, pondré las más significativas. Se trata de ir superando todas las comprobaciones hasta sacar el valor del serial. La primera comprobación es el nº de dígitos que tiene el serial:

1129:  lea    rax,[rbp-0x430]  <== Posición de memoria de Serial
1130:   mov    rdi,rax
1133:   call   d50 <strlen@plt>   <== Halla length(Serial)
1138:   cmp    rax,0x18     <== Compara con 24
113c:   ja     1143         <== Si es mayor que 24 sigue
113e:   call   100f         <== Si no lo es sale

Comprueba que en las posiciones 0x5, 0xb y 0x12 el Serial tenga el caracter "-".

1143:  movzx  eax,BYTE PTR [rbp-0x42b]     <== Posicion 5 del Serial ($rbp-0x430 es la posición 0)
114a:   cmp    al,0x2d              <== Valor ASCII de '-'
114c:   je     1169 
114e:   movzx  eax,BYTE PTR [rbp-0x425]     <== Posición 0xb del Serial(0x430-0x425 = 0xb)
1155:   cmp    al,0x2d              <== Valor ASCII de '-'
1157:   je     1169 
1159:   movzx  eax,BYTE PTR [rbp-0x41e]     <== Posición 0xb del Serial(0x430-0x41e = 0x12)
1160:   cmp    al,0x2d              <== Valor ASCII de '-'
1162:   je     1169 
1164:   call   100f 

La siguiente comprobación es que las posiciones 0x0 y 0xa del Serial sean iguales, Serial[0x0]=Serial[0xa]:

1169:  0f b6 95 d0 fb ff ff    movzx  edx,BYTE PTR [rbp-0x430] <== 0x430-0x430 -> Posición 0x0
1170:   0f b6 85 da fb ff ff    movzx  eax,BYTE PTR [rbp-0x426] <== 0x430-0x426 -> Posición 0xa
1177:   38 c2                   cmp    dl,al
1179:   74 05                   je     1180 
117b:   e8 8f fe ff ff          call   100f 

Si seguimos el ensamblador se tiene que cumplir lo siguiente:

1187 cmp     al, 7Ah :     Serial[0x1] = 0x7a ('z')
1197 cmp     al, 79h :  Serial[0x3] = 0x79 ('y')
11A7 test    al, al  :  Serial[0x19] = 0x00 (Último caracter de Serial, indica fin de cadena)
11B7 cmp     al, 65h :  Serial[0x2] = 0x65 ('e')
11D7 cmp     eax, edx:  Serial[0x4] = Serial[0x11]+0x2
11E7 cmp     al, 64h :  Serial[0x6] = 0x64 ('d')
11F7 cmp     al, 72h :  Serial[0x7] = 0x72 ('r')
120E cmp     dl, al  :  Serial[0x8] = Serial[0x16]
121E cmp     al, 4ch :  Serial[0x9] = 0x4c ('L')
1244 call    sub_1029:  Serial[0xc] = Serial[0x5]+Serial[0x5]+0x9
126C cmp     eax, edx:  Serial[0x17] = Serial[0x11]+0x1
127C cmp     al, 74h :  Serial[0xd] = 0x74 ('t')
128C cmp     al, 66h :  Serial[0xe] = 0x66 ('f')
12B2 call    sub_1029:  Serial[0x10] = Serial[0xf]+Serial[0xf]+0xFFFFFF7A
12CA cmp     al, 54h :  Serial[0x15] = 0x54 ('T')
12DA cmp     al, 48h :  Serial[0x10] = 0x48 ('H')
12EA cmp     al, 75h :  Serial[0x14] = 0x48 ('u')
12FA cmp     al, 35h :  Serial[0x11] = 0x35 ('5')
130A cmp     al, 70h :  Serial[0x13] = 0x70 ('p')
131A cmp     al, 46h :  Serial[0x16] = 0x46 ('F')
1331 cmp     dl, al  :  Serial[0xa] = Serial[0x15]
1357 call    sub_1029:  Serial[0x14] = Serial[0x18]+Serial[0x18]+0xFFFFFFC3

Si lo ponemos todo en orden y cumplimos las condiciones nos sale el siguiente serial: Tzey7-drFLT-ctfgH5-puTF6Y. Y ejecutando el programa nos devuelve la flag:

./crazy_serial-350 
 Enter your Mail
 > AA@A
 Enter Serial
 > Tzey7-drFLT-ctfgH5-puTF6Y

  fwhibbit{r4bb1t_s3r14l-2JBH8tckcTj}

Creo que esto se hace mucho más rápido si usamos la libreria de python angr. A ver si aprendo y publico la solución usándola.