I was just looking at some PHP code for one of our clients, and found a case I haven't seen many times before. I thought I should share it here.
The code I was looking at looks like this:
<?php
// Init the PHP array with some SQL code to start the query
$declareSQLArray = InitializedArray('stuff');
// Use a strong enough validation routine for do the input
// validation of POST variables
while(list($name, $value) = each($_POST)) {
if(!is_array($value))
$$name = StrongValidation($value);
else
$$name = $value;
}
// Do something with my variables and always do a proper
// validation when I use the data
// Eventually, build my SQL command, and send this to the DB
$sql_command = join(' ', $declareSQLArray);
mysql_query($sql_command);
?>
The code, even if horribly constructed, does not seem to show important
weaknesses, but the usual case of submitting a POST variable as an array, and
bypassing the StrongValidation. Then, in that case, it would have
failed every other validation routines in the code.
Even if experienced with PHP, you might not have encountered variable variables before. In short, this allows to dynamically declare named variables. Here is a simple example:
hubert:~ Romain$ php -r '$name="foo"; $$name="Hello World!\n"; echo $foo;' Hello World!
Here, the variable $foo gets declared, and assigned using PHP's
variable variables capabilities.
Getting back to our code example, I'm sure the reader will spot the issue,
and what an attacker can do to exploit such scenario to trigger, in that case,
a SQL injection. Since the variable $declareSQLArray is defined
and initialized before the POST variables lookup, it is possible to reassign it
using the variable variables. In that case, no validation is performed when we
submit an array, and this is exactly what we want to do!
To exploit the SQL injection, you only need to submit POST variables to
overwrite the $declareSQLArray, and add the content that we want
in it!
POST /code_example.php HTTP/1.1 Host: example.com ... declareSQLArray%5B%5D=SELECT...;&declareSQLArray%5B%5D=--&whatever...
Job done! The resulting SQL query will start with the payload that was
submitted as part of $declareSQLArray. You've got your SQL injection.
Update: While driving back home, I was wondering if I could overwrite values from the SESSION using this technique. A couple of lines of code, and POST request after the answer is short: YES.
Imagine that you have an isadmin variable as part of the
session (which is an associative array). This variable would be set in a code
like this:
if ($user->isNotAdmin())
$_SESSION['isadmin'] = 0;
else
$_SESSION['isadmin'] = 1;
Exploiting the previous weakness of the code example, we are able to
overwrite the $_SESSION['isadmin'] content, only by supplying what
will be interpreted as an associative array by PHP:
POST /code_example.php HTTP/1.1 Host: example.com ... _SESSION%5Bisadmin%5D=1&whatever...
I'm sure you're thinking, as I do, that this is getting more interesting!
Anyways, this issue is not new at all, it is known as Dynamic Variable Evaluation (thanks to Steve Christey).
The interesting part of it is that DAST won't be able to detect it (or maybe
if you are lucky enough), and it is very hard for a SAST to deal with it
(actually, I doubt any SAST vendor who supports PHP handles this case, but it's
not impossible since they have all they need to solve the problem).
Update 2: Based on the comments, I did some testing and observed that even if we can overwrite data from the session, this data does not get persisted in the session. This means that you can still control a value from a super global for the remaining execution of the script, but cannot persist the data.


Last comments