LinuxParty
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[]){
char serial[15] = "UNREGISTERED";
char login[20]; printf("Introduzca su nombre de usuario: ");
gets(login);
printf("Comprobando número de serie... %sn", serial);
if(!strcmp(serial, "00-11-22-33-44"))
{
printf("Hola %s, bienvenido al sistema...n", login);
printf("La contraseña del siguiente nivel es LALALALAn");
} else
{
printf("Lo siento %s, su numero de serie ha caducadon", login);
exit(-1);
}
}
Al ejecutar el fichero ELF vemos que efectivamente
sigue el flujo especificado en la plantilla de código C anterior: se
pide el nombre de usuario y se comprueba el nº de serie 00-11-22-33-44
contra el que el ejecutable guarda en memoria. Lógicamente los números
son distintos y por tanto, se informa al usuario del error:
La forma más rápida de saltarse la protección consiste en darse cuenta de que se utiliza una función gets() para obtener el nombre del usuario SIN hacer ninguna comprobacióndel tamaño de la entrada. Teniendo esto en cuenta, y viendo cómo se almacenan las variables login y serial, ¿qué ocurrirá si tecleamos un nombre (login) de más de 20 caracteres? ¿y si esos caracteres de más son justo 00-11-22-33-44? El resultado es que la zona de memoria reservada para guardar el login se desborda y machaca la zona de memoria reservada para guardar el número de serie. Es decir, provocaremos un simple buffer overflow. Simple pero lo suficientemente efectivo como para romper el flujo de ejecución a nuestro gusto y obtener la clave deseada.
No obstante, hay más soluciones para resolver este reto. Por ejemplo, podemos abrir el fichero con VIM y retocar el nº de serie almacenado (00-11-22-33-44) para que en su lugar aparezca el que deseamos (UNREGISTERED). Hay que tener cuidado de no cambiar el tamaño del ejecutable, para evitar un error del tipo “Violación de Segmento”.
Pero el procedimiento general que me interesaba es otro. Quería desensamblar el ejecutable, analizar el código ASM y modificar las instrucciones necesarias para romper el flujo normal de ejecución de tal forma que consigamos la clave directamente. O sea, droga dura.
Vayamos paso a paso :
1) Desempolvando GDB (desensamblar el ELF):
Para esta parte, usaremos GDB, el debugger GNU. Podemos abrir el ejecutable directamente con gdb, así:
$ gdb nivel04
(gdb) break main <— poner un punto de ruptura en el método main
(gdb) run <– comenzar a ejecutar el ELF
Starting program: /tmp/nivel04/nivel04
Breakpoint 1, 0×080483ca in main () <— interesante, main comienza en esa dirección de memoria
(gdb) disassemble 0×080483ca 0×80484ff <– desensamblar desde main() unas cuantas instrucciones
… saltemos a la parte interesante …
0x08048498 <main+212>: mov 0xfffff7dc(%ebp),%edi
0x0804849e <main+218>: mov 0xfffff7d8(%ebp),%ecx
0x080484a4 <main+224>: repz cmpsb %es:(%edi),%ds:(%esi) <--- comparación de strings, mmmhhh...
0x080484a6 <main+226>: seta %dl
0x080484a9 <main+229>: setb %al
0x080484ac <main+232>: mov %dl,%cl
0x080484ae <main+234>: sub %al,%cl
0x080484b0 <main+236>: mov %cl,%al
0x080484b2 <main+238>: movsbl %al,%eax
0x080484b5 <main+241>: test %eax,%eax
0x080484b7 <main+243>: jne 0x8048583 <main+447> <--- bifurcación en función de la comparación ...
0x080484bd <main+249>: lea 0xfffff7f8(%ebp),%eax
...
Vale… ¿qué estamos comparando exactamente en la instrucción situada en 0×080484a4 ? Veámoslo.
(gdb) break *0×080484a4
Breakpoint 2 at 0×80484a4
(gdb) continue
Continuing.
Introduzca su nombre de usuario: Juanan
Comprobando n�mero de serie… UNREGISTERED
Breakpoint 2, 0×080484a4 in main ()
(gdb) x/14c $edi <— veamos qué tenemos en la zona apuntada por %edi
0×804873f <_IO_stdin_used+75>: 48 ‘0′ 48 ‘0′ 45 ‘-’ 49 ‘1′ 49 ‘1′ 45 ‘-’ 50 ‘2′ 50 ‘2′
0×8048747 <_IO_stdin_used+83>: 45 ‘-’ 51 ‘3′ 51 ‘3′ 45 ‘-’ 52 ‘4′ 52 ‘4′
(gdb) x/s $edi <— lo mismo de antes, dicho de otra forma más human-friendly ;-)
0×804873f <_IO_stdin_used+75>: “00-11-22-33-44″
Anda! el número de serie con el que comparamos… por tanto, en %esi tendremos…
(gdb) x/s $esi
0xbfeeae14: “UNREGISTERED”
Olé! Justo lo que suponía.
Así que si esta comparación se cumple, habrá un salto (bifurcación if) a alguna zona de memoria, y en caso contrario, seguirá el flujo normal (sin salto). Según el código ASM, el primer salto tras esa comparación se produce en 0×080484b7 (salto JNE a 0×8048583). El objetivo es cambiar esa instrucción para hacer justo lo contrario de lo que hace ahora, es decir, que si antes saltábamos si UNREGISTERED y 00-11-22-33-44 eran distintas, ahora no saltaremos (y viceversa). ¿Cómo? Cambiando el JNE - Jump if Not Equivalent - por la instrucción JE - Jump if Equivalent.
2) Cambiando el curso del río (cómo retocar el fichero binario a nuestro gusto)
Quiero cambiar la instrucción situada en la zona de memoria 0×080484b7, JNE por un JE. Para ello, tengo que calcular el offset a esa instrucción dentro del fichero. Es decir, cuando abra el fichero binario, ¿dónde estará el byte que tengo que cambiar? ¿y a qué valor lo cambio? Las direcciones del código main() empiezan todas por 0×08048xxx. Ese desplazamiento (xxx) es el que me interesa. En nuestro caso, 4b7. Ese será el offset dentro del fichero binario.
Abrimos con un editor hexadecimal el fichero ejecutable nivel04. Por ejemplo, con ghex2, el editor hexadecimal de GNOME.
Nos situamos en el offset 4b7 y apuntamos el código de operación: 0F 85. Toda operación tiene un código unívoco. Según la siguiente URL, el 0F 85 corresponde a JNE. Bingo! En gHex2 nos ponemos en modo inserción (Edición / Modo insertar) y cambiamos el 0F 85 por 0F 84 (siguiendo la página anterior, 0F 84 es el código de operación de JE (Jump if Equivalent).
Grabamos el resultado con otro nombre (por ejemplo, nivel04_hacked) y ejecutamos:
$ chmod +x nivel04_hacked
$ ./nivel04_hacked
Introduzca su nombre de usuario: j
Comprobando n�mero de serie… UNREGISTERED
Hola j, bienvenido al sistema…
La contrase�a del siguiente nivel es k0m13nz0s
Magia! :-) Bueno, ya me he desestrado un buen rato aprendiendo a resolver este problema, desempolvando mis conocimientos de GDB, y ASM. Si alguien quiere profundizar más en este arte casi perdido de la ingeniería inversa, puede empezar por los enlaces que acompañan a este artículo… o probando las pruebas de mayor dificultad que encontrará en el blog de txipi, el autor del HackIt! 2006 al que hay que agradecer el enorme trabajo que hizo (y animar a que nos vuelva a retar en el 2007 !)
Para finalizar, indicar que hemos montado un subdominio hackit.diariolinux.com para todo aquel que quiera echar unas partiditas ;-)
[1] BULMA: Introducción a GDB http://bulma.net/body.phtml?nIdNoticia=1805
[2] Análisis de un ejecutable toUpper con GDB: http://www.csee.umbc.edu/~cpatel2/links/310/projects/toupper.txt
[3] Exploiting your 1st buffer overflow vulnerability : http://www.devtarget.org/downloads/wolfgarten-your-first-buffer-overflow.pdf
[4] HackIt! 2006 : http://blog.txipinet.com/index.php/2006/08/15/20-solucion-de-los-5-primeros-niveles-del-hack-it-de-la-euskal-encounter-2006
-
Programación
- Thomas E. Kurtz, coinventor de BASIC, muere a los 96 años
- Profesor de informática del MIT prueba el impacto de la IA en la formación de programadores
- Lanzamiento del IDE de código abierto Qt Creator 14 con soporte para complementos basados en Lua
- Plantillas para Joomla - Episodio 1: Plantillas, marcos y clubes o no...
- Este es el mejor libro que he visto para aprender a programar en Python en castellano desde cero, gratis y online
- ¿Deberían los niños seguir aprendiendo a programar en la era de la IA?
- La 'obsolescencia' de VBScript confirmada por Microsoft y su eventual eliminación de Windows
- El Gran Debate: ¿Deberían los Modelos de Inteligencia Artificial Ser de Código Abierto?
- El lenguaje de programación BASIC cumple 60 años
- El CEO de Nvidia dice que los niños no deberían aprender a programar
- 40 años de Turbo Pascal: recuerdos del dinosaurio codificador que revolucionó los IDE
- Los lenguajes de programación más populares y dónde aprenderlos.
- Top 5 de los principales lenguajes de programación para desarrollar aplicaciones de escritorio Linux
- Qt Creator 12 lanzado con complementos de grabación de pantalla y Explorador de compiladores
- 10 sitios web para practicar problemas de programación