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!