Unlock the secrets of your code with our AI-powered Code Explainer. Take a look!
SQL injection is a code injection technique that is used to execute SQL queries via the user input data to the vulnerable web application. It is one of the most common and dangerous web hacking techniques.
A successful SQL injection exploit can cause a lot of harmful damage to the database and web application in general. For example, it can read sensitive data such as user passwords from the database, insert, modify and even delete data.
In this tutorial, you will learn how to build a simple Python script to detect SQL injection vulnerability in web applications.
Related: Build 24 Ethical Hacking Scripts & Tools with Python EBook
Let's install required libraries for this tutorial:
pip3 install requests bs4
Let's import the necessary modules:
import requests
from bs4 import BeautifulSoup as bs
from urllib.parse import urljoin
from pprint import pprint
# initialize an HTTP session & set the browser
s = requests.Session()
s.headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.106 Safari/537.36"
We also initialized a requests session and set the user agent.
Since SQL injection is all about user inputs, we will need to extract web forms first. Luckily for us, I've already written a tutorial about extracting and filling forms in Python. We gonna need the below functions:
def get_all_forms(url):
"""Given a `url`, it returns all forms from the HTML content"""
soup = bs(s.get(url).content, "html.parser")
return soup.find_all("form")
def get_form_details(form):
"""
This function extracts all possible useful information about an HTML `form`
"""
details = {}
# get the form action (target url)
try:
action = form.attrs.get("action").lower()
except:
action = None
# get the form method (POST, GET, etc.)
method = form.attrs.get("method", "get").lower()
# get all the input details such as type and name
inputs = []
for input_tag in form.find_all("input"):
input_type = input_tag.attrs.get("type", "text")
input_name = input_tag.attrs.get("name")
input_value = input_tag.attrs.get("value", "")
inputs.append({"type": input_type, "name": input_name, "value": input_value})
# put everything to the resulting dictionary
details["action"] = action
details["method"] = method
details["inputs"] = inputs
return details
get_all_forms()
uses BeautifulSoup library to extract all form tags from HTML and returns them as a Python list, whereas get_form_details()
function gets a single form tag object as an argument and parses useful information about the form, such as action (the target URL), method (GET
, POST
, etc) and all input field attributes (type
, name
and value
).
Next, we define a function that tells us whether a web page has SQL errors in it, this will be handy when checking for SQL injection vulnerability:
def is_vulnerable(response):
"""A simple boolean function that determines whether a page
is SQL Injection vulnerable from its `response`"""
errors = {
# MySQL
"you have an error in your sql syntax;",
"warning: mysql",
# SQL Server
"unclosed quotation mark after the character string",
# Oracle
"quoted string not properly terminated",
}
for error in errors:
# if you find one of these errors, return True
if error in response.content.decode().lower():
return True
# no error detected
return False
Get: Build 24 Ethical Hacking Scripts & Tools with Python Book
Obviously, I can't define errors for all database servers. For more reliable checking, you need to use regular expressions to find error matches. Check this XML file which has many of them (used by the sqlmap utility).
Now that we have all the tools, let's define the main function that searches for all forms in the web page and tries to place quote and double quote characters in input fields:
def scan_sql_injection(url):
# test on URL
for c in "\"'":
# add quote/double quote character to the URL
new_url = f"{url}{c}"
print("[!] Trying", new_url)
# make the HTTP request
res = s.get(new_url)
if is_vulnerable(res):
# SQL Injection detected on the URL itself,
# no need to preceed for extracting forms and submitting them
print("[+] SQL Injection vulnerability detected, link:", new_url)
return
# test on HTML forms
forms = get_all_forms(url)
print(f"[+] Detected {len(forms)} forms on {url}.")
for form in forms:
form_details = get_form_details(form)
for c in "\"'":
# the data body we want to submit
data = {}
for input_tag in form_details["inputs"]:
if input_tag["type"] == "hidden" or input_tag["value"]:
# any input form that is hidden or has some value,
# just use it in the form body
try:
data[input_tag["name"]] = input_tag["value"] + c
except:
pass
elif input_tag["type"] != "submit":
# all others except submit, use some junk data with special character
data[input_tag["name"]] = f"test{c}"
# join the url with the action (form request URL)
url = urljoin(url, form_details["action"])
if form_details["method"] == "post":
res = s.post(url, data=data)
elif form_details["method"] == "get":
res = s.get(url, params=data)
# test whether the resulting page is vulnerable
if is_vulnerable(res):
print("[+] SQL Injection vulnerability detected, link:", url)
print("[+] Form:")
pprint(form_details)
break
Before extracting forms and submitting them, the above function checks for the vulnerability in the URL first, as the URL itself may be vulnerable. This is simply done by appending a quote character to the URL.
We then make the request using requests library and check whether the response content has the errors that we're searching for.
After that, we parse the forms and make submissions with quote characters on each form found, here is my run after testing on a known vulnerable web page:
if __name__ == "__main__":
url = "http://testphp.vulnweb.com/artists.php?artist=1"
scan_sql_injection(url)
Output:
[!] Trying http://testphp.vulnweb.com/artists.php?artist=1"
[+] SQL Injection vulnerability detected, link: http://testphp.vulnweb.com/artists.php?artist=1"
As you can see, this was vulnerable in the URL itself, but after I tested this on my local vulnerable server (DVWA), I got this output:
[!] Trying http://localhost:8080/DVWA-master/vulnerabilities/sqli/"
[!] Trying http://localhost:8080/DVWA-master/vulnerabilities/sqli/'
[+] Detected 1 forms on http://localhost:8080/DVWA-master/vulnerabilities/sqli/.
[+] SQL Injection vulnerability detected, link: http://localhost:8080/DVWA-master/vulnerabilities/sqli/
[+] Form:
{'action': '#',
'inputs': [{'name': 'id', 'type': 'text', 'value': ''},
{'name': 'Submit', 'type': 'submit', 'value': 'Submit'}],
'method': 'get'}
Note: If you want to test the script on local vulnerable web applications like DVWA, you must log in first. I've provided the code for logging to DVWA in the script here, and you'll find it as a commented code in the beginning.
Note that I've tested this script on many vulnerable websites, and it works just fine. However, if you want a more reliable SQL injection tool, consider using sqlmap
, it is written in Python language as well, and it automates the process of detecting as well as exploiting SQL injection flaws.
You can extend this code by adding an exploitation feature; this cheat sheet can help you use the right SQL commands. Or you may want to extract all website links and check for vulnerability on all site pages. You can do it as well!
In our Ethical Hacking with Python Ebook, we build 24 hacking tools from scratch using Python. Make sure to check it out here if you're interested!
Disclaimer: Use this ethically. We are not responsible for any misuse of this code!
Learn also: How to Build a XSS Vulnerability Scanner in Python.
Happy Hacking ♥
Just finished the article? Now, boost your next project with our Python Code Generator. Discover a faster, smarter way to code.
View Full Code Convert My Code
Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!