Skip to content

Commit fb93c34

Browse files
committed
added sql injection scanner tutorial
1 parent fb50388 commit fb93c34

File tree

4 files changed

+142
-0
lines changed

4 files changed

+142
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# [How to Build a SQL Injection Scanner in Python](https://www.thepythoncode.com/code/sql-injection-vulnerability-detector-in-python)
2+
To run this:
3+
- `pip3 install -r requirements.txt`
4+
- Provide the URL in the command line arguments, as follows:
5+
```
6+
python sql_injection_detector.py http://testphp.vulnweb.com/artists.php?artist=1
7+
```
8+
**Output:**
9+
```
10+
[!] Trying http://testphp.vulnweb.com/artists.php?artist=1"
11+
[+] SQL Injection vulnerability detected, link: http://testphp.vulnweb.com/artists.php?artist=1"
12+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<pre>You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''''' at line 1</pre>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
requests
2+
bs4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import requests
2+
# import re # uncomment this for DVWA
3+
from bs4 import BeautifulSoup as bs
4+
from urllib.parse import urljoin
5+
from pprint import pprint
6+
7+
s = requests.Session()
8+
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"
9+
10+
# below code is for logging to your local DVWA
11+
# uncomment it if you want to use this on DVWA
12+
# login_payload = {
13+
# "username": "admin",
14+
# "password": "password",
15+
# "Login": "Login",
16+
# }
17+
# # change URL to the login page of your DVWA login URL
18+
# login_url = "http://localhost:8080/DVWA-master/login.php"
19+
20+
# # login
21+
# r = s.get(login_url)
22+
# token = re.search("user_token'\s*value='(.*?)'", r.text).group(1)
23+
# login_payload['user_token'] = token
24+
# s.post(login_url, data=login_payload)
25+
26+
27+
def get_all_forms(url):
28+
"""Given a `url`, it returns all forms from the HTML content"""
29+
soup = bs(s.get(url).content, "html.parser")
30+
return soup.find_all("form")
31+
32+
33+
def get_form_details(form):
34+
"""
35+
This function extracts all possible useful information about an HTML `form`
36+
"""
37+
details = {}
38+
# get the form action (target url)
39+
try:
40+
action = form.attrs.get("action").lower()
41+
except:
42+
action = None
43+
# get the form method (POST, GET, etc.)
44+
method = form.attrs.get("method", "get").lower()
45+
# get all the input details such as type and name
46+
inputs = []
47+
for input_tag in form.find_all("input"):
48+
input_type = input_tag.attrs.get("type", "text")
49+
input_name = input_tag.attrs.get("name")
50+
input_value = input_tag.attrs.get("value", "")
51+
inputs.append({"type": input_type, "name": input_name, "value": input_value})
52+
# put everything to the resulting dictionary
53+
details["action"] = action
54+
details["method"] = method
55+
details["inputs"] = inputs
56+
return details
57+
58+
59+
def is_vulnerable(response):
60+
"""A simple boolean function that determines whether a page
61+
is SQL Injection vulnerable from its `response`"""
62+
errors = {
63+
# MySQL
64+
"you have an error in your sql syntax;",
65+
"warning: mysql",
66+
# SQL Server
67+
"unclosed quotation mark after the character string",
68+
# Oracle
69+
"quoted string not properly terminated",
70+
}
71+
for error in errors:
72+
# if you find one of these errors, return True
73+
if error in response.content.decode().lower():
74+
return True
75+
# no error detected
76+
return False
77+
78+
79+
def scan_sql_injection(url):
80+
# test on URL
81+
for c in "\"'":
82+
# add quote/double quote character to the URL
83+
new_url = f"{url}{c}"
84+
print("[!] Trying", new_url)
85+
# make the HTTP request
86+
res = s.get(new_url)
87+
if is_vulnerable(res):
88+
# SQL Injection detected on the URL itself,
89+
# no need to preceed for extracting forms and submitting them
90+
print("[+] SQL Injection vulnerability detected, link:", new_url)
91+
return
92+
# test on HTML forms
93+
forms = get_all_forms(url)
94+
print(f"[+] Detected {len(forms)} forms on {url}.")
95+
for form in forms:
96+
form_details = get_form_details(form)
97+
for c in "\"'":
98+
# the data body we want to submit
99+
data = {}
100+
for input_tag in form_details["inputs"]:
101+
if input_tag["value"] or input_tag["type"] == "hidden":
102+
# any input form that has some value or hidden,
103+
# just use it in the form body
104+
try:
105+
data[input_tag["name"]] = input_tag["value"] + c
106+
except:
107+
pass
108+
elif input_tag["type"] != "submit":
109+
# all others except submit, use some junk data with special character
110+
data[input_tag["name"]] = f"test{c}"
111+
# join the url with the action (form request URL)
112+
url = urljoin(url, form_details["action"])
113+
if form_details["method"] == "post":
114+
res = s.post(url, data=data)
115+
elif form_details["method"] == "get":
116+
res = s.get(url, params=data)
117+
# test whether the resulting page is vulnerable
118+
if is_vulnerable(res):
119+
print("[+] SQL Injection vulnerability detected, link:", url)
120+
print("[+] Form:")
121+
pprint(form_details)
122+
break
123+
124+
if __name__ == "__main__":
125+
import sys
126+
url = sys.argv[1]
127+
scan_sql_injection(url)

0 commit comments

Comments
 (0)