sqlmap/lib/controller/controller.py

370 lines
14 KiB
Python
Raw Normal View History

2008-10-15 19:38:22 +04:00
#!/usr/bin/env python
"""
2008-10-15 19:56:32 +04:00
$Id$
2008-10-15 19:38:22 +04:00
Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
2010-10-15 03:18:29 +04:00
See the file 'doc/COPYING' for copying permission
2008-10-15 19:38:22 +04:00
"""
2010-10-15 04:34:16 +04:00
import re
2008-10-15 19:38:22 +04:00
from lib.controller.action import action
from lib.controller.checks import checkSqlInjection
2010-10-11 16:26:35 +04:00
from lib.controller.checks import heuristicCheckSqlInjection
2008-10-15 19:38:22 +04:00
from lib.controller.checks import checkDynParam
from lib.controller.checks import checkStability
from lib.controller.checks import checkString
from lib.controller.checks import checkRegexp
2008-10-15 19:38:22 +04:00
from lib.controller.checks import checkConnection
2010-09-16 12:43:10 +04:00
from lib.controller.checks import checkNullConnection
2010-06-02 16:45:40 +04:00
from lib.core.common import getUnicode
2008-10-15 19:38:22 +04:00
from lib.core.common import paramToDict
2010-03-05 18:25:53 +03:00
from lib.core.common import parseTargetUrl
2008-10-15 19:38:22 +04:00
from lib.core.common import readInput
from lib.core.data import conf
from lib.core.data import kb
from lib.core.data import logger
2010-11-08 12:44:32 +03:00
from lib.core.enums import HTTPMETHOD
from lib.core.enums import PLACE
from lib.core.exception import exceptionsTuple
2008-10-15 19:38:22 +04:00
from lib.core.exception import sqlmapNotVulnerableException
from lib.core.exception import sqlmapSilentQuitException
2010-09-30 23:45:23 +04:00
from lib.core.exception import sqlmapUserQuitException
2008-10-15 19:38:22 +04:00
from lib.core.session import setInjection
from lib.core.session import setMatchRatio
2010-03-15 14:55:13 +03:00
from lib.core.target import initTargetEnv
from lib.core.target import setupTargetEnv
2008-10-15 19:38:22 +04:00
from lib.utils.parenthesis import checkForParenthesis
def __selectInjection(injData):
"""
Selection function for injection place, parameters and type.
"""
message = "there were multiple injection points, please select the "
message += "one to use to go ahead:\n"
for i in xrange(0, len(injData)):
injPlace = injData[i][0]
2008-10-15 19:38:22 +04:00
injParameter = injData[i][1]
injType = injData[i][2]
2008-10-15 19:38:22 +04:00
message += "[%d] place: %s, parameter: " % (i, injPlace)
message += "%s, type: %s" % (injParameter, injType)
if i == 0:
message += " (default)"
message += "\n"
message += "[q] Quit"
select = readInput(message, default="0")
2008-10-15 19:38:22 +04:00
if not select:
index = 0
elif select.isdigit() and int(select) < len(injData) and int(select) >= 0:
index = int(select)
elif select[0] in ( "Q", "q" ):
return "Quit"
else:
warnMsg = "invalid choice, retry"
2008-10-15 19:38:22 +04:00
logger.warn(warnMsg)
__selectInjection(injData)
return injData[index]
def start():
"""
This function calls a function that performs checks on both URL
stability and all GET, POST, Cookie and User-Agent parameters to
check if they are dynamic and SQL injection affected
"""
if not conf.start:
2010-09-26 18:56:55 +04:00
return False
if conf.direct:
initTargetEnv()
setupTargetEnv()
action()
2010-09-26 18:56:55 +04:00
return True
if conf.url and not conf.forms:
kb.targetUrls.add(( conf.url, conf.method, conf.data, conf.cookie ))
2008-10-15 19:38:22 +04:00
if conf.configFile and not kb.targetUrls:
errMsg = "you did not edit the configuration file properly, set "
errMsg += "the target url, list of targets or google dork"
2008-10-15 19:38:22 +04:00
logger.error(errMsg)
2010-09-26 18:56:55 +04:00
return False
2008-10-15 19:38:22 +04:00
if kb.targetUrls and len(kb.targetUrls) > 1:
infoMsg = "sqlmap got a total of %d targets" % len(kb.targetUrls)
logger.info(infoMsg)
hostCount = 0
cookieStr = ""
setCookieAsInjectable = True
2008-10-15 19:38:22 +04:00
for targetUrl, targetMethod, targetData, targetCookie in kb.targetUrls:
try:
conf.url = targetUrl
conf.method = targetMethod
conf.data = targetData
conf.cookie = targetCookie
injData = []
2010-10-15 13:54:29 +04:00
2010-10-15 04:34:16 +04:00
initTargetEnv()
parseTargetUrl()
2010-10-15 13:54:29 +04:00
2010-10-15 04:34:16 +04:00
testSqlInj = False
2010-11-08 12:44:32 +03:00
if PLACE.GET in conf.parameters:
for parameter in re.findall(r"([^=]+)=[^&]+&?", conf.parameters[PLACE.GET]):
paramKey = (conf.hostname, conf.path, PLACE.GET, parameter)
2010-10-15 04:34:16 +04:00
if paramKey not in kb.testedParams:
testSqlInj = True
break
2010-10-15 13:54:29 +04:00
else:
paramKey = (conf.hostname, conf.path, None, None)
if paramKey not in kb.testedParams:
testSqlInj = True
2010-10-15 04:34:16 +04:00
if not testSqlInj:
infoMsg = "skipping '%s'" % targetUrl
logger.info(infoMsg)
continue
if conf.multipleTargets:
hostCount += 1
message = "%s %d:\n%s %s" % ("form" if conf.forms else "url", hostCount, conf.method or HTTPMETHOD.GET, targetUrl)
if conf.cookie:
message += "\nCookie: %s" % conf.cookie
if conf.data:
message += "\nPOST data: %s" % repr(conf.data) if conf.data else ""
if conf.forms:
if conf.method == HTTPMETHOD.GET and targetUrl.find("?") == -1:
continue
message += "\ndo you want to test this form? [Y/n/q] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
if conf.method == HTTPMETHOD.POST:
message = "Edit POST data [default: %s]: " % (conf.data if conf.data else "")
conf.data = readInput(message, default=conf.data)
elif conf.method == HTTPMETHOD.GET:
if conf.url.find("?") > -1:
firstPart = conf.url[:conf.url.find("?")]
secondPart = conf.url[conf.url.find("?")+1:]
message = "Edit GET data [default: %s]: " % secondPart
test = readInput(message, default=secondPart)
conf.url = "%s?%s" % (firstPart, test)
elif test[0] in ("n", "N"):
continue
elif test[0] in ("q", "Q"):
break
2008-10-15 19:38:22 +04:00
else:
message += "\ndo you want to test this url? [Y/n/q]"
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
pass
elif test[0] in ("n", "N"):
continue
elif test[0] in ("q", "Q"):
break
2008-10-15 19:38:22 +04:00
logMsg = "testing url %s" % targetUrl
logger.info(logMsg)
setupTargetEnv()
if not checkConnection() or not checkString() or not checkRegexp():
2008-10-15 19:38:22 +04:00
continue
if conf.nullConnection:
checkNullConnection()
2010-09-16 12:43:10 +04:00
2010-09-26 18:02:13 +04:00
if not conf.dropSetCookie and conf.cj:
for _, cookie in enumerate(conf.cj):
2010-06-02 16:45:40 +04:00
cookie = getUnicode(cookie)
index = cookie.index(" for ")
cookieStr += "%s;" % cookie[8:index]
2008-10-15 19:38:22 +04:00
if cookieStr:
cookieStr = cookieStr[:-1]
2010-11-08 12:44:32 +03:00
if PLACE.COOKIE in conf.parameters:
message = "you provided an HTTP Cookie header value. "
message += "The target url provided its own Cookie within "
message += "the HTTP Set-Cookie header. Do you want to "
message += "continue using the HTTP Cookie values that "
message += "you provided? [Y/n] "
test = readInput(message, default="Y")
if not test or test[0] in ("y", "Y"):
setCookieAsInjectable = False
if setCookieAsInjectable:
2010-11-08 16:26:45 +03:00
conf.httpHeaders.append(("Cookie", cookieStr))
2010-11-08 12:44:32 +03:00
conf.parameters[PLACE.COOKIE] = cookieStr
__paramDict = paramToDict(PLACE.COOKIE, cookieStr)
if __paramDict:
2010-11-08 12:44:32 +03:00
conf.paramDict[PLACE.COOKIE] = __paramDict
2010-03-21 03:39:44 +03:00
# TODO: consider the following line in __setRequestParams()
__testableParameters = True
2008-10-15 19:38:22 +04:00
if not kb.injPlace or not kb.injParameter or not kb.injType:
if not conf.string and not conf.regexp and not conf.eRegexp:
# NOTE: this is not needed anymore, leaving only to display
# a warning message to the user in case the page is not stable
checkStability()
2010-11-08 02:37:15 +03:00
# Do a little prioritization reorder of a testable parameter list
parameters = conf.parameters.keys()
2010-11-08 12:44:32 +03:00
for place in (PLACE.URI, PLACE.POST, PLACE.GET):
2010-11-08 02:37:15 +03:00
if place in parameters:
parameters.remove(place)
parameters.insert(0, place)
for place in parameters:
if not conf.paramDict.has_key(place):
continue
paramDict = conf.paramDict[place]
for parameter, value in paramDict.items():
testSqlInj = True
2010-10-15 04:34:16 +04:00
paramKey = (conf.hostname, conf.path, place, parameter)
if paramKey in kb.testedParams:
testSqlInj = False
2010-10-15 04:34:16 +04:00
infoMsg = "skipping previously processed %s parameter '%s'" % (place, parameter)
logger.info(infoMsg)
# Avoid dinamicity test if the user provided the
# parameter manually
elif parameter in conf.testParameter:
pass
2010-10-15 04:34:16 +04:00
elif not checkDynParam(place, parameter, value):
warnMsg = "%s parameter '%s' is not dynamic" % (place, parameter)
logger.warn(warnMsg)
testSqlInj = False
2010-10-15 04:34:16 +04:00
else:
logMsg = "%s parameter '%s' is dynamic" % (place, parameter)
2008-10-15 19:38:22 +04:00
logger.info(logMsg)
kb.testedParams.add(paramKey)
if testSqlInj:
2010-10-11 16:26:35 +04:00
heuristicCheckSqlInjection(place, parameter, value)
2010-10-15 04:34:16 +04:00
for parenthesis in range(0, 4):
logMsg = "testing sql injection on %s " % place
logMsg += "parameter '%s' with " % parameter
logMsg += "%d parenthesis" % parenthesis
logger.info(logMsg)
2008-10-15 19:38:22 +04:00
injType = checkSqlInjection(place, parameter, value, parenthesis)
2008-10-15 19:38:22 +04:00
if injType:
injData.append((place, parameter, injType))
break
2010-10-15 04:34:16 +04:00
else:
infoMsg = "%s parameter '%s' is not " % (place, parameter)
infoMsg += "injectable with %d parenthesis" % parenthesis
logger.info(infoMsg)
2008-10-15 19:38:22 +04:00
if not injData:
warnMsg = "%s parameter '%s' is not " % (place, parameter)
warnMsg += "injectable"
logger.warn(warnMsg)
if not kb.injPlace or not kb.injParameter or not kb.injType:
if len(injData) == 1:
injDataSelected = injData[0]
elif len(injData) > 1:
injDataSelected = __selectInjection(injData)
else:
raise sqlmapNotVulnerableException, "all parameters are not injectable"
2008-10-15 19:38:22 +04:00
if injDataSelected == "Quit":
return
else:
kb.injPlace, kb.injParameter, kb.injType = injDataSelected
setInjection()
2008-10-15 19:38:22 +04:00
if kb.injPlace and kb.injParameter and kb.injType:
if conf.multipleTargets:
message = "do you want to exploit this SQL injection? [Y/n] "
exploit = readInput(message, default="Y")
2008-10-15 19:38:22 +04:00
condition = not exploit or exploit[0] in ("y", "Y")
else:
condition = True
if condition:
2010-11-04 19:47:18 +03:00
if kb.paramMatchRatio:
conf.matchRatio = kb.paramMatchRatio[(kb.injPlace, kb.injParameter)]
setMatchRatio()
2010-11-07 18:34:52 +03:00
checkForParenthesis()
action()
except KeyboardInterrupt:
2010-11-05 19:03:12 +03:00
if conf.multipleTargets:
2010-11-05 19:08:42 +03:00
warnMsg = "Ctrl+C detected in multiple target mode"
2010-11-05 19:03:12 +03:00
logger.warn(warnMsg)
2010-11-07 19:23:03 +03:00
message = "do you want to skip to the next target in list? [Y/n/q]"
2010-11-05 19:03:12 +03:00
test = readInput(message, default="Y")
2010-11-05 19:03:12 +03:00
if not test or test[0] in ("y", "Y"):
pass
elif test[0] in ("n", "N"):
return False
elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException
else:
raise
2010-09-30 23:45:23 +04:00
except sqlmapUserQuitException:
raise
2010-11-10 22:44:51 +03:00
except sqlmapSilentQuitException:
raise
except exceptionsTuple, e:
2010-06-02 16:45:40 +04:00
e = getUnicode(e)
2008-10-15 19:38:22 +04:00
if conf.multipleTargets:
e += ", skipping to next url"
logger.error(e)
else:
2010-09-27 17:41:18 +04:00
logger.critical(e)
2010-09-26 18:56:55 +04:00
return False
2008-10-15 19:38:22 +04:00
if conf.loggedToOut:
logger.info("Fetched data logged to text files under '%s'" % conf.outputPath)
2010-09-26 18:56:55 +04:00
return True