davidnoren.com


Hi there. All you will find here are a few musings about information security and other topics I find personally interesting. If you want to know what I'm working on, find me on GitHub.


PHP extract() Vulnerability

PHP has a function named extract() to take all provided GET and POST requests and assign them to internal variables. Developers will, at times, use this function instead of manually assigning ‘$_POST[var1]’ to ‘$var1’. This function will overwrite any previously defined variables, including server variables. Extract() has options which prevent overwriting previously-defined variables, however this safety is not enabled by default, and developers might not enable the safety, just as many do not perform input validation. This vulnerability is similar in design to the register globals vulnerabilities present in PHP.

Note: This post was originally written on a WordPress-powered blog, and has been exported and imported to this blog platform. As a result, unfortunately, not all markdown, code, and output looks as pretty as it once did.

A sample application (available on GitHub) below uses extract() to assign POST requests (which, in PHP, are by default entered into the $ _POST array) to internal variables onto an idea submission page. A snippet of the vulnerable code from the site is included below:

//Start Session Management session_start(); //start session - $_SESSION variables assigned if(!isset($_SESSION['admin'])){ $_SESSION['admin'] = false; //set user to non-admin if not defined } //Done with Session Management extract($_POST); //explode the $_POST array to internal variables

Two vulnerabilities were available for exploitation because of this, both involving writing over an element of the $_SESSION array, which is a pre-defined PHP variable for session management. The application used two session variables: $_SESSION[admin] and $_SESSION[captcha].

The following two vulnerabilities were present due to the extract($_POST); line in the PHP code above. Or, more exactly, the extract(); function being after the session_start(); function.

CAPTCHA Bypass Vulnerability

The application was vulnerable to CAPTCHA bypass because of the improper use of extract(); in the application. The attacker could enter an arbitrary value, such as ‘12345’, into the CAPTCHA field:

The attacker could then capture the request in Burp Suite, and append that same value as “&_SESSION[captcha]=12345” to the request:

Once the attacker submitted the request, we see that the application accepts the CAPTCHA as valid:

 

Administration Panel Authentication Bypass

Worse yet, the admin panel was vulnerable to complete authentication bypass because of the improper use of extract(); in the PHP code.

If the attacker visited the admin URL, they would see they were not logged in as an administrator:

Just like the CAPTCHA bypass vulnerability, the attacker can go back to the vulnerable form and submit it:

And just like before, intercept the request with Burp Suite. This time, the attacker adds the parameter “&_SESSION[admin]=true”:

pic6

Submit the request, revisit the admin page, and:

Is it an issue?

Yes. This vulnerability is similar in concept to turning on PHP register globals. Essentially, with this vulnerability, I can overwrite any previously defined variable in the script, at least until the script re-defines an absolute value for the variable. While this vulnerability was a little back-handed in how it was added into this capture the flag application, I have no doubt many PHP applications with sloppy development are vulnerable to similar sorts of attacks. I have submitted vulnerability findings on other, publicly available, open-source PHP web applications because developers did this exact same thing.

There are a few pre-requisites for usage of extract() to be vulnerable:

  1. The extract() function must be done after variables are defined; for example, my PHP script might include a database connection include to handle MySQL connections. If I include that in the top of the script, translate user input to local variables using extract(), and finally use the database connection previously opened, then the site is vulnerable.
  2. If I re-write variables after extract() is used, then the code might not be vulnerable (still poor form).
  3. An attacker still has to guess internal variable names to exploit this issue. I personally don’t think this is too hard; $_SESSION[‘admin’]=true isn’t uncommon, nor are other relevantly-named variables.

The scary thing is, many developers don’t know why this is a bad idea – even when it is explained to them. Fortunately the top pages on Google for “PHP extract()” now have some comments with people saying “Don’t do this!”, but not all do, and some pages on popular development tip blogs and sites don’t warn about improper usage.

How can you fix it?

Well, simple. Don’t use extract() on user input ($_GET and $_POST). If you have to, then use the EXTR_SKIP option. A few other things would have made this site not vulnerable, however the improper usage would still be present – just not exploitable:

  1. Moving the extract(); function before session_start(); would have meant the two vulnerabilities would not have been exploitable.
  2. Using weird variable names would have reduced likelihood of exploitation. Instead, I used the form name “captcha” as the input source and $_SESSION[‘captcha’] as the destination. Hardly difficult to guess.
  3. The application should not have accepted $_SESSION[‘admin’]=true as the sole check for admin status. The app could have known I did not follow actual authentication, or was not active under a certain username. Both of those are red flags – no logical userflow would let me suddenly declare myself as an admin with nothing happening to get me there first. The app should have booted me out, killed my session ID, and had me start over.

How can you test for it?

Testing for this can be done a few simple ways, however nothing but a source code review will actually find if the application is actually vulnerable:

  1. For session variable attacks, try taking form element names (such as “name”, “email”, etc) and stick them in the elements of the _SESSION variable, as done above, and append those to the request.
  2. Try sending “&q=fake&sql=fake&query=fake&db=fake&host=fake” to attempt to break database connections. Those variable names are commonly used for connections and SQL statements. If something weird happens when sending those requests, then register globals is on or extract() is being used improperly.
  3. Get access to a page showing phpinfo(); output, server errors, or server information, and try to poison the $_SERVER variable.
  4. If there is file upload functionality on the site, try breaking $_FILES to gain access to other files on the server.

Option one and two are easily automated – yet I wouldn’t doubt that the major automated application assessment tools on the market (I’m looking at you, you, and you) don’t include checks like that. It wouldn’t take much to include these checks on forms.

February 2015 Update: PortSwigger, the developer of Burp Suite, is looking into whether this sort of issue can be tested with Burp Suite.

Try it Yourself

My Github repository for PHPinjectable was used to demonstrate the PHP extract() vulnerability in this blog post. Go download the source code, set it up, and give it a shot yourself!