@@ -67,7 +67,24 @@ def unescape(string, dbms):
6767 "Sybase" : Sybase .unescape
6868 }
6969
70- return unescaper [dbms ](string )
70+ if dbms in unescaper :
71+ return unescaper [dbms ](string )
72+ else :
73+ return string
74+
75+ def unescapeDbms (payload , injection , dbms ):
76+ # If this is a DBMS-specific test (dbms), sqlmap identified the
77+ # DBMS during previous a test (injection.dbms) or the user
78+ # provided a DBMS (conf.dbms), unescape the strings between single
79+ # quotes in the payload
80+ if injection .dbms is not None :
81+ payload = unescape (payload , injection .dbms )
82+ elif dbms is not None :
83+ payload = unescape (payload , dbms )
84+ elif conf .dbms is not None :
85+ payload = unescape (payload , conf .dbms )
86+
87+ return payload
7188
7289def checkSqlInjection (place , parameter , value ):
7390 # Store here the details about boundaries and payload used to
@@ -77,42 +94,46 @@ def checkSqlInjection(place, parameter, value):
7794 for test in conf .tests :
7895 title = test .title
7996 stype = test .stype
80- proceed = True
8197
98+ # Skip test if the risk is higher than the provided (or default)
99+ # value
82100 # Parse test's <risk>
83101 if test .risk > conf .risk :
84102 debugMsg = "skipping test '%s' because the risk " % title
85103 debugMsg += "is higher than the provided"
86104 logger .debug (debugMsg )
87105 continue
88106
107+ # Skip test if the level is higher than the provided (or default)
108+ # value
89109 # Parse test's <level>
90110 if test .level > conf .level :
91111 debugMsg = "skipping test '%s' because the level " % title
92112 debugMsg += "is higher than the provided"
93113 logger .debug (debugMsg )
94114 continue
95115
96- if "details" in test and "dbms" in test .details :
97- dbms = test .details .dbms
98- else :
99- dbms = None
100-
101- # Skip current test if it is the same SQL injection type
102- # already identified by another test
116+ # Skip test if it is the same SQL injection type already
117+ # identified by another test
103118 if injection .data and stype in injection .data :
104119 debugMsg = "skipping test '%s' because " % title
105- debugMsg += "we have already the payload for %s" % PAYLOAD .SQLINJECTION [stype ]
120+ debugMsg += "the payload for %s has " % PAYLOAD .SQLINJECTION [stype ]
121+ debugMsg += "already been identified"
106122 logger .debug (debugMsg )
107-
108123 continue
109124
110- # Skip DBMS-specific tests if they do not match the DBMS
111- # identified
125+ # Skip DBMS-specific test if it does not match either the
126+ # previously identified or the user's provided DBMS
127+ if "details" in test and "dbms" in test .details :
128+ dbms = test .details .dbms
129+ else :
130+ dbms = None
131+
112132 if dbms is not None :
113133 if injection .dbms is not None and injection .dbms != dbms :
114134 debugMsg = "skipping test '%s' because " % title
115- debugMsg += "the back-end DBMS is %s" % injection .dbms
135+ debugMsg += "the back-end DBMS identified is "
136+ debugMsg += "%s" % injection .dbms
116137 logger .debug (debugMsg )
117138
118139 continue
@@ -128,16 +149,10 @@ def checkSqlInjection(place, parameter, value):
128149 logger .info (infoMsg )
129150
130151 # Parse test's <request>
131- payload = agent .cleanupPayload (test .request .payload )
132-
133- if dbms :
134- payload = unescape (payload , dbms )
135-
136- if "comment" in test .request :
137- comment = test .request .comment
138- else :
139- comment = ""
140- testPayload = "%s%s" % (payload , comment )
152+ comment = agent .getComment (test .request )
153+ fstPayload = agent .cleanupPayload (test .request .payload )
154+ fstPayload = unescapeDbms (fstPayload , injection , dbms )
155+ fstPayload = "%s%s" % (fstPayload , comment )
141156
142157 if conf .prefix is not None and conf .suffix is not None :
143158 # Create a custom boundary object for user's supplied prefix
@@ -162,17 +177,21 @@ def checkSqlInjection(place, parameter, value):
162177 else :
163178 boundary .ptype = 1
164179
165- # Prepend user's provided boundaries to all others
180+ # Prepend user's provided boundaries to all others boundaries
166181 conf .boundaries .insert (0 , boundary )
167182
168183 for boundary in conf .boundaries :
184+ injectable = False
185+
186+ # Skip boundary if the level is higher than the provided (or
187+ # default) value
169188 # Parse boundary's <level>
170189 if boundary .level > conf .level :
171190 # NOTE: shall we report every single skipped boundary too?
172191 continue
173192
174- # Parse test's <clause> and boundary's <clause>
175193 # Skip boundary if it does not match against test's <clause>
194+ # Parse test's <clause> and boundary's <clause>
176195 clauseMatch = False
177196
178197 for clauseTest in test .clause :
@@ -183,8 +202,8 @@ def checkSqlInjection(place, parameter, value):
183202 if test .clause != [ 0 ] and boundary .clause != [ 0 ] and not clauseMatch :
184203 continue
185204
186- # Parse test's <where> and boundary's <where>
187205 # Skip boundary if it does not match against test's <where>
206+ # Parse test's <where> and boundary's <where>
188207 whereMatch = False
189208
190209 for where in test .where :
@@ -199,11 +218,10 @@ def checkSqlInjection(place, parameter, value):
199218 prefix = boundary .prefix if boundary .prefix else ""
200219 suffix = boundary .suffix if boundary .suffix else ""
201220 ptype = boundary .ptype
202- injectable = False
203221
204222 # If the previous injections succeeded, we know which prefix,
205- # postfix and parameter type to use for further tests, no
206- # need to cycle through all of the boundaries anymore
223+ # suffix and parameter type to use for further tests, no
224+ # need to cycle through the boundaries for the following tests
207225 condBound = (injection .prefix is not None and injection .suffix is not None )
208226 condBound &= (injection .prefix != prefix or injection .suffix != suffix )
209227 condType = injection .ptype is not None and injection .ptype != ptype
@@ -213,50 +231,50 @@ def checkSqlInjection(place, parameter, value):
213231
214232 # For each test's <where>
215233 for where in test .where :
216- # The <where> tag defines where to add our injection
217- # string to the parameter under assessment.
234+ # Threat the parameter original value according to the
235+ # test's <where> tag
218236 if where == 1 :
219237 origValue = value
220238 elif where == 2 :
221239 origValue = "-%s" % value
222240 elif where == 3 :
223241 origValue = ""
224242
225- # Forge payload by prepending with boundary's prefix and
226- # appending with boundary's suffix the test's
227- # ' <payload><command > ' string
228- boundPayload = "%s%s %s %s" % (origValue , prefix , testPayload , suffix )
243+ # Forge request payload by prepending with boundary's
244+ # prefix and appending the boundary's suffix to the
245+ # test's ' <payload><comment > ' string
246+ boundPayload = "%s%s %s %s" % (origValue , prefix , fstPayload , suffix )
229247 boundPayload = boundPayload .strip ()
230248 boundPayload = agent .cleanupPayload (boundPayload )
231249 reqPayload = agent .payload (place , parameter , value , boundPayload )
232250
251+ # Perform the test's request and check whether or not the
252+ # payload was successful
233253 # Parse test's <response>
234- # Check wheather or not the payload was successful
235254 for method , check in test .response .items ():
236255 check = agent .cleanupPayload (check )
237256
238257 # In case of boolean-based blind SQL injection
239258 if method == "comparison" :
240259 sndPayload = agent .cleanupPayload (test .response .comparison )
260+ sndPayload = unescapeDbms (sndPayload , injection , dbms )
261+ sndPayload = "%s%s" % (sndPayload , comment )
241262
242- if dbms :
243- sndPayload = unescape (sndPayload , dbms )
244-
245- if "comment" in test .response :
246- sndComment = test .response .comment
247- else :
248- sndComment = ""
249-
250- sndPayload = "%s%s" % (sndPayload , sndComment )
263+ # Forge response payload by prepending with
264+ # boundary's prefix and appending the boundary's
265+ # suffix to the test's ' <payload><comment> '
266+ # string
251267 boundPayload = "%s%s %s %s" % (origValue , prefix , sndPayload , suffix )
252268 boundPayload = boundPayload .strip ()
253269 boundPayload = agent .cleanupPayload (boundPayload )
254270 cmpPayload = agent .payload (place , parameter , value , boundPayload )
255271
256- # Useful to set conf.matchRatio at first
272+ # Useful to set conf.matchRatio at first based on
273+ # the False response content
257274 conf .matchRatio = None
258275 _ = Request .queryPage (cmpPayload , place )
259276
277+ # Compare True and False response contents
260278 trueResult = Request .queryPage (reqPayload , place )
261279
262280 if trueResult :
@@ -273,6 +291,8 @@ def checkSqlInjection(place, parameter, value):
273291
274292 # In case of error-based or UNION query SQL injections
275293 elif method == "grep" :
294+ # Perform the test's request and grep the response
295+ # body for the test's <grep> regular expression
276296 reqBody , _ = Request .queryPage (reqPayload , place , content = True )
277297 match = re .search (check , reqBody , re .DOTALL | re .IGNORECASE )
278298
@@ -290,8 +310,11 @@ def checkSqlInjection(place, parameter, value):
290310
291311 injectable = True
292312
293- # In case of time-based blind or stacked queries SQL injections
313+ # In case of time-based blind or stacked queries
314+ # SQL injections
294315 elif method == "time" :
316+ # Perform the test's request and check how long
317+ # it takes to get the response back
295318 start = time .time ()
296319 _ = Request .queryPage (reqPayload , place )
297320 duration = calculateDeltaSeconds (start )
@@ -302,26 +325,44 @@ def checkSqlInjection(place, parameter, value):
302325
303326 injectable = True
304327
305- if injectable is True :
306- injection .place = place
307- injection .parameter = parameter
308- injection .ptype = ptype
309- injection .prefix = prefix
310- injection .suffix = suffix
311-
312- injection .data [stype ] = (boundPayload , comment )
313-
314- if "details" in test :
315- for detailKey , detailValue in test .details .items ():
316- if detailKey == "dbms" and injection .dbms is None :
317- injection .dbms = detailValue
318- elif detailKey == "dbms_version" and injection .dbms_version is None :
319- injection .dbms_version = detailValue
320- elif detailKey == "os" and injection .os is None :
321- injection .os = detailValue
328+ # If the injection test was successful feed the injection
329+ # object with the test's details
330+ if injectable is True :
331+ # Feed with the boundaries details only the first time a
332+ # test has been successful
333+ if injection .place is None or injection .parameter is None :
334+ injection .place = place
335+ injection .parameter = parameter
336+ injection .ptype = ptype
337+ injection .prefix = prefix
338+ injection .suffix = suffix
339+
340+ # Feed with test details every time a test is successful
341+ injection .data [stype ] = (title , reqPayload , where , comment )
342+
343+ if "details" in test :
344+ for detailKey , detailValue in test .details .items ():
345+ if detailKey == "dbms" and injection .dbms is None :
346+ injection .dbms = detailValue
347+ kb .dbms = detailValue
348+ elif detailKey == "dbms_version" and injection .dbms_version is None :
349+ injection .dbms_version = detailValue
350+ kb .dbmsVersion = [ detailValue ]
351+ elif detailKey == "os" and injection .os is None :
352+ injection .os = detailValue
353+
354+ beep ()
355+
356+ # There is no need to perform this test for other
357+ # <where> tags
358+ break
322359
360+ if injectable is True :
361+ # There is no need to perform this test with others
362+ # boundaries
323363 break
324364
365+ # Return the injection object
325366 if injection .place is not None and injection .parameter is not None :
326367 return injection
327368 else :
0 commit comments