7

When I'm penetration testing I sometimes notice undesirable behaviour of a PHP script when I inject a single or multidimensional array using the HTTP GET parameters.

Possible availability risk

This can cause an availability risk, especially in case of a multidimensional array injection when a PHP script is recursively looping through a (deep) multidimensional array and there is no or a high limit on execution time. Because this can cause resource exhaustion. Practically this can be the case when the whole $_GET array is looped through to apply some kind of filtering or validation.


Questions regarding this behaviour

  1. What would be the proper term for such a vulnerability? "Array injection"?
  2. In what vulnerability category does this belong (for example in the OWASP Top 10 or CWE list)?
  3. Is this, other than the availability risk, considered to be a vulnerability?
  4. What is a good scenario wherein this can be exploited?

Example of behaviour

Input: (expect string as parameter)

/search.php?query=test

Result: (in PHP's $_GET)

[query] => test

Input: (injected array)

/search.php?query[]=test

Result: (in PHP's $_GET)

[query] => Array
(
    [0] => test
)

Input: (injected multidimensional array)

/search.php?query[][][][]=test

Result: (in PHP's $_GET)

[query] => Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => Array
                        (
                            [0] => test
                        )
                )
        )
)
5
  • If the expected parameter is query=, the page won't mistakenly take query[]= in its place (did you intend to imply so?). The third scenario would only occur if query[][][][]= is expected by the page, and so I wouldn't consider it an injection.
    – Robbie
    Jun 22, 2016 at 10:52
  • 1
    @Robyn I clarified the question with some examples. The script does handle query[][][]= as $_GET['query'] and not as $_GET['query[][][]'].
    – Bob Ortiz
    Jun 22, 2016 at 11:04
  • This looks like it could be a serious command injection vulnerability since PHP is accepting all kind of statements in the brackets. Have you tested if the behavior of the application differs if you enter things like ?query[1==1]=foo, ?query[die()]=foo, ?query[-1]=foo ?
    – Noir
    Jun 22, 2016 at 14:32
  • 1
    @Noir why do you expect that your example will work in such a way? I did test it but I don't seem to get why this will possible work, unless the application does something special with the array keys. Can you maybe work it out in an answer?
    – Bob Ortiz
    Jun 24, 2016 at 8:02
  • 1
    @Noir You can't use = and & in $GET/$_POST array name.
    – Xavier59
    Jun 30, 2016 at 12:31

4 Answers 4

4
+50

What would be the proper term for such a vulnerability? "Array injection"?

In what vulnerability category does this belong (for example in the OWASP Top > 10 or CWE list)?

This is called "parameter tampering" and has been called as such by PHP developers, OWASP, and falls under different CWE numbers with the parent being CWE-371, e.g. when the paremeter involves injection SQL statement, it becomes a SQL Injection. All you are doing is manipulating (tampering) with the input in an effort to produce an adverse affect.

Is this, other than the availability risk, considered to be a vulnerability?

The vulnerability would be that the server fails to sanitize input

What is a good scenario wherein this can be exploited?

This depends on the application itself. You would have to continue manipulating input in order to see what could/would happen under different circumstances. For example, have you tried inserting commands nested within the tampering? E.g.:

/search.php?query[() { example;};echo \"Content-type: text/plain\"; echo; echo; /bin/cat /etc/shadow"]=test

Without understanding the underlying application, it is hard to determine what is vulnerable. The fact that input is not validated, you should be able to test all sorts of things to inject in order to try to get the server to respond to your tampered data. You could insert large amounts of data to see if you could trigger a stack/buffer/heap overflow. There are a few things you could try but again, without knowing the function of the initial application, this becomes a guessing game

7

I think the term HTTP parameter pollution would be the best fit. This type of pollution usually submits the same parameter multiple times with different values in the same request. This results in the parameter being treated as an array of values with the parameter name as the array name as described in this article. The way the web server and programming language handles these arrays compared to the single value is what this bug class exploits.

Your examples similarly cause an array with the same name, although through a different method to repeating the parameter, but it seems like a better fit compared to other terms like fault injection or type confusion.

While this type of pollution usually exploits undefined behaviour, the examples you have provided would not. They would cause defined behaviour for handling an array. This may still trigger exceptions which could be used to alter the flow of an application, such as erroring out before a authorisation check changed your session resulting in a privilege escalation.

0
4

Being able to use arrays in $_GET in this way is in itself not a security vulnerability. The DoS vector seems negligible to me, although this may indeed be amplified by the code using it.

However, the examples you provided missed the most important part of this feature (from an attackers point of view): it is not only possible to pass regular arrays in GET and POST parameters this way, but also associative arrays. In fact, PHP does not destinguish between the two:

PHP arrays can contain integer and string keys at the same time as PHP does not distinguish between indexed and associative arrays. – PHP: Arrays - Manual

This means that if a client supplies POST parameters like this:

<input type="text" name="pass[pass1]" value="Password 1">
<input type="text" name="pass[pass2]" value="Password 2">

$_POST / $_GET will look like this:

$_POST = array(
    ...
    'pass' => array(
        'pass1' => 'Password 1',
        'pass2' => 'Password 2', 
    ),
    ...
);

But if the server was expecting it to be a normal array instead of an associative one, it could lead to a security vulnerability:

//Simplified example, bug is possible with parameterized queries as well
//Expects ?pass[]=password1&pass[]=password2
$query = 'INSERT INTO users (index, password) ';
$parts = array();

foreach($_GET['user'] AS $index=>$value){
  parts[]=" VALUES ({$index}, ".$pdo->real_escape_string($value).')';
}

$query .= implode(", ", $parts);
$pdo->exec($query);//

$query with expected input:

INSERT INTO USERS (index, password) VALUES(0, 'password1'), VALUES(1, 'password2')

$query with ?pass[0,'');DROP DATABASE DATABASE();--]=foo:

INSERT INTO USERS (index, password) VALUES(0,'');DROP DATABASE DATABASE();--, 'foo')

BOOM.

In fact, this is the exact same mistake that lead to the security flaw called "Drupalgeddon" – and it received that name for a good reason.

A vulnerability in this API allows an attacker to send specially crafted requests resulting in arbitrary SQL execution. Depending on the content of the requests this can lead to privilege escalation, arbitrary PHP execution, or other attacks. – SA-CORE-2014-005 - Drupal core - SQL injection

Automated attacks began compromising Drupal 7 websites that were not patched or updated to Drupal 7.32 within hours of the announcement of SA-CORE-2014-005 - Drupal core - SQL injection. You should proceed under the assumption that every Drupal 7 website was compromised unless updated or patched before Oct 15th, 11pm UTC, that is 7 hours after the announcement. – Drupal Core - Highly Critical - Public Service announcement - PSA-2014-003

It was also the vulnerability that most likely lead to the disclosure of the Panama Papers (probably should've installed those security updates…).

The Drupal vulnerability was remedied by updating the code to pass the received array to array_values(), which returns an indexed array containing only values of the array it received, thus getting rid of any malicious keys.

The example code could be fixed in the same way:

Before:

foreach($_GET['user'] AS $index=>$value){

After:

foreach(array_values($_GET['user']) AS $index=>$value){

More information about Drupalgeddon, including a working exploit: Drupal 7: Drupalgeddon Exploit

Note that this stuff works the same way, regardless if you're working with $_GET or $_POST. I'm not sure about $_REQUEST and $_COOKIE.

3
  • 2
    Looks like typical SQL-injection to me. Anybody who constructs SQL queries with user input by string manipulation isntead of binding should be shot. Jun 24, 2016 at 13:03
  • @Oleg I used PDO->exec because I could write a more concise example that way, but the Drupal vulerability mentioned used parameterized queries. They generated placeholders like :name_{$index} without realizing that the index could be non-numeric because of associative arrays. See the linked blog post and the relevant source code (now fixed). Jun 24, 2016 at 13:12
  • 1
    Yeah, still SQL-injection. Keys were used verbatim to construct SQL query - the article you've linked even says so itself "This is where SQL could have been injected to exploit any website running Drupal." Jun 24, 2016 at 13:19
0

An other possible attack exist, such as with MongoDB.

If you are passing $_GET (or $_POST) parameters to your queries, make sure that they are cast to strings first. Users can insert associative arrays in GET and POST requests, which could then become unwanted $-queries.

A fairly innocuous example: suppose you are looking up a user's information with the request http://www.example.com?username=bob. Your application does the query $collection->find(array("username" => $_GET['username'])).

Someone could subvert this by getting http://www.example.com?username[$ne]=foo, which PHP will magically turn into an associative array, turning your query into $collection->find(array("username" => array('$ne' => "foo"))), which will return all users not named "foo" (all of your users, probably).

http://php.net/manual/en/mongo.security.php

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .