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