Solución del CTF Fetch the Flag 2022: Juggalo Central
Alexander Kraev
10 de noviembre de 2022
0 minutos de lectura¡Gracias por participar en Fetch con nosotros! Felicitaciones a los miles de jugadores que se unieron a nosotros enFetch the Flag CTF. Y muchas gracias a los Snykers que crearon, probaron y documentaron los desafíos!
Si estuviste en Fetch the Flag 2022 de Snyk y no pudiste terminar el desafío Juggalo Central, llegaste al lugar indicado. ¡Veamos juntos la solución!
Guía paso a paso
Para empezar, hay un enlace a un sitio web que contiene el típico formulario de inicio de sesión con dos campos: username y password. También hay un archivo con extensión .php. Primero, echemos un vistazo al sitio web.
El sitio web
Como mencioné antes, la página contiene dos campos de entrada (y una imagen de un payaso). Primero revisé el código fuente de la página, el archivo .css e incluso la imagen. No encontré nada que pudiera usar. Noté algo: había dos mensajes de error diferentes, uno cuando usaba un nombre de usuario estándar como “admin” y otro cuando usaba una cadena aleatoria como “qweasdzxc”.
El código
Dentro de index.php, vi tres funciones: getFlag, searchUserByName y validateCredentails.
La primera función, getFlag, consulta una tabla de la base de datos llamada flag y devuelve la primera fila que encuentra. Hice algunos cálculos y llegué a la conclusión de que la clave que busco está almacenada en esta tabla.
La segunda función, searchUserByName, intenta encontrar un usuario según la variable de entrada llamada $username. Si hay resultados, la función los devuelve. Noté una cosa en esta parte, la línea:
Eso significa que esta función no devolvería una sola fila (aunque espero que los usuarios tengan nombres de usuario únicos), sino que devolvería todos los resultados si hay más de un usuario con el mismo nombre de usuario. Más adelante descubrí que esto no nos sirve por lo que sucede en la siguiente función. A veces eso pasa en un CTF.
La tercera función, validateCredentails, lee el nombre de usuario y la contraseña proporcionados en el cuerpo de la solicitud POST y llama a searchUserByName para comprobar si existe un usuario con ese nombre de usuario. Aquí también entendí por qué el mensaje de error era diferente para un nombre de usuario con caracteres aleatorios. Si el usuario proporcionaba un nombre de usuario inexistente, la función devolvía: "Juggalo not found!". En cambio, para cualquier nombre de usuario existente con una contraseña incorrecta, el mensaje de error era: "Invalid credalo".
Cómo obtener un nombre de usuario válido
Ahora vamos a buscar credenciales válidas. Cuando probé algo tan común como “admin”, vi el mensaje de error "Invalid credalo". Con el análisis del código del paso anterior, puedo deducir que existe un usuario cuyo campo de nombre de usuario tiene el valor “admin”.
La primera hipótesis
Ahora que tenemos un nombre de usuario válido, veamos cómo hackear la contraseña. El mensaje de error que indica que no tenemos acceso a este usuario se debe a la siguiente línea de código:
En el lado izquierdo de la condición, se obtiene la contraseña del usuario de la base de datos. En el lado derecho están los primeros 20 caracteres del hash md5, que contiene dos variables proporcionadas por el usuario: username y password.
Al principio pensé que, debido a esta condición, cualquier contraseña válida debía ser una cadena de 20 caracteres. En resumen: me equivoqué.
La segunda hipótesis
Después de pensar un rato en el desafío, se me ocurrió otra idea. Esta línea usa el operador de comparación !=. Así que consulté la documentación oficial de PHP para revisar cómo funcionan los operadores de comparación en PHP. Una línea de la documentación que no entendía estaba junto al operador != y decía:
Espera. ¿Qué es la “conversión de tipos”? Después de leer eso, volví a abrir la página web del desafío. El payaso seguía ahí, mirándome.
El hackeo
Resulta que en PHP existe algo llamado “conversión de tipos”. Después de leer sobre el tema y hacer algunas pruebas de PHP en línea, llegué a la siguiente condición
Sorprendentemente, esto devolvió “true”. Esto sucede porque PHP convierte ambos operandos en valores numéricos. Ahora, para encontrar la contraseña correcta del usuario “admin”, solo tenemos que generar un hash md5 que cumpla dos condiciones: que empiece por “0e” y que solo contenga números después (o al menos 18 caracteres después, por el uso de substr).
El script
Aquí tienes un script de Python que puede encontrar el hash correcto:
Después de un minuto, pude iniciar sesión en la página web de Juggalo Central con las credenciales generadas por este script.
Más soluciones de Fetch the Flag
¿Quieres saber cómo encontramos todas las demás banderas? Visita nuestra página de soluciones de Fetch the Flag para ver cómo lo hicimos.
