While code reviewing a PHP web application last weekend I was happy to find a SQL injection vulnerability quickly in a search function. it was a time-based one and verified with a simple sleep() command So I decided to launch SQLmap to automate the rest of the exploitation part, However turned out things did not sort out that easy.
I did try many tweaks to make SQLmap able to continue the exploitation part but no luck, SQLmap was only able to verify the vulnerability with the sleep command and that’s it. maybe I am not very good in using SQLmap so I switched back to the code and start reading it to understand what cause the deadlock here, hey reading the code is always fun right!?
I was able to pinpoint where the SQL statement was built inside the code base and right there I was presented with the cause of this strange behavior. the PHP explode() function was used in the vulnerable search function, user can send a series of multiple keywords to search among the database and these keywords can be separated using commas.
as you properly know when We want to attack the underlining database We will send a different payloads to abuse the databases functions, such as if conditions, substring and so on. these functions do make a use of commas to work as intended. Now the explode() function is killing/slicing our payloads and treat it as a series of strings that will be searched inside the database. by now I knew what cause the failure of SQLmap and should find a way to bypass this implementation of the code.
Interactive mode enabled php > $payload = "SELECT 1 FROM (SELECT if ( length(version()) = 27 , SLEEP(2) , 0 ) )o;"; php > $corrupt = explode("," ,$payload); php > echo $corrupt; SELECT 1 FROM (SELECT if ( length(version()) = 27 php > echo $corrupt; SLEEP(2) php > echo $corrupt; 0 ) )o; php >
Luckily MySQL and other DBMS engines have many ways to get things done. in our example We can get rid of the commas and if condition for good and replace them with CASE control, CASE do the same things as if condition but without commas. So the above payload can be replaced with the following one and works perfectly inside the MySQL shell.
mysql> SELECT 1 FROM (SELECT case when length(version()) = 27 then sleep(5) else 0 end AS PP )o; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (5.00 sec)
things looks good for now but We need to construct the whole payload for the time-based SQL injection, We have to use the substring() function to extract the data from the database and again this function make a use of commas.
nevertheless the DBMS have many tricks to solve our issue with the substring() function. instead of calling the substring() function with commas We can call it as a simple SELECT query and slice any character out of it, the FROM part will act as an index.
mysql> SELECT substring('test' FROM 1 FOR 1); +--------------------------------+ | substring('test' FROM 1 FOR 1) | +--------------------------------+ | t | +--------------------------------+ 1 row in set (0.00 sec)
and that’s it! by now you can combine the 2 tricks to bypass the comma problem and construct a full working payload for any similar SQL injection scenarios.