TermuxCTF
Mucho gusto me presento soy usuario activo de la comunidad de ivam3 cindarela. Soy cubano y curso el primer año de ingeneniería software y opero bajo el alias de DemonHunter.
[!NOTE] No te quedes con la duda de nada si no lo explico o no lo entiendes a buscalo en Internet
Sin más preámbulo para que estamos acá.
En ocasiones no cumplimos nuestras metas por que no te nemos los recuerdos necesarios o esos creemos quien no tiene un dispositivo Android en su poder hoy en día. Usando termux será lo que usted desee.
Los temas sentrales que estaré tocando serán de análisis a binarios y ingeniería inversa usando termux el objetivo no será optener la bandera sino tocar en detalles los aspectos a tender en cuenta para llegar a la meta .
Comenzando con nuestro primer CTF en la plataforma de Tryhackme …
1. Procedemos a descargar el archivo de el primer desafío
Estaremos asiendo un análisis estatico del mismo
En que consiste el análisis estático
- Análisis el archivo sin ejecutarlo 😎
Aunque quisiéramos no podriamos ejecutarlo entre comillas. Pero bueno no es el caso ahora…
Empezamos con lo más básico que es saber a que nos estamos enfrentando usando.
file crackme1
crackme1: 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]=672f525a7ad3c33f190c060c09b11e9ffd007f34, not stripped
Estructurado la salida optenida vamos a ver lo que tenemos un archivo ELF 64 bist executable
compilado para x86-64
y el LSB
lo que significa que los números se ordenan con byte
menos significativo en memoria.
Estaremos utilizando la herramienta de radare2 qué se puede instalar desde el administrador de paquetes apt
con apt install radare2
Procedemos a abrir el binario en r2
Banderas:
-A
: Para que analise todo el código referenciado
El comando ‘print’ se abrevia como p
y tiene varios submodos pero no te agobies si le añades ?
tendrás un menú de ayuda.
Procedemos a desmontar la función principal que no es siempre el caso hay programas que ejecutan funcinoes antes de main
.
pdf @ main
[0x00400450]> pdf@main
; ICOD XREF from entry0 @ 0x40046d(r)
┌ 321: int main (int argc, char **argv);
│ `- args(rdi, rsi) vars(31:sp[0xc..0xa8])
│ 0x00400546 55 push rbp
│ 0x00400547 4889e5 mov rbp, rsp
│ 0x0040054a 4881eca000.. sub rsp, 0xa0
│ 0x00400551 89bd6cffffff mov dword [var_94h], edi ; argc
│ 0x00400557 4889b560ff.. mov qword [var_a0h], rsi ; argv
│ 0x0040055e c745902500.. mov dword [var_70h], 0x25 ; '%' ; 37
│ 0x00400565 c745942b00.. mov dword [var_6ch], 0x2b ; '+' ; 43
│ 0x0040056c c745982000.. mov dword [var_68h], 0x20 ; 32
│ 0x00400573 c7459c2600.. mov dword [var_64h], 0x26 ; '&' ; 38
│ 0x0040057a c745a03a00.. mov dword [var_60h], 0x3a ; ':' ; 58
│ 0x00400581 c745a42d00.. mov dword [var_5ch], 0x2d ; '-' ; 45
│ 0x00400588 c745a82e00.. mov dword [var_58h], 0x2e ; '.' ; 46
│ 0x0040058f c745ac3300.. mov dword [var_54h], 0x33 ; '3' ; 51
│ 0x00400596 c745b01e00.. mov dword [var_50h], 0x1e ; 30
│ 0x0040059d c745b43300.. mov dword [var_4ch], 0x33 ; '3' ; 51
│ 0x004005a4 c745b82700.. mov dword [var_48h], 0x27 ; '\'' ; 39
│ 0x004005ab c745bc2000.. mov dword [var_44h], 0x20 ; 32
│ 0x004005b2 c745c03300.. mov dword [var_40h], 0x33 ; '3' ; 51
│ 0x004005b9 c745c41e00.. mov dword [var_3ch], 0x1e ; 30
│ 0x004005c0 c745c82a00.. mov dword [var_38h], 0x2a ; '*' ; 42
│ 0x004005c7 c745cc2800.. mov dword [var_34h], 0x28 ; '(' ; 40
│ 0x004005ce c745d02d00.. mov dword [var_30h], 0x2d ; '-' ; 45
│ 0x004005d5 c745d42300.. mov dword [var_2ch], 0x23 ; '#' ; 35
│ 0x004005dc c745d81e00.. mov dword [var_28h], 0x1e ; 30
│ 0x004005e3 c745dc2e00.. mov dword [var_24h], 0x2e ; '.' ; 46
│ 0x004005ea c745e02500.. mov dword [var_20h], 0x25 ; '%' ; 37
│ 0x004005f1 c745e41e00.. mov dword [var_1ch], 0x1e ; 30
│ 0x004005f8 c745e82400.. mov dword [var_18h], 0x24 ; '$' ; 36
│ 0x004005ff c745ec2b00.. mov dword [var_14h], 0x2b ; '+' ; 43
│ 0x00400606 c745f02500.. mov dword [var_10h], 0x25 ; '%' ; 37
│ 0x0040060d c745f43c00.. mov dword [var_ch], 0x3c ; '<' ; 60
│ 0x00400614 c745f8bfff.. mov dword [var_8h], 0xffffffbf ; 4294967231
│ 0x0040061b 488d8570ff.. lea rax, [s]
│ 0x00400622 ba1b000000 mov edx, 0x1b ; 27 ; size_t n
│ 0x00400627 be41000000 mov esi, 0x41 ; 'A' ; 65 ; int c
│ 0x0040062c 4889c7 mov rdi, rax ; void *s
│ 0x0040062f e8ecfdffff call sym.imp.memset ; void *memset(void *s, int c, size_t n)
│ 0x00400634 c745fc0000.. mov dword [var_4h], 0
│ ┌─< 0x0040063b eb2c jmp 0x400669
│ │ ; CODE XREF from main @ 0x40066f(x)
│ ┌──> 0x0040063d 8b45fc mov eax, dword [var_4h]
│ ╎│ 0x00400640 4898 cdqe
│ ╎│ 0x00400642 0fb6840570.. movzx eax, byte [rbp + rax - 0x90]
│ ╎│ 0x0040064a 89c2 mov edx, eax
│ ╎│ 0x0040064c 8b45fc mov eax, dword [var_4h]
│ ╎│ 0x0040064f 4898 cdqe
│ ╎│ 0x00400651 8b448590 mov eax, dword [rbp + rax*4 - 0x70]
│ ╎│ 0x00400655 01d0 add eax, edx
│ ╎│ 0x00400657 89c2 mov edx, eax
│ ╎│ 0x00400659 8b45fc mov eax, dword [var_4h]
│ ╎│ 0x0040065c 4898 cdqe
│ ╎│ 0x0040065e 88940570ff.. mov byte [rbp + rax - 0x90], dl
│ ╎│ 0x00400665 8345fc01 add dword [var_4h], 1
│ ╎│ ; CODE XREF from main @ 0x40063b(x)
│ ╎└─> 0x00400669 8b45fc mov eax, dword [var_4h]
│ ╎ 0x0040066c 83f81a cmp eax, 0x1a ; 26
│ └──< 0x0040066f 76cc jbe 0x40063d
│ 0x00400671 488d8570ff.. lea rax, [s]
│ 0x00400678 4889c7 mov rdi, rax ; const char *s
│ 0x0040067b e890fdffff call sym.imp.puts ; int puts(const char *s)
│ 0x00400680 b800000000 mov eax, 0
│ 0x00400685 c9 leave
└ 0x00400686 c3 ret
[0x00400450]>
Buscando instrucciones relacionada con la pila
- Las instrucciones que manipulan la pila suelen ser:
push
: Guarda un valor en la pila.pop
: Extrae un valor de la pila.mov [rbp - X], Y
: Guarda un valor en una posición específica de la pila.lea
: Carga una dirección de memoria en un registro (a menudo usado para direccionar la pila).
Sabiendo lo anterior vemos que varios valores se guardan el la pila
estos valores se almacenan en posiciones relativas al registro rbp
que sería rbp - 0x70, rbp - 0x6c
ect…
radare2 agrega comentarios en automático en el código, esto nos ayuda e entender que valores se están guardando pero parese ser la bandera.
¿ Qué son los registros?
Los egistro de procesador es una ubicación de acceso rápido disponible para el procesador de una computadora
Los registros de 64 bits
tienen nombres que comienzan con “r”, por lo que, por ejemplo, la extensión de 64 bits de eax
se llama rax
. Los nuevos registros se denominan r8
a r15
ojo con los hibridos xD:
Llamada a funciones
Cuando se llama a una función, el compilador utiliza un marco de pila (ubicado dentro de la pila de ejecución del programa ) para almacenar toda la información temporal que la función requiere para operar. Dependiendo de la convención de llamada, la función que llama colocará la información antes mencionada en registros específicos o en la pila del programa o en ambos.
En Linux, se colocarán los argumentos en los registros RDI
, RSI
, RDX
, RCX
, R8
y R9
y cualquier cosa adicional se colocará en la pila.
Tenemos dos llamadas a función y una es la función memset()
y puts()
qué la podemos identificar por la instrucción de call
qué es la encargada de llamar a un función.
Ya sabiendo lo anterior tenemos una función llamada memset()
y los valores que toma por argumento . Buscando en Internet vemos que C
cuenta con una función del mismo nombre y no es creada por el usuario. (ojo)
El código llama a memset
para inicializar un bloque de memoria con el valor 0x41
('A'
):
lea rax, [s]
mov edx, 0x1b ; 27 ; size_t n
mov esi, 0x41 ; 'A' ; 65 ; int c
mov rdi, rax ; void *s
call sym.imp.memset ; void *memset(void *s, int c, size_t n)
Esto llena 27 bytes con el carácter 'A'
.
Después nos topamos con un bucle que realiza una operación de transformación sobre los valores inicializados.
Inicialización del bucle
mov dword [var_4h], 0
jmp 0x400669
Cuerpo del bucle
mov eax, dword [var_4h]
cdqe
movzx eax, byte [rbp + rax - 0x90]
mov edx, eax
mov eax, dword [var_4h]
cdqe
mov eax, dword [rbp + rax*4 - 0x70]
add eax, edx
mov edx, eax
mov eax, dword [var_4h]
cdqe
mov byte [rbp + rax - 0x90], dl
add dword [var_4h], 1
- El bucle itera sobre un arreglo de bytes.
- Para cada iteración, toma un valor del arreglo
[rbp + rax - 0x90]
y lo suma con un valor del arreglo[rbp + rax*4 - 0x70]
. - El resultado se almacena de nuevo en
[rbp + rax - 0x90]
.
Condición del bucle
cmp eax, 0x1a ; 26
jbe 0x40063d
- El bucle se ejecuta mientras
eax
sea menor o igual a0x1a
(26).
Después del bucle, el código imprime el contenido de [s]
usando puts
:
lea rax, [s]
mov rdi, rax ; const char *s
call sym.imp.puts ; int puts(const char *s)
El bucle realiza una operación de transformación sobre los valores inicializados. Podemos inferir que:
- El arreglo
[rbp - 0x90]
contiene 27 bytes inicializados con'A'
. - El arreglo
[rbp - 0x70]
contiene los valores constantes inicializados al principio. - Para cada byte en
[rbp - 0x90]
, se suma el valor correspondiente de[rbp - 0x70]
y se almacena el resultado.
Bueno ya sabemos lo que esta haciendo el programa esta generando una cadena transformada a partir de los valores guardados en la pila sabiendo esto vamos a tratar de recrear el programa bueno no lo dejo de tarea. Bueno acá el resumen.
Podemos realizar la operación matemática directa mente de la línea de comandos de radare2 usando la calculadora integrada usando el comando de ? (valores)
permite hacer cálculos en diferentes bases
? 0x41 + 0x25