What a silly WAF. The first hurdle is getting past the check for bad
words which blacklists anything looking like path traversal, stream
wrapper protocols or the file name "flag". After staring at the code
for way too long, I noticed the check is done before processing the
JSON. What this means is that if you manage to craft JSON which
doesn't trigger the WAF before decoding, but does evil things after
decoding, you're in. In this case it's as easy as escaping any of the
problematic letters using its unicode representation:
- `{"page": "./index.html "}`: Prints the index.html file
- `{"page": "/flag"}`: Prints an error
- `{"page": "/\u0066lag"}`: Prints a redacted flag
That's right, there's another hurdle to clear:
// no data exfiltration!!!
$content = preg_replace('/HarekazeCTF\{.+\}/i', 'HarekazeCTF{<censored>}', $content);
echo json_encode(['content' => $content]);
Using the same trick we can use the `php:` stream wrapper protocol
which supports filtering stdout using a PHP string function. Did you
know PHP supports rot13 out of the box?
- `{"page": "php\u003a//filter/read=string.rot13/resource=/\u0066lag"}`: Prints out the unredacted flag