diff --git a/lib/controller/controller.py b/lib/controller/controller.py index bd1a65c11..f50f7fbf9 100644 --- a/lib/controller/controller.py +++ b/lib/controller/controller.py @@ -368,7 +368,7 @@ def start(): parameters = conf.parameters.keys() # Order of testing list (last to first) - orderList = (PLACE.URI, PLACE.GET, PLACE.POST) + orderList = (PLACE.URI, PLACE.GET, PLACE.POST, PLACE.CUSTOM_POST) for place in orderList: if place in parameters: diff --git a/lib/core/agent.py b/lib/core/agent.py index 5ef865b54..210b7fba0 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -12,6 +12,7 @@ import re from xml.etree import ElementTree as ET from lib.core.common import Backend +from lib.core.common import extractRegexResult from lib.core.common import isDBMSVersionAtLeast from lib.core.common import isTechniqueAvailable from lib.core.common import randomInt @@ -62,9 +63,6 @@ class Agent: if where is None and isTechniqueAvailable(kb.technique): where = kb.injection.data[kb.technique].where - # Debug print - #print "value: %s, newValue: %s, where: %s, kb.technique: %s" % (value, newValue, where, kb.technique) - if kb.injection.place is not None: place = kb.injection.place @@ -81,6 +79,9 @@ class Agent: for char in ('?', '=', ':'): if char in origValue: origValue = origValue[origValue.rfind(char) + 1:] + elif place == PLACE.CUSTOM_POST: + origValue = origValue.split(CUSTOM_INJECTION_MARK_CHAR)[0] + origValue = extractRegexResult(r"(?s)(?P(\W+\Z|\w+\Z))", origValue) if value is None: if where == PAYLOAD.WHERE.ORIGINAL: @@ -112,7 +113,7 @@ class Agent: child.text = self.addPayloadDelimiters(newValue) retValue = ET.tostring(root) - elif place == PLACE.URI: + elif place in (PLACE.URI, PLACE.CUSTOM_POST): retValue = paramString.replace("%s%s" % (origValue, CUSTOM_INJECTION_MARK_CHAR), self.addPayloadDelimiters(newValue)) elif place in (PLACE.UA, PLACE.REFERER, PLACE.HOST): retValue = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) diff --git a/lib/core/enums.py b/lib/core/enums.py index 219e59584..1dfd6b0ab 100644 --- a/lib/core/enums.py +++ b/lib/core/enums.py @@ -66,6 +66,7 @@ class PLACE: UA = "User-Agent" REFERER = "Referer" HOST = "Host" + CUSTOM_POST = "(custom) POST" class HTTPMETHOD: GET = "GET" diff --git a/lib/core/option.py b/lib/core/option.py index 40cfc81d5..518607040 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1473,6 +1473,7 @@ def __setKnowledgeBaseAttributes(flushAll=True): kb.pageTemplate = None kb.pageTemplates = dict() kb.previousMethod = None + kb.processUserMarks = None kb.orderByColumns = None kb.originalCode = None kb.originalPage = None diff --git a/lib/core/target.py b/lib/core/target.py index f9f73839b..148560f71 100644 --- a/lib/core/target.py +++ b/lib/core/target.py @@ -59,16 +59,16 @@ def __setRequestParams(): conf.parameters[None] = "direct connection" return - __testableParameters = False + testableParameters = False # Perform checks on GET parameters if conf.parameters.has_key(PLACE.GET) and conf.parameters[PLACE.GET]: parameters = conf.parameters[PLACE.GET] - __paramDict = paramToDict(PLACE.GET, parameters) + paramDict = paramToDict(PLACE.GET, parameters) - if __paramDict: - conf.paramDict[PLACE.GET] = __paramDict - __testableParameters = True + if paramDict: + conf.paramDict[PLACE.GET] = paramDict + testableParameters = True # Perform checks on POST parameters if conf.method == HTTPMETHOD.POST and not conf.data: @@ -83,18 +83,17 @@ def __setRequestParams(): else: conf.data = conf.data.replace("\n", " ") - # Check if POST data is in xml syntax if re.match(SOAP_REGEX, conf.data, re.I | re.M): place = PLACE.SOAP else: place = PLACE.POST conf.parameters[place] = conf.data - __paramDict = paramToDict(place, conf.data) + paramDict = paramToDict(place, conf.data) - if __paramDict: - conf.paramDict[place] = __paramDict - __testableParameters = True + if paramDict: + conf.paramDict[place] = paramDict + testableParameters = True conf.method = HTTPMETHOD.POST @@ -109,40 +108,52 @@ def __setRequestParams(): message += "in the target url itself? [Y/n/q] " test = readInput(message, default="Y") - if not test or test[0] in ("y", "Y"): + if not test or test[0] not in ("n", "N"): conf.url = "%s%s" % (conf.url, CUSTOM_INJECTION_MARK_CHAR) - elif test[0] in ("n", "N"): - pass + kb.processUserMarks = True elif test[0] in ("q", "Q"): raise sqlmapUserQuitException - if CUSTOM_INJECTION_MARK_CHAR in conf.url: - conf.parameters[PLACE.URI] = conf.url - conf.paramDict[PLACE.URI] = {} - parts = conf.url.split(CUSTOM_INJECTION_MARK_CHAR) + for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data)): + if CUSTOM_INJECTION_MARK_CHAR in (value or ""): + if kb.processUserMarks is None: + message = "custom injection mark ('%s') found in " % CUSTOM_INJECTION_MARK_CHAR + message += "'%s'. Do you want to process it? [Y/n/q] " % {PLACE.URI: '-u', PLACE.CUSTOM_POST: '--data'}[place] + test = readInput(message, default="Y") + if test and test[0] in ("q", "Q"): + raise sqlmapUserQuitException + else: + kb.processUserMarks = not test or test[0] not in ("n", "N") - for i in xrange(len(parts)-1): - result = str() + if not kb.processUserMarks: + continue - for j in xrange(len(parts)): - result += parts[j] + conf.parameters[place] = value + conf.paramDict[place] = {} + parts = value.split(CUSTOM_INJECTION_MARK_CHAR) - if i == j: - result += CUSTOM_INJECTION_MARK_CHAR + for i in xrange(len(parts) - 1): + conf.paramDict[place]["#%d%s" % (i + 1, CUSTOM_INJECTION_MARK_CHAR)] = "".join("%s%s" % (parts[j], CUSTOM_INJECTION_MARK_CHAR if i == j else "") for j in xrange(len(parts))) - conf.paramDict[PLACE.URI]["#%d%s" % (i+1, CUSTOM_INJECTION_MARK_CHAR)] = result + if place == PLACE.URI and PLACE.GET in conf.paramDict: + del conf.paramDict[PLACE.GET] + elif place == PLACE.CUSTOM_POST and PLACE.POST in conf.paramDict: + del conf.paramDict[PLACE.POST] - conf.url = conf.url.replace(CUSTOM_INJECTION_MARK_CHAR, str()) - __testableParameters = True + testableParameters = True + + if kb.processUserMarks: + conf.url = conf.url.replace(CUSTOM_INJECTION_MARK_CHAR, "") + conf.data = conf.data.replace(CUSTOM_INJECTION_MARK_CHAR, "") if conf.data else conf.data # Perform checks on Cookie parameters if conf.cookie: conf.parameters[PLACE.COOKIE] = conf.cookie - __paramDict = paramToDict(PLACE.COOKIE, conf.cookie) + paramDict = paramToDict(PLACE.COOKIE, conf.cookie) - if __paramDict: - conf.paramDict[PLACE.COOKIE] = __paramDict - __testableParameters = True + if paramDict: + conf.paramDict[PLACE.COOKIE] = paramDict + testableParameters = True # Perform checks on header values if conf.httpHeaders: @@ -157,7 +168,7 @@ def __setRequestParams(): if condition: conf.paramDict[PLACE.UA] = { PLACE.UA: headerValue } - __testableParameters = True + testableParameters = True elif httpHeader == PLACE.REFERER: conf.parameters[PLACE.REFERER] = urldecode(headerValue) @@ -166,7 +177,7 @@ def __setRequestParams(): if condition: conf.paramDict[PLACE.REFERER] = { PLACE.REFERER: headerValue } - __testableParameters = True + testableParameters = True elif httpHeader == PLACE.HOST: conf.parameters[PLACE.HOST] = urldecode(headerValue) @@ -175,14 +186,14 @@ def __setRequestParams(): if condition: conf.paramDict[PLACE.HOST] = { PLACE.HOST: headerValue } - __testableParameters = True + testableParameters = True if not conf.parameters: errMsg = "you did not provide any GET, POST and Cookie " errMsg += "parameter, neither an User-Agent, Referer or Host header value" raise sqlmapGenericException, errMsg - elif not __testableParameters: + elif not testableParameters: errMsg = "all testable parameters you provided are not present " errMsg += "within the GET, POST and Cookie parameters" raise sqlmapGenericException, errMsg diff --git a/lib/request/connect.py b/lib/request/connect.py index 8e7d9dd52..d11525b66 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -51,6 +51,7 @@ from lib.core.enums import PLACE from lib.core.enums import REDIRECTION from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapSyntaxException +from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_SILENT_TIMEOUT from lib.core.settings import MAX_CONNECTION_CHUNK_SIZE @@ -557,7 +558,7 @@ class Connect: value = urlEncodeCookieValues(value) elif place: - if place in (PLACE.GET, PLACE.POST, PLACE.URI): + if place in (PLACE.GET, PLACE.POST, PLACE.URI, PLACE.CUSTOM_POST): # payloads in GET and/or POST need to be urlencoded # throughly without safe chars (especially & and =) # addendum: as we support url encoding in tampering @@ -582,6 +583,9 @@ class Connect: if PLACE.POST in conf.parameters: post = conf.parameters[PLACE.POST] if place != PLACE.POST or not value else value + if PLACE.CUSTOM_POST in conf.parameters: + post = conf.parameters[PLACE.CUSTOM_POST].replace(CUSTOM_INJECTION_MARK_CHAR, "") if place != PLACE.CUSTOM_POST or not value else value + if PLACE.SOAP in conf.parameters: post = conf.parameters[PLACE.SOAP] if place != PLACE.SOAP or not value else value