Como resolver Impossible Password (HTB)

René Silva Valdés
Challenges  HTB
Published in
4 min readNov 11, 2019

Guía para resolver el reversing challenge “Impossible Password” en Hack The Box.

El archivo del challenge contiene un binario (.bin) que si ejecutamos nos dará la opción de ingresar un input para luego finalizar su ejecución. Para analizar este tipo de archivos existen varias herramientas disponibles tales como IDA, Binary Ninja o Radare 2. En este writeup usaremos la herramienta Ghidra.

Ejecución y comando file del archivo impossible_password.bin.
Interfaz Ghidra.

Creamos un nuevo proyecto, importamos el binario y aceptamos los mensajes que aparezcan (para realizar un análisis automático al archivo). Exploramos las funciones en la sección “Symbol Tree” y nos encontraremos con la siguiente función (en la sección de decompilador):

void FUN_0040085d(void){
int iVar1;
char *__s2;
undefined local_48;
undefined local_47;
undefined local_46;
undefined local_45;
undefined local_44;
undefined local_43;
undefined local_42;
undefined local_41;
undefined local_40;
undefined local_3f;
undefined local_3e;
undefined local_3d;
undefined local_3c;
undefined local_3b;
undefined local_3a;
undefined local_39;
undefined local_38;
undefined local_37;
undefined local_36;
undefined local_35;
char local_28 [20];
int local_14;
char *local_10;

local_10 = "SuperSeKretKey";
local_48 = 0x41;
local_47 = 0x5d;
local_46 = 0x4b;
local_45 = 0x72;
local_44 = 0x3d;
local_43 = 0x39;
local_42 = 0x6b;
local_41 = 0x30;
local_40 = 0x3d;
local_3f = 0x30;
local_3e = 0x6f;
local_3d = 0x30;
local_3c = 0x3b;
local_3b = 0x6b;
local_3a = 0x31;
local_39 = 0x3f;
local_38 = 0x6b;
local_37 = 0x38;
local_36 = 0x31;
local_35 = 0x74;
printf("* ");
__isoc99_scanf(&DAT_00400a82,local_28);
printf("[%s]\n",local_28);
local_14 = strcmp(local_28,local_10);
if (local_14 != 0) {
/* WARNING: Subroutine does not return */
exit(1);
}
printf("** ");
__isoc99_scanf(&DAT_00400a82,local_28);
__s2 = (char *)FUN_0040078d(0x14);
iVar1 = strcmp(local_28,__s2);
if (iVar1 == 0) {
FUN_00400978(&local_48);
}
return;
}

Esta función llama la atención por varios motivos. Primeramente porque aparece un string en texto plano que hace referencia a una contraseña y en segundo porque se hace print del carácter “*”, el cual podemos leer cada vez que ejecutamos el binario. Como podemos observar a partir del código, la ejecución del programa dependerá de una comparación del string en texto plano antes mencionado. Si ingresamos dicho string, el binario se ejecutará de la siguiente forma:

A partir de esta ejecución, podemos intuir que sería necesaria una segunda palabra clave. Si continuamos el análisis de la función de arriba (FUN_0040085d), notamos que la segunda parte del binario llama a otra función:

void * FUN_0040078d(int iParm1){
int iVar1;
time_t tVar2;
void *pvVar3;
int local_c;

tVar2 = time((time_t *)0x0);
DAT_00601074 = DAT_00601074 + 1;
srand(DAT_00601074 + (int)tVar2 * iParm1);
pvVar3 = malloc((long)(iParm1 + 1));
if (pvVar3 != (void *)0x0) {
local_c = 0;
while (local_c < iParm1) {
iVar1 = rand();
*(char *)((long)local_c + (long)pvVar3) = (char)(iVar1 % 0x5e) + '!';
local_c = local_c + 1;
}
*(undefined *)((long)pvVar3 + (long)iParm1) = 0;
return pvVar3;
}
/* WARNING: Subroutine does not return */
exit(1);
}

Esta función puede ser complicada de entender y/o analizar, sin embargo, lo que debemos rescatar es que cumplir con la segunda condición ingresando la segunda palabra clave es, a lo menos, muy difícil (de ahí el nombre y la descripción del challenge). Tomando esto en cuenta, seguimos con el análisis de FUN_0040085d y observamos que esta llama a FUN_00400978 cuando se cumple la segunda condición, por lo que analizamos dicha función:

void FUN_00400978(byte *pbParm1){
int local_14;
byte *local_10;

local_14 = 0;
local_10 = pbParm1;
while ((*local_10 != 9 && (local_14 < 0x14))) {
putchar((int)(char)(*local_10 ^ 9));
local_10 = local_10 + 1;
local_14 = local_14 + 1;
}
putchar(10);
return;
}

Como podemos observar, esta función muestra en la salida estándar el resultado de una operación XOR (operador ^) dentro de un while. Si recreamos la ejecución de esta función realizando la operación XOR con el primer input entregado en FUN_0040085d (local_48 = 0x41 = 65) y nos ayudamos de una tabla ASCII, podremos notar que el primer carácter que se mostrará es una “H” (si, la de “HTB{flag}”), por lo si ejecutamos esta función de acuerdo a como lo hace el programa (podemos ayudarnos de un script) obtendremos la flag.

Hint: Sin entrar en detalles técnicos, la operación XOR se realiza para cada uno de los caracteres definidos en FUN_0040085d, respetando el orden en el cual se definen.

A partir de este challenge, hemos adquirido algunas nociones sobre reversing y sobre el uso de la herramienta Ghidra, además de reforzar nuestros conocimientos del lenguaje C. Seguramente existen formas más sencillas (y documentadas en Internet) de obtener la flag, sin embargo, un objetivo propuesto al comenzar con este challenge fue resolverlo utilizando Ghidra, herramienta que probablemente volvamos a ver más adelante y que es bueno tener en nuestro kit de herramientas junto a tantas otras.

Muchas gracias por leer mi publicación, espero que haber sido de ayuda. Si tienes alguna duda u observación puedes comunicarte usando la sección de comentarios :). Pronto subiré más writeups de challenges de Hack The Box.

--

--