The vulnerability to be detected for this challenge was a resource (variable) injection that enabled authentication to be bypassed. Solving the challenge requires knowledge of certain PHP functions.

Note: This article is also available in french 🇫🇷. The challenge was announced in this tweet 🐦.

Explanation

The problem lies mainly in using the extract() function on unsafe data (user input).

extract() allows you to import variables from an array into the symbol table.

As a reminder, array() are not arrays in the classical meaning of other languages, but are map (association of a key and a value) sometimes called associative arrays (Dictionary in Python, Hash in Ruby, etc.).

extract() will therefore take an associative array and create variables whose names are the indexes/keys of this array, and assign them the associated value.

Using extract() on an associative array from a user input allows the user to arbitrarily create variables.

The vulnerable code was as follows:

if (isset($_REQUEST['color']['color']))
  extract($_REQUEST['color']);

Note: the extract page of the PHP manual issues a warning about this coding error.

The extract($config); above is quite benign, as the data is not user-controllable.

Note: the $_SERVER['PHP_AUTH_USER']} in the welcome message is bad practice, as it’s a user input that is not sanitized. On the other hand, there is no security risk in this case, as the message is only displayed once the user has been authenticated and it has been validated that $_SERVER['PHP_AUTH_USER']} corresponds to the user’s name (except, of course, in the case of resource injection presented here).

Let’s analyze extract($_REQUEST['color']):

  • $_REQUEST is a global variable which retrieves all entries from $_GET, $_POST and $_COOKIE.
  • The use of the name color in $_REQUEST['color'] might lead you to believe that it only allows you to modify the $color variable extracted from the $config associative array. But this is not the case: the name of the HTTP parameter is arbitrary, and extract can be used to create or modify any variable.
  • Without argument, by default extract() will use the EXTR_OVERWRITE flag, so if there is a collision when extracting a variable (if it already exists) then its contents will be overwritten. If the variable does not already exist, then it will be created.

So extract() + $_REQUEST allows you to overwrite any variable from an HTTP GET or POST parameter.

Let’s now analyze the if condition (isset($_REQUEST['color']['color'])): this is a semblance of protection that simply checks that the color key is present in the color parameter and therefore that the color parameter is of array type.

The following requests will therefore not be allowed:

At the very least, the color key must be present, i.e., for example, http://127.0.0.2:8080/app.vuln.php?color[color]=green, which is the case that seems to have been planned to be able to change the background color of the text.

However, the application checks that the color key exists, but not that no other key is present. It is therefore perfectly possible to add a second key in addition to color: http://127.0.0.2:8080/app.vuln.php?color[color]=green&color[messages]=void.

Apart from breaking the application’s behavior, this doesn’t seem to get us very far.

This behavior should be correlated with the following code:

if (!empty($credentials)) {
  login($credentials['user'], $credentials['password']);
}

In fact, authentication is only performed if $credentials is not empty. No authentication if the configuration is left empty. With extract, we could overwrite $credentials with an empty-like value by empty to bypass authentication. For example, 0 is evaluated as empty.

The following payload can be used to bypass authentication:

http://127.0.0.2:8080/app.vuln.php?color[color]=green&color[credentials]=0

Note: putting nothing (null) rather than 0 also works.

Fixed code

Here is the corrected code:

Fixed code

Simply reassign a value to the $color variable when a user input is entered, there’s no risk of overwriting another variable and XSS is not possible thanks to URL encoding.

Diff

The source code is available on the Github repository Acceis/vulnerable-code-snippets and on the website acceis.github.io/avcs-website.

About the author

Article written by Alexandre ZANNI aka noraj, Penetration Testing Engineer at ACCEIS.