Implementation for an Issue #79

This commit is contained in:
Miroslav Stampar 2012-10-16 12:32:58 +02:00
parent 3e64ab214e
commit 2cb1b054bb
6 changed files with 55 additions and 5 deletions

View File

@ -1962,7 +1962,31 @@ def extractErrorMessage(page):
return retVal return retVal
def urldecode(value, encoding=None, unsafe="%&=", convall=False): def findMultipartPostBoundary(post):
"""
Finds value for a boundary parameter in given multipart POST body
"""
retVal = None
done = set()
candidates = []
for match in re.finditer(r"(?m)^--(.+?)(--)?$", post or ""):
_ = match.group(1)
if _ in done:
continue
else:
candidates.append((post.count(_), _))
done.add(_)
if candidates:
candidates.sort(key=lambda _: _[0], reverse=True)
retVal = candidates[0][1]
return retVal
def urldecode(value, encoding=None, unsafe="%%&=%s" % CUSTOM_INJECTION_MARK_CHAR, convall=False):
result = None result = None
if value: if value:

View File

@ -197,6 +197,7 @@ SQL_STATEMENTS = {
POST_HINT_CONTENT_TYPES = { POST_HINT_CONTENT_TYPES = {
POST_HINT.JSON: "application/json", POST_HINT.JSON: "application/json",
POST_HINT.MULTIPART: "multipart/form-data",
POST_HINT.SOAP: "application/soap+xml", POST_HINT.SOAP: "application/soap+xml",
POST_HINT.XML: "application/xml" POST_HINT.XML: "application/xml"
} }

View File

@ -68,6 +68,7 @@ class PLACE:
class POST_HINT: class POST_HINT:
SOAP = "SOAP" SOAP = "SOAP"
JSON = "JSON" JSON = "JSON"
MULTIPART = "MULTIPART"
XML = "XML (generic)" XML = "XML (generic)"
class HTTPMETHOD: class HTTPMETHOD:

View File

@ -471,8 +471,11 @@ LIMITED_ROWS_TEST_NUMBER = 15
# Regular expression for SOAP-like POST data # Regular expression for SOAP-like POST data
SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+</\2.*>\s*\Z" SOAP_RECOGNITION_REGEX = r"(?s)\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+</\2.*>\s*\Z"
# Regular expressing used for detecting JSON-like POST data # Regular expression used for detecting JSON-like POST data
JSON_RECOGNITION_REGEX = r'(?s)\A\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*\Z' JSON_RECOGNITION_REGEX = r'(?s)\A\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*\Z'
# Regular expression used for detecting multipart POST data
MULTIPART_RECOGNITION_REGEX = r"(?i)Content-Disposition:[^;]+;\s*name="
# Default POST data content-type # Default POST data content-type
DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded" DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"

View File

@ -41,6 +41,7 @@ from lib.core.option import __setAuthCred
from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
from lib.core.settings import HOST_ALIASES from lib.core.settings import HOST_ALIASES
from lib.core.settings import JSON_RECOGNITION_REGEX from lib.core.settings import JSON_RECOGNITION_REGEX
from lib.core.settings import MULTIPART_RECOGNITION_REGEX
from lib.core.settings import REFERER_ALIASES from lib.core.settings import REFERER_ALIASES
from lib.core.settings import RESULTS_FILE_FORMAT from lib.core.settings import RESULTS_FILE_FORMAT
from lib.core.settings import SOAP_RECOGNITION_REGEX from lib.core.settings import SOAP_RECOGNITION_REGEX
@ -95,7 +96,6 @@ def __setRequestParams():
elif test[0] not in ("n", "N"): elif test[0] not in ("n", "N"):
conf.data = re.sub(r'("[^"]+"\s*:\s*"[^"]+)"', r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR, conf.data) conf.data = re.sub(r'("[^"]+"\s*:\s*"[^"]+)"', r'\g<1>%s"' % CUSTOM_INJECTION_MARK_CHAR, conf.data)
conf.data = re.sub(r'("[^"]+"\s*:\s*)(-?\d[\d\.]*\b)', r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR, conf.data) conf.data = re.sub(r'("[^"]+"\s*:\s*)(-?\d[\d\.]*\b)', r'\g<0>%s' % CUSTOM_INJECTION_MARK_CHAR, conf.data)
kb.processUserMarks = True
kb.postHint = POST_HINT.JSON kb.postHint = POST_HINT.JSON
elif re.search(SOAP_RECOGNITION_REGEX, conf.data): elif re.search(SOAP_RECOGNITION_REGEX, conf.data):
@ -105,10 +105,19 @@ def __setRequestParams():
if test and test[0] in ("q", "Q"): if test and test[0] in ("q", "Q"):
raise sqlmapUserQuitException raise sqlmapUserQuitException
elif test[0] not in ("n", "N"): elif test[0] not in ("n", "N"):
conf.data = re.sub(r"(<([^>]+)( [^<]*)?>)([^<]+)(</\2)", r"\g<1>\g<4>*\g<5>", conf.data) conf.data = re.sub(r"(<([^>]+)( [^<]*)?>)([^<]+)(</\2)", r"\g<1>\g<4>%s\g<5>" % CUSTOM_INJECTION_MARK_CHAR, conf.data)
kb.processUserMarks = True
kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML kb.postHint = POST_HINT.SOAP if "soap" in conf.data.lower() else POST_HINT.XML
elif re.search(MULTIPART_RECOGNITION_REGEX, conf.data):
message = "Multipart like data found in POST data. "
message += "Do you want to process it? [Y/n/q] "
test = readInput(message, default="Y")
if test and test[0] in ("q", "Q"):
raise sqlmapUserQuitException
elif test[0] not in ("n", "N"):
conf.data = re.sub(r"(?si)(Content-Disposition.+?)((\r)?\n--)", r"\g<1>%s\g<2>" % CUSTOM_INJECTION_MARK_CHAR, conf.data)
kb.postHint = POST_HINT.MULTIPART
else: else:
place = PLACE.POST place = PLACE.POST
@ -119,6 +128,8 @@ def __setRequestParams():
conf.paramDict[place] = paramDict conf.paramDict[place] = paramDict
testableParameters = True testableParameters = True
kb.processUserMarks = True if kb.postHint else kb.processUserMarks
if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(map(lambda place: place in conf.parameters, [PLACE.GET, PLACE.POST])): if re.search(URI_INJECTABLE_REGEX, conf.url, re.I) and not any(map(lambda place: place in conf.parameters, [PLACE.GET, PLACE.POST])):
warnMsg = "you've provided target url without any GET " warnMsg = "you've provided target url without any GET "
warnMsg += "parameters (e.g. www.site.com/article.php?id=1) " warnMsg += "parameters (e.g. www.site.com/article.php?id=1) "

View File

@ -23,6 +23,7 @@ from lib.core.common import clearConsoleLine
from lib.core.common import cpuThrottle from lib.core.common import cpuThrottle
from lib.core.common import evaluateCode from lib.core.common import evaluateCode
from lib.core.common import extractRegexResult from lib.core.common import extractRegexResult
from lib.core.common import findMultipartPostBoundary
from lib.core.common import getCurrentThreadData from lib.core.common import getCurrentThreadData
from lib.core.common import getHostHeader from lib.core.common import getHostHeader
from lib.core.common import getRequestHeader from lib.core.common import getRequestHeader
@ -292,6 +293,15 @@ class Connect:
if post and HTTPHEADER.CONTENT_TYPE not in headers: if post and HTTPHEADER.CONTENT_TYPE not in headers:
headers[HTTPHEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE) headers[HTTPHEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE)
if headers[HTTPHEADER.CONTENT_TYPE] == POST_HINT_CONTENT_TYPES[POST_HINT.MULTIPART]:
warnMsg = "missing 'boundary parameter' in '%s' header. " % HTTPHEADER.CONTENT_TYPE
warnMsg += "Will try to reconstruct"
singleTimeWarnMessage(warnMsg)
boundary = findMultipartPostBoundary(conf.data)
if boundary:
headers[HTTPHEADER.CONTENT_TYPE] = "%s; boundary=%s" % (headers[HTTPHEADER.CONTENT_TYPE], boundary)
if auxHeaders: if auxHeaders:
for key, item in auxHeaders.items(): for key, item in auxHeaders.items():
headers[key] = item headers[key] = item