AVILX
REVERSING WRITEUP · CRACKMES.ONE · 2026
VVSC
Password numérico hardcodeado — conversión hex a decimal
| Autor reto | elijunior01 |
| Plataforma | crackmes.one |
| Dificultad | 1.0 / 5.0 |
| Arquitectura | x86-64 — Unix/Linux |
| Modo | Normal Mode — Análisis estático |
| Analista | Jesús Ávila (Avilx) |
| Fecha | 22 de Septiembre 2026 |
| Portafolio | avilx.dev |
Documentación de aprendizaje en reversing | Entorno controlado
Información del Binario
| Campo | Valor |
|---|---|
| Nombre | VVSC |
| Tipo | ELF 64-bit LSB executable |
| Arquitectura | x86-64 |
| Enlazado | dynamically linked |
| Stripped | no |
| BuildID | 7ea0eace145e89811a030f9914f6637e69d29d66 |
| Stack Canary | No Canary Found |
| NX | Enabled |
| PIE | PIE Disabled |
| RELRO | Partial RELRO |
La combinación de No Canary y PIE Disabled hace que este binario sea potencialmente vulnerable a buffer overflow con direcciones predecibles. Sin embargo, el mecanismo de validación resultó ser una comparación directa de entero en el disassembly, sin necesidad de explotar ninguna vulnerabilidad de memoria. Partial RELRO deja el GOT escribible — relevante si existiera un vector de escritura arbitraria.
Reconocimiento
File y checksec
$ file ./VVSC
./VVSC: 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]=7ea0eace145e89811a030f9914f6637e69d29d66,
not stripped
$ checksec file ./VVSC
Partial RELRO | No Canary Found | NX enabled | PIE Disabled | 80 symbols
Strings
$ strings -a ./VVSC | grep -v "^_Z\|GLIBC\|\.so\|\."
Enter the numerical password:
El output de
strings no contiene ningún password en texto claro. El único string relevante es el prompt “Enter the numerical password:”, que indica que el binario valida un número entero, no una cadena de texto. Esto descarta el vector de texto claro directo y apunta al disassembly como siguiente paso.Ejecución inicial
$ ./VVSC
Enter the numerical password:
3
Access denied.
El binario pide un entero y lo compara internamente contra un valor hardcodeado. La comparación debe ser visible en el disassembly como una instrucción
cmp contra un valor inmediato, justo después de la llamada a cin.Análisis del Disassembly
Flujo de main
$ objdump -d -M intel ./VVSC | grep -A 50 "<main>"
400a93: call _ZNSirsERi ; cin >> variable (lee entero)
400a98: cmp DWORD PTR [rsp+0xc],0xd80b1
400aa0: je 400b68 ; si igual -> "Allowed access."
400aa6: mov edx,0xf
400aab: mov esi,0x400d59 ; "Access denied."
El patrón es inmediato:
cin lee el input del usuario en [rsp+0xc], y la siguiente instrucción lo compara contra el valor inmediato 0xd80b1. Si son iguales, je salta al bloque de éxito. Este es el mismo patrón lectura + comparación + salto condicional visto en challenges anteriores, pero con un entero en lugar de un string.La diferencia clave respecto a challenges anteriores: aquí no hay
strcmp ni operator==. La comparación es directa contra un valor inmediato en el código. En C/C++, esto corresponde a if (input == 884913). El compilador lo optimiza a un cmp directo sin llamada a función.Conversión del valor
El password está en hexadecimal en el disassembly. La conversión a decimal es directa:
>>> 0xd80b1
884913
El password numérico es 884913 (
0xd80b1 en hex). Identificado directamente en la instrucción cmp del disassembly de main.Solución
Password encontrado
PASSWORD ENCONTRADO
$ ./VVSC
Enter the numerical password:
884913
Allowed access.
El password 884913 fue identificado convirtiendo el valor hexadecimal
0xd80b1 de la instrucción cmp del disassembly. El binario respondió “Allowed access.” confirmando el vector.Reflexión Final
Qué salió bien
Reconocer inmediatamente que la ausencia de password en strings significaba que el valor estaba en el disassembly como inmediato. El patrón cin + cmp + je es ahora reconocible a primera vista.
La conversión de hex a decimal es directa — 0xd80b1 a 884913 sin necesidad de herramientas externas.
Qué salió mal
Al leer el disassembly inicialmente hubo dificultad para separar el ruido del compilador C++ (símbolos de cout/cin mangleados) de las instrucciones relevantes. La clave es enfocarse únicamente en cmp, test, je/jne y las llamadas a funciones conocidas como cin/strcmp.
Lecciones aprendidas
Cuando strings no revela el password, el siguiente paso es siempre el disassembly. En binarios que piden enteros, la comparación aparece como cmp contra un valor inmediato en hexadecimal — no hay llamada a strcmp ni operator==.
El patrón universal de validación en crackmes:
-
Leer input (
cin,scanf,gets) -
Comparar contra valor esperado (
cmp,strcmp,operator==) -
Salto condicional (
je/jne) al bloque de éxito o fallo
Identificar este patrón en cualquier variante es la base del análisis de crackmes. Todo lo demás es ruido del compilador.
Tiempo total
| Fase | Tiempo |
|---|---|
| Reconocimiento (file, checksec, strings) | 3 minutos |
| Análisis del disassembly | 10 minutos |
| Conversión hex a decimal | 1 minuto |
| Verificación | 2 minutos |
| Total | 16 minutos |