Skip to main content

Solución del CTF Fetch the Flag 2022: Roadrunner

Escrito por

Giovanni Funchal

feature ctf roadrunner

10 de noviembre de 2022

0 minutos de lectura

¡Gracias por jugar Fetch con nosotros! Felicitaciones a los miles de jugadores que participaron en Fetch the Flag CTF. ¡Y muchísimas gracias a los Snykers que crearon, probaron y documentaron los desafíos!

Si te gustan las salas de escape, te encantará el desafío Roadrunner de la competencia Fetch the Flag 2022 de Snyk. En esta publicación del blog, explicaré cómo abordé y resolví el desafío al explotar una vulnerabilidad de validación de entradas en el código que nos permite escapar de un sandbox.

Desafío

¿Puedes correr más rápido que un correcaminos? ¡Ni de broma!

Este desafío empieza provocándonos. ¿Encontraremos la flag?

Primero, tenemos acceso a una página web donde encontramos algo parecido a un entorno de programación en línea. Estos entornos suelen usarse para aprender o experimentar sin tener que instalar una cadena de herramientas de un lenguaje. Go incluso tiene uno oficial.

Podemos escribir código Go y hacer clic en Run. El código se envía a un backend, que lo ejecuta y devuelve el resultado; luego, la página web lo muestra.

También tenemos el código fuente Go del servicio backend que ejecuta esa página web (roadrunner.go), así como el Dockerfile que se usa para empaquetar el servicio.

Nuestro primer paso es revisar las pistas que nos dieron y explorar un poco.

Guía paso a paso

Explorar

La curiosidad suele ser una buena habilidad para resolver estos desafíos. Por eso, lo primero que hice fue echar un vistazo y probar algunas cosas. No programo en Go, así que busqué en línea un ejemplo de “hello world”, lo pegué en la página web, hice clic en Run y obtuve un “hello world”. Hasta ahí, todo bien.

Sandbox de Roadrunner que muestra código de Go que imprime «hello world», con un botón para ejecutar y un panel de salida que muestra «hello world»

Este ejemplo de “hello world” usa la biblioteca fmt para imprimir un mensaje en la salida.

Después, inspeccioné el código fuente de la página web con el navegador. Noté que el botón Run estaba vinculado a una acción de submit en un formulario.

Fragmento de código HTML que muestra un formulario con una etiqueta «Roadrunner Sand…» y un botón Ejecutar

Luego revisé el controlador de JavaScript, que envía una solicitud POST al endpoint /run del backend y, como era de esperar, establece el contenido del cuadro de resultados a partir de la respuesta.

Código JavaScript que procesa el envío de un formulario y envía datos de un script a un endpoint /run mediante una solicitud POST

Luego, centré mi atención en el código fuente del backend que se proporciona en el desafío. Empecemos por el Dockerfile:

Código de Dockerfile que muestra argumentos para las versiones de Debian y Go, comandos de instalación de paquetes y la copia de flag.txt al directorio de la aplicación

¡Encontramos la flag! Podemos ver que la imagen implementada contiene un archivo flag.txt en el directorio raíz. Ahora solo tenemos que capturarlo. Necesitamos encontrar una forma de leer el contenido de este archivo.

Encontrar una pista

Veamos roadrunner.go. La función main siempre es un buen punto de partida.

Fragmento de código Go que muestra cómo se inicia un servidor Roadrunner y escucha en un puerto local

Aquí vemos los dos endpoints, / y /run. Primero veamos /, que está gestionado por la función welcome.

Editor de código que muestra una función de bienvenida en Go que analiza y ejecuta una plantilla index.html.

El código usa la biblioteca de Go html/template para generar el HTML de la página web; lee el archivo index.html e interpola atributos. Interesante.

Ahora veamos el endpoint /run, que está gestionado por la función runner.

Editor de código con tema oscuro que muestra un controlador HTTP de Go que decodifica una solicitud, sanitiza y ejecuta un script, y escribe el resultado.

Aquí pasan varias cosas interesantes. Primero, el código crea un sandbox, toma el script de entrada de la solicitud y lo escribe en un archivo dentro del sandbox. Después, sanitiza el script para comprobar si es seguro ejecutarlo y, posiblemente, rechazar la entrada. Si la acepta, ejecuta el script, captura el resultado y lo devuelve en la respuesta.

Al revisar esto, primero noté que el campo dirname de la estructura Sandbox también se decodifica desde el JSON de la solicitud y, por lo tanto, el usuario puede controlarlo. Luego, este campo se usa para crear una ruta donde se escribe un archivo, lo cual constituye una vulnerabilidad. Sin embargo, no estaba segura de cómo explotarla, así que decidí volver a centrarme en el paso de sanitización. Veamos más de cerca sanitizeScript.

Código Go para sanitizeScript que bloquea la manipulación de archivos, las llamadas al sistema y las importaciones de redes.

Este código analiza el script de entrada y luego revisa las importaciones. Hay una lista de bloqueo con las bibliotecas de Go más comunes para manipular archivos. Si nuestro script de entrada importa alguna de ellas, se rechazará con un mensaje gracioso.

Recuerda que nuestro objetivo es leer el contenido del archivo /flag.txt.

Al principio pensé que quizá esta lista no era exhaustiva o que había otras formas de leer archivos. Como mencioné antes, no programo en Go, así que hice algunas búsquedas en línea.

La revelación

¿Recuerdas algún otro lugar que hayamos visto antes donde se leen archivos?

Fragmento de código que muestra un controlador de bienvenida en Go que analiza y ejecuta una plantilla index.html.

Sí, es otra vez la función welcome. Claramente está leyendo un archivo y usa la biblioteca de plantillas, que no está en la lista de bloqueo de sanitizeScript. ¿Quizá podamos usarla?

Así que consulté la documentación de esta biblioteca. Después de crear un objeto Template, ejecutamos la plantilla, que escribe el resultado en el writer.

Documentación de código que muestra el método `Execute` de Go para una `Template`, con sus parámetros writer, data y error

Tenemos que convertir la interfaz io.Writer en una cadena que luego podamos imprimir. Después de buscar un poco más en línea, descubrí que podía usar strings.Builder (la biblioteca strings tampoco está en la lista de bloqueo).

Así que nuestra solución final queda así:

Captura de pantalla titulada «Roadrunner Sandbox» que muestra código Go que analiza una plantilla flag.txt e imprime el resultado procesado.

Al ejecutar este fragmento, se vuelca todo el contenido del archivo flag.txt en la salida. ¡Lo logramos!

Conclusión sobre Roadrunner

¿Qué aprendimos? Usamos una entrada contaminada y diseñada cuidadosamente para explotar una vulnerabilidad de validación de entradas en un entorno de programación en línea. Escapamos del sandbox debido a una falla en la sanitización del script. Conocer un poco el funcionamiento interno de la aplicación nos ayudó a pasar de la idea a la explotación muy rápido. Esto era solo un desafío de CTF. En una situación real, construir este tipo de exploit podría requerir algo de ensayo y error, además de ciertas conjeturas.

Snyk Code, una herramienta gratuita de pruebas de seguridad de aplicaciones estáticas (SAST), puede ayudar a encontrar y corregir vulnerabilidades de validación de entradas en tu código.

Otras técnicas, como la seguridad por capas, también pueden dificultar este exploit. Por ejemplo, se podría modificar la función runScript para ejecutar el script como otro usuario con permisos reducidos. En este caso, la vulnerabilidad no habría podido explotarse por sí sola, a menos que se combinara con una segunda vulnerabilidad, como una escalada de privilegios.

Ejecutar de forma segura código no confiable en un sandbox es notoriamente difícil. Los sandboxes están diseñados para impedir que las aplicaciones accedan a otros recursos y datos. En la práctica, lo mejor es evitar por completo la ejecución de código no confiable, si es posible.

Eso es todo, amigos. ¡Espero que lo hayan disfrutado! ¿Quieres saber cómo encontramos las demás flags? Visita nuestra página de soluciones de Fetch the Flag para ver cómo lo hicimos.

Comienza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.

Leer más

Blog

Los modelos de frontera encontraron las vulnerabilidades. Solo el atacante encontró las cadenas.

El análisis estático encontró las fallas, pero solo las pruebas de ataque en vivo demostraron cómo podían encadenarse para provocar brechas. Una comparación de Evo COS, Claude Security y Claude Code Security.

feature insights context
Blog

Los ataques autónomos ya están aquí. La defensa debe estar a su altura.

Los atacantes autónomos están reduciendo el tiempo disponible para defenderse. Descubre cómo el descubrimiento, la corrección, la validación y la prevención continuos pueden ayudar a los equipos de seguridad a seguirles el ritmo.

Blog

Por qué los agentes de programación con IA siguen generando fallas de control de acceso

Los agentes de programación con IA pueden generar lógica de autorización que compila y supera la revisión, pero expone los datos de un inquilino a otro. Descubre por qué es difícil detectar el control de acceso roto y cómo prevenirlo.