AVILX
REVERSING WRITEUP · HACK THE BOX · 2026
Hunting License
Tres passwords: texto claro, reverse y XOR
| Plataforma | Hack The Box |
| Categoría | Reversing |
| Dificultad | Very Easy |
| Arquitectura | x86-64 — Unix/Linux |
| Analista | Jesús Ávila (Avilx) |
| Fecha | 08 de Septiembre 2026 |
| Portafolio | avilx.dev |
Documentación de aprendizaje en reversing | Entorno controlado
Información del Binario
| Campo | Valor |
|---|---|
| Nombre | license |
| Tipo | ELF 64-bit LSB executable |
| Arquitectura | x86-64 |
| Enlazado | dynamically linked |
| Stripped | not stripped (70 símbolos) |
| Librería | libreadline |
| Stack Canary | No Canary Found |
| NX | Enabled |
| PIE | PIE Disabled |
| RELRO | Partial RELRO |
Sin PIE las direcciones son fijas, facilitando el análisis con GDB. El binario usa
libreadline para leer el input del usuario, identificable con ldd. Tiene 70 símbolos incluyendo las funciones main, exam, reverse y xor.Reconocimiento
Análisis inicial
Strings
$ strings -a ./license | grep -v "^_Z\|GLIBC\|\.so\|\."
[Mensajes del programa]
So, you want to be a relic hunter?
Okay, first, a warmup - what's the first password?
This one's not even hidden:
PasswordNumeroUno
Not even close!
Getting harder - what's the second password?
Your final test - give me the third, and most protected, password:
Well done hunter - consider yourself certified!
[Strings sospechosos]
0wTdr0wss4P
G{zawR}wUz}r
[Funciones relevantes]
reverse
exam
main
strcmp
readline
El primer password
PasswordNumeroUno está en texto claro. Hay dos strings ofuscados: 0wTdr0wss4P y G{zawR}wUz}r. Existe una función reverse que sugiere que alguno de estos strings está invertido. El programa usa strcmp para comparar los passwords.Ejecución inicial
$ ./license
So, you want to be a relic hunter?
...
Okay, first, a warmup - what's the first password?
This one's not even hidden: PasswordNumeroUno
Getting harder - what's the second password?
El primer password se acepta directamente. El segundo no está visible en los strings. La función
reverse probablemente invierte 0wTdr0wss4P para obtener el segundo password.Análisis del Flujo
Password 1: texto claro
PasswordNumeroUno
El primer password está hardcodeado en
.rodata en texto claro, visible directamente con strings.Password 2: función reverse()
$ python3 -c "print('0wTdr0wss4P'[::-1])"
P4ssw0rdTw0
El string
0wTdr0wss4P invertido produce P4ssw0rdTw0. La función reverse() copia el string fuente al destino en orden inverso byte por byte.El segundo password es
P4ssw0rdTw0, resultado de invertir 0wTdr0wss4P con la función reverse().Password 3: XOR + GDB
El tercer password usa la función xor() con estos argumentos:
$ objdump -d -M intel ./license | grep -A 10 "call.*xor"
401353: lea rax, [rbp-0x30]
401357: mov ecx, 0x13 ; clave XOR = 0x13 = 19
40135c: mov edx, 0x11 ; longitud
401361: mov esi, 0x404070 ; fuente: G{zawR}wUz}r
401366: mov rdi, rax ; destino: [rbp-0x30]
401369: call xor
La clave XOR es
0x13 (valor de ecx), no 0x11 (que es la longitud en edx). Esta confusión fue un intento fallido inicial. El string cifrado G{zawR}wUz}r en 0x404070 se XORea con 0x13 y el resultado se guarda en [rbp-0x30].Se intentó decodificar manualmente con Python usando
0x11 como clave XOR pero el resultado VjkpfClfDklcn### no era un password válido. La clave correcta era 0x13.Lectura del tercer password con GDB
$ gdb -q ./license
break *0x40136e
run
# Input: y, PasswordNumeroUno, P4ssw0rdTw0
# En el stack despues del XOR:
RSP → 'ThirdAndFinal!!!'
Después de ejecutar la función
xor(), pwndbg mostró el resultado directamente en el stack: ThirdAndFinal!!!. El registro RSP apuntaba al string ya descifrado antes de que el programa lo comparara con el input del usuario.El tercer password es
ThirdAndFinal!!!, obtenido leyendo el stack con GDB después de la operación XOR con clave 0x13.Conexión al Servidor
$ nc 154.57.164.82 30540
What is the file format of the executable?
> ELF
[+] Correct!
What is the CPU architecture of the executable?
> x86-64
[+] Correct!
What library is used to read lines for user answers?
> libreadline
[+] Correct!
What is the address of the `main` function?
> 0x401172
[+] Correct!
How many calls to `puts` are there in `main`?
> 5
[+] Correct!
What is the first password?
> PasswordNumeroUno
[+] Correct!
What is the reversed form of the second password?
> 0wTdr0wss4P
[+] Correct!
What is the real second password?
> P4ssw0rdTw0
[+] Correct!
What is the XOR key used to encode the third password?
> 0x13
[+] Correct!
What is the third password?
> ThirdAndFinal!!!
[+] Correct!
[+] Here is the flag: HTB{l1c3ns3_4cquir3d-hunt1ng_t1m3!}
Flag
FLAG OBTENIDA
Reflexión Final
Qué salió bien
El reconocimiento inicial con strings identificó el primer password y los strings ofuscados. La función reverse() fue fácil de identificar por su nombre en los símbolos. El uso de GDB para leer el stack después del XOR fue la técnica más eficiente para obtener el tercer password.
Qué salió mal
La clave XOR se confundió inicialmente. El argumento edx = 0x11 es la longitud, no la clave. La clave es ecx = 0x13. Conocer el orden de los argumentos en la calling convention SysV AMD64 es crucial para no confundirlos.
También se intentó adivinar la flag en lugar de conectarse al servidor remoto, perdiendo tiempo innecesariamente.
Lecciones aprendidas
Este challenge enseñó tres niveles de ofuscación de passwords en un solo binario:
-
Texto claro: visible directamente con
strings -
Reverse: string invertido, recuperable con Python
-
XOR: cifrado con clave, recuperable con GDB o Python
La diferencia entre los argumentos de una función en assembly es crítica. Siempre verificar cuál registro corresponde a cuál parámetro usando la calling convention SysV AMD64: RDI, RSI, RDX, RCX, R8, R9.
Tiempo total
| Fase | Tiempo |
|---|---|
| Reconocimiento inicial | 10 minutos |
| Password 1 y 2 | 10 minutos |
| Análisis XOR + GDB | 30 minutos |
| Conexión al servidor | 10 minutos |
| Total | 1 Hora |