It is often said that the best hackers remain unknown, and the greatest attacks are left undiscovered, but it's hard for an up-and-coming penetration tester or white hat to learn anything unless one of those factors is actually known or discovered. But the end goal here in our SQL injection lessons is to make that statement as true as possible for us when performing our hacks.
Frequently, when testing for SQL injection, an attacker relies on improperly sanitized user input in order to be successful. But what about when everything seems to be filtered correctly? There are various ways to bypass these defenses using techniques that abuse specific features of SQL.
Method 1: White Space
The first method we can use to try to evade signature detection utilizes white space. Adding extra spaces or special characters like tabs or new lines won't affect the SQL statement, but could potentially get malicious payloads through filters. For example:
SELECT * FROM Users WHERE id =1
This statement will execute exactly the same as:
SELECT * FROM Users WHERE id=1
A tab character or new line will also not affect the statement:
SELECT * FROM
Users WHERE
id=1
Method 2: Null Bytes
Often, the filter will block certain characters from being executed in the SQL statement. This is probably the most common way attacks are thwarted since, without special characters like apostrophes or dashes, injection is unlikely to be successful.
One way to get around this is by using null bytes (%00) in front of any blocked characters. For instance, if we know that the application is blocking apostrophes, the following injection could be used to trick the filter into allowing them:
%00' or 1=1--
Method 3: URL Encoding
Another way to avoid detection is by using URL encoding. This type of encoding is used to send web address information over the internet via HTTP. Since URLs can only contain ASCII values, any invalid characters need to be encoded to valid ASCII characters. URLs also cannot include spaces, so they are usually converted to a + sign or %20. By masking a malicious SQL query using URL encoding, it is possible to bypass filters. Take the following injection for example:
' or 1=1--
Using URL encoding it would look like:
%27%20or%201%3D1--
Method 4: Hex Encoding
Hex encoding replaces characters in the original SQL statement with the equivalent hexadecimal value. The hexadecimal numeral system, also referred to as base 16, uses sixteen symbols to represent values 0–15. The numbers 0–9 represent just that, while A–F represents 10–15.
A compact hex table can easily be accessed by typing man ascii in the terminal and scrolling down a bit:
Tables
For convenience, below is a more compact table in hex.
2 3 4 5 6 7
-------------
0: 0 @ P ` p
1: ! 1 A Q a q
2: " 2 B R b r
3: # 3 C S c s
4: $ 4 D T d t
5: % 5 E U e u
6: & 6 F V f v
7: ' 7 G W g w
8: ( 8 H X h x
9: ) 9 I Y i y
A: * : J Z j z
B: + ; K [ k {
C: , < L \ l |
D: - = M ] m }
E: . > N ^ n ~
F: / ? O _ o DEL
This number system is utilized a lot in computer engineering and programming because it provides a more human-readable representation of binary values, and it can more efficiently represent large numbers with fewer digits. Using hex encoding on SQL statements can often aid in evading detection. For example:
SELECT * FROM Users WHERE name='admin'--
The hex encoded equivalent would be:
SELECT * FROM Users WHERE name=61646D696E--
Alternatively, we could use the UNHEX() function to achieve the same results:
SELECT * FROM Users WHERE name=UNHEX('61646D696E')--
Method 5: Character Encoding
Character encoding works similarly to hex encoding in that characters in the original SQL statement are replaced with converted values. This type of encoding uses the CHAR() function to encode characters as decimal values.
Just like before, a compact decimal table can be accessed by typing man ascii in the terminal:
Tables
For convenience, below is a more compact table in decimal.
30 40 50 60 70 80 90 100 110 120
---------------------------------
0: ( 2 < F P Z d n x
1: ) 3 = G Q [ e o y
2: * 4 > H R \ f p z
3: ! + 5 ? I S ] g q {
4: " , 6 @ J T ^ h r |
5: # - 7 A K U _ i s }
6: $ . 8 B L V ` j t ~
7: % / 9 C M W a k u DEL
8: & 0 : D N X b l v
9: ' 1 ; E O Y c m w
Take a look at the following query:
SELECT * FROM Users WHERE name='admin'--
Using character encoding, the statement would look like:
SELECT * FROM Users WHERE name=CHAR(97,100,109,105,110)--
Method 6: String Concatenation
Another method that is used to bypass filters is string concatenation. We covered string concatenation in an earlier tutorial, but the same concept can be applied here; We can often avoid detection by breaking up keywords in the malicious SQL query. Keep in mind that string concatenation varies between different database systems. Let's look at the following statement:
SELECT * FROM Users WHERE id=1
Utilizing string concatenation in MySQL, we can craft a query that could potentially evade filters:
CONCAT('SEL', 'ECT') * FROM Users WHERE id=1
Here is what the query would look like in MS SQL:
'SEL' + 'ECT' * FROM Users WHERE id=1
PostgreSQL:
'SEL' || 'ECT' * FROM Users WHERE id=1
Oracle (two options):
CONCAT('SEL', 'ECT') * FROM Users WHERE id=1
'SEL' || 'ECT' * FROM Users WHERE id=1
Method 7: Comments
Abusing the way that SQL handles inline comments can also aid in bypassing filters and avoiding detection when performing SQL injection attacks. Since there can be any number of comments in a statement and still be valid, we can use them to break up the query and possibly circumvent any filters that are present. For instance, we can insert comments in between keywords like so:
SELECT/**/*/**/FROM/**/Users/**/WHERE/**/name/**/=/**/'admin'--
Method 8: Combinations
Sometimes, even these signature evasion techniques will not be successful on their own, but we can combine them to further our chances of successfully bypassing defenses and completing an attack. For example, let's say a filter on the application we are attacking doesn't allow comment characters. To get around this, we can try crafting a query that encodes these characters in an effort to trick the filter into allowing them. The original query that fails:
SELECT/**/*/**/FROM/**/Users/**/WHERE/**/name/**/=/**/'admin'--
The same query using URL encoding to mask the comment characters:
SELECT%2F%2A%2A%2F%2A%2F%2A%2A%2FFROM%2F%2A%2A%2FUsers%2F%2A%2A%2FWHERE%2F%2A%2A%2Fname%2F%2A%2A%2F%3D%2F%2A%2A%2F%E2%80%99admin%E2%80%99--
Any of these methods can be combined to help get around pesky filters, and as such, this greatly increases our chances of success when performing SQL injection.
Method 9: Second Order Injections
The final method that we can employ to aid in signature evasion is a bit more complicated, but depending on the way the database is configured, this type of attack can potentially help when all else fails.
Second Order SQL injection occurs when a web application or database properly filters input from a user but fails to provide any further sanitation after the initial query is passed through and validated. When the application performs some other function utilizing the database, our SQL query is executed dynamically and the malicious code is run. As such, this type of injection can be thought of as having two distinct stages: the initial query which encapsulates the malicious code and another process that executes the code at a later time.
A common mitigation technique is to double up any single quotes that appear within user input before the query is ran against the database. Let's suppose we have an application that offers users the ability to create an account, as well as a password reset function. When we create an account, the SQL query could look something like this:
INSERT INTO Users (username, password) VALUES ('johndoe''', 'hunter2')
This causes no problems for the database since once the single quotes are doubled up, it is still a valid statement. Now let's suppose we would like to change our password. The query sent to the database would look like this:
SELECT password FROM Users WHERE username='johndoe''
Since the value for the username stored in the database is the string 'johndoe' an SQL injection flaw is now present because the original input is no longer being filtered. In order to exploit this, all we would have to do is register a username containing malicious code, such as:
UNION ALL SELECT * FROM Users WHERE username='admin'--
The account creation itself will be handled successfully, but when we go to change the password the malicious query will be executed, thus bypassing input validation.
Stay Tuned for More on SQL Injection
As you can see, there are many techniques that can be used to bypass filters and avoid detection when performing SQL injection. In addition to increasing the likelihood of a successful attack, these methods can help you fly under the radar, which is important when hacking in general. Now that we've covered signature evasion, it's time to take a look at common methods of defense used in the industry.
Just updated your iPhone to iOS 18? You'll find a ton of hot new features for some of your most-used Apple apps. Dive in and see for yourself:
3 Comments
I don't really get how this is still a problem in 2018. When I started coding about 10 years ago I made all those mistakes, but today there are so many easy ways to avoid SQL-Injection. Just use a strongly typed language, parse the Values to the expected type and use Sql-Variables and not a String-Concatenation.
The biggest problem I see, when I look at beginner tutorials and books expecially for php, there are so many bad practices taught in there and if new people learn them by heart it is hard to correct it.
I agree, with the prevalence of all the beginner tutorials out there comes all the beginner mistakes, and like you said even if they move on to more advanced coding it is hard to break old habits.
i don't get it. if i create a user with the query UNION ALL SELECT * FROM Users WHERE username='admin'-- it will be inserted like this:
INSERT INTO Users (username, password) VALUES ('UNION ALL SELECT * FROM Users WHERE username=''admin''--', 'hunter2').
right? So it will be saved this way on the DB:
UNION ALL SELECT * FROM Users WHERE username='admin'--(at least on the mysql). Then, when i change the pass should be stay this way:
SELECT password FROM Users WHERE username='UNION ALL SELECT * FROM Users WHERE username='admin'--';
And it causes an error on the syntax. Someone could explain to me?
Share Your Thoughts