Work for Issue #197 and Issue #49

This commit is contained in:
Miroslav Stampar 2012-10-04 11:25:44 +02:00
parent bcbf0571a5
commit 461e5ebc5f
8 changed files with 87 additions and 84 deletions

View File

@ -108,15 +108,7 @@ class Agent:
newValue = self.cleanupPayload(newValue, origValue) newValue = self.cleanupPayload(newValue, origValue)
if place == PLACE.SOAP: if place in (PLACE.URI, PLACE.CUSTOM_POST):
root = ET.XML(paramString)
iterator = root.getiterator(parameter)
for child in iterator:
child.text = self.addPayloadDelimiters(newValue)
retVal = ET.tostring(root)
elif place in (PLACE.URI, PLACE.CUSTOM_POST):
retVal = paramString.replace("%s%s" % (origValue, CUSTOM_INJECTION_MARK_CHAR), self.addPayloadDelimiters(newValue)).replace(CUSTOM_INJECTION_MARK_CHAR, "") retVal = paramString.replace("%s%s" % (origValue, CUSTOM_INJECTION_MARK_CHAR), self.addPayloadDelimiters(newValue)).replace(CUSTOM_INJECTION_MARK_CHAR, "")
elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST): elif place in (PLACE.USER_AGENT, PLACE.REFERER, PLACE.HOST):
retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue)) retVal = paramString.replace(origValue, self.addPayloadDelimiters(newValue))

View File

@ -506,7 +506,6 @@ def paramToDict(place, parameters=None):
if place in conf.parameters and not parameters: if place in conf.parameters and not parameters:
parameters = conf.parameters[place] parameters = conf.parameters[place]
if place != PLACE.SOAP:
parameters = parameters.replace(", ", ",") parameters = parameters.replace(", ", ",")
parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters) parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters)
splitParams = parameters.split(conf.pDel or (DEFAULT_COOKIE_DELIMITER if place == PLACE.COOKIE else DEFAULT_GET_POST_DELIMITER)) splitParams = parameters.split(conf.pDel or (DEFAULT_COOKIE_DELIMITER if place == PLACE.COOKIE else DEFAULT_GET_POST_DELIMITER))
@ -539,24 +538,6 @@ def paramToDict(place, parameters=None):
if test[0] not in ("y", "Y"): if test[0] not in ("y", "Y"):
raise sqlmapSilentQuitException raise sqlmapSilentQuitException
else:
root = ET.XML(parameters)
iterator = root.getiterator()
for child in iterator:
parameter = child.tag
if "}" in parameter:
testParam = parameter.split("}")[1]
else:
testParam = parameter
condition = not conf.testParameter
condition |= testParam in conf.testParameter
if condition:
testableParameters[parameter] = child.text
if conf.testParameter and not testableParameters: if conf.testParameter and not testableParameters:
paramStr = ", ".join(test for test in conf.testParameter) paramStr = ", ".join(test for test in conf.testParameter)
@ -1992,7 +1973,7 @@ def urldecode(value, encoding=None):
return result return result
def urlencode(value, safe="%&=", convall=False, limit=False): def urlencode(value, safe="%&=", convall=False, limit=False):
if conf.direct or PLACE.SOAP in conf.paramDict: if conf.direct:
return value return value
count = 0 count = 0

View File

@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission
""" """
from lib.core.enums import DBMS from lib.core.enums import DBMS
from lib.core.enums import POST_HINT
from lib.core.settings import BLANK from lib.core.settings import BLANK
from lib.core.settings import NULL from lib.core.settings import NULL
from lib.core.settings import MSSQL_ALIASES from lib.core.settings import MSSQL_ALIASES
@ -193,3 +194,8 @@ SQL_STATEMENTS = {
"commit ", "commit ",
"rollback ", ), "rollback ", ),
} }
POST_HINT_CONTENT_TYPES = {
POST_HINT.JSON: "application/json",
POST_HINT.SOAP: "application/soap+xml"
}

View File

@ -58,7 +58,6 @@ class OS:
class PLACE: class PLACE:
GET = "GET" GET = "GET"
POST = "POST" POST = "POST"
SOAP = "SOAP"
URI = "URI" URI = "URI"
COOKIE = "Cookie" COOKIE = "Cookie"
USER_AGENT = "User-Agent" USER_AGENT = "User-Agent"
@ -66,6 +65,10 @@ class PLACE:
HOST = "Host" HOST = "Host"
CUSTOM_POST = "(custom) POST" CUSTOM_POST = "(custom) POST"
class POST_HINT:
SOAP = "SOAP"
JSON = "JSON"
class HTTPMETHOD: class HTTPMETHOD:
GET = "GET" GET = "GET"
POST = "POST" POST = "POST"

View File

@ -1508,6 +1508,7 @@ def __setKnowledgeBaseAttributes(flushAll=True):
kb.pageCompress = True kb.pageCompress = True
kb.pageTemplate = None kb.pageTemplate = None
kb.pageTemplates = dict() kb.pageTemplates = dict()
kb.postHint = None
kb.previousMethod = None kb.previousMethod = None
kb.processUserMarks = None kb.processUserMarks = None
kb.orderByColumns = None kb.orderByColumns = None

View File

@ -231,7 +231,7 @@ META_REFRESH_REGEX = r'<meta http-equiv="?refresh"?[^>]+content="?[^">]+url=(?P<
EMPTY_FORM_FIELDS_REGEX = r'(?P<result>[^=]+=(&|\Z))' EMPTY_FORM_FIELDS_REGEX = r'(?P<result>[^=]+=(&|\Z))'
# Regular expression for soap message recognition # Regular expression for soap message recognition
SOAP_REGEX = r"\A(<\?xml[^>]+>)?\s*<soap.+</soap" SOAP_RECOGNITION_REGEX = r"\A(<\?xml[^>]+>)?\s*<([^> ]+)( [^>]+)?>.+</\2"
# Reference: http://www.cs.ru.nl/bachelorscripties/2010/Martin_Devillers___0437999___Analyzing_password_strength.pdf # Reference: http://www.cs.ru.nl/bachelorscripties/2010/Martin_Devillers___0437999___Analyzing_password_strength.pdf
COMMON_PASSWORD_SUFFIXES = ("1", "123", "2", "12", "3", "13", "7", "11", "5", "22", "23", "01", "4", "07", "21", "14", "10", "06", "08", "8", "15", "69", "16", "6", "18") COMMON_PASSWORD_SUFFIXES = ("1", "123", "2", "12", "3", "13", "7", "11", "5", "22", "23", "01", "4", "07", "21", "14", "10", "06", "08", "8", "15", "69", "16", "6", "18")
@ -471,4 +471,7 @@ VIEWSTATE_REGEX = r'(?P<name>__VIEWSTATE[^"]*)[^>]+value="(?P<name>[^"]+)'
LIMITED_ROWS_TEST_NUMBER = 15 LIMITED_ROWS_TEST_NUMBER = 15
# Regular expressing used for detecting JSON-like POST data # Regular expressing used for detecting JSON-like POST data
JSON_RECOGNITION_REGEX = r'(?s)\A\s*.*"[^"]+"\s*:\s*"[^"]+".+\}\s*\Z' JSON_RECOGNITION_REGEX = r'\A\s*\{.*"[^"]+"\s*:\s*("[^"]+"|\d+).*\}\s*\Z'
# Default POST data content-type
DEFAULT_CONTENT_TYPE = "application/x-www-form-urlencoded"

View File

@ -29,6 +29,7 @@ from lib.core.enums import HASHDB_KEYS
from lib.core.enums import HTTPHEADER from lib.core.enums import HTTPHEADER
from lib.core.enums import HTTPMETHOD from lib.core.enums import HTTPMETHOD
from lib.core.enums import PLACE from lib.core.enums import PLACE
from lib.core.enums import POST_HINT
from lib.core.exception import sqlmapFilePathException from lib.core.exception import sqlmapFilePathException
from lib.core.exception import sqlmapGenericException from lib.core.exception import sqlmapGenericException
from lib.core.exception import sqlmapSyntaxException from lib.core.exception import sqlmapSyntaxException
@ -42,7 +43,7 @@ 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 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_REGEX from lib.core.settings import SOAP_RECOGNITION_REGEX
from lib.core.settings import SUPPORTED_DBMS from lib.core.settings import SUPPORTED_DBMS
from lib.core.settings import UNENCODED_ORIGINAL_VALUE from lib.core.settings import UNENCODED_ORIGINAL_VALUE
from lib.core.settings import UNICODE_ENCODING from lib.core.settings import UNICODE_ENCODING
@ -78,12 +79,35 @@ def __setRequestParams():
errMsg = "HTTP POST method depends on HTTP data value to be posted" errMsg = "HTTP POST method depends on HTTP data value to be posted"
raise sqlmapSyntaxException, errMsg raise sqlmapSyntaxException, errMsg
if conf.data: if re.search(JSON_RECOGNITION_REGEX, conf.data or ""):
message = "JSON 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'("[^"]+"\s*:\s*"[^"]+)"', r'\g<1>*"', conf.data)
conf.data = re.sub(r'("[^"]+"\s*:\s*)(\d+)', r'\g<1>"\g<2>*"', conf.data)
kb.processUserMarks = True
kb.postHint = POST_HINT.JSON
elif re.search(SOAP_RECOGNITION_REGEX, conf.data or ""):
message = "SOAP 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"(<([^>]+)( [^<]*)?>)([^<]+)(</\2)", r"\g<1>\g<4>*\g<5>", conf.data)
kb.processUserMarks = True
kb.postHint = POST_HINT.SOAP
elif conf.data:
if hasattr(conf.data, UNENCODED_ORIGINAL_VALUE): if hasattr(conf.data, UNENCODED_ORIGINAL_VALUE):
original = getattr(conf.data, UNENCODED_ORIGINAL_VALUE) original = getattr(conf.data, UNENCODED_ORIGINAL_VALUE)
setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original) setattr(conf.data, UNENCODED_ORIGINAL_VALUE, original)
place = PLACE.SOAP if re.match(SOAP_REGEX, conf.data, re.I | re.M) else PLACE.POST place = PLACE.POST
conf.parameters[place] = conf.data conf.parameters[place] = conf.data
paramDict = paramToDict(place, conf.data) paramDict = paramToDict(place, conf.data)
@ -111,17 +135,6 @@ def __setRequestParams():
elif test[0] in ("q", "Q"): elif test[0] in ("q", "Q"):
raise sqlmapUserQuitException raise sqlmapUserQuitException
if re.search(JSON_RECOGNITION_REGEX, conf.data or ""):
message = "JSON 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'("[^"]+"\s*:\s*"[^"]+)"', r'\g<1>*"', conf.data or "")
kb.processUserMarks = True
for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data)): for place, value in ((PLACE.URI, conf.url), (PLACE.CUSTOM_POST, conf.data)):
if CUSTOM_INJECTION_MARK_CHAR in (value or ""): if CUSTOM_INJECTION_MARK_CHAR in (value or ""):
if kb.processUserMarks is None: if kb.processUserMarks is None:

View File

@ -6,6 +6,7 @@ See the file 'doc/COPYING' for copying permission
""" """
import httplib import httplib
import json
import re import re
import socket import socket
import string import string
@ -38,17 +39,20 @@ from lib.core.common import urlencode
from lib.core.data import conf from lib.core.data import conf
from lib.core.data import kb from lib.core.data import kb
from lib.core.data import logger from lib.core.data import logger
from lib.core.dicts import POST_HINT_CONTENT_TYPES
from lib.core.enums import CUSTOM_LOGGING from lib.core.enums import CUSTOM_LOGGING
from lib.core.enums import HTTPHEADER from lib.core.enums import HTTPHEADER
from lib.core.enums import HTTPMETHOD from lib.core.enums import HTTPMETHOD
from lib.core.enums import NULLCONNECTION from lib.core.enums import NULLCONNECTION
from lib.core.enums import PAYLOAD from lib.core.enums import PAYLOAD
from lib.core.enums import PLACE from lib.core.enums import PLACE
from lib.core.enums import POST_HINT
from lib.core.enums import REDIRECTION from lib.core.enums import REDIRECTION
from lib.core.exception import sqlmapCompressionException from lib.core.exception import sqlmapCompressionException
from lib.core.exception import sqlmapConnectionException from lib.core.exception import sqlmapConnectionException
from lib.core.exception import sqlmapSyntaxException from lib.core.exception import sqlmapSyntaxException
from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR from lib.core.settings import CUSTOM_INJECTION_MARK_CHAR
from lib.core.settings import DEFAULT_CONTENT_TYPE
from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_HEADER_VALUE
from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE from lib.core.settings import HTTP_ACCEPT_ENCODING_HEADER_VALUE
from lib.core.settings import HTTP_SILENT_TIMEOUT from lib.core.settings import HTTP_SILENT_TIMEOUT
@ -260,7 +264,7 @@ class Connect:
requestMsg += "?%s" % get requestMsg += "?%s" % get
if conf.method == HTTPMETHOD.POST and not post: if conf.method == HTTPMETHOD.POST and not post:
for place in (PLACE.POST, PLACE.SOAP): for place in (PLACE.POST,):
if place in conf.parameters: if place in conf.parameters:
post = conf.parameters[place] post = conf.parameters[place]
break break
@ -284,6 +288,9 @@ class Connect:
headers[HTTPHEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if method != HTTPMETHOD.HEAD and kb.pageCompress else "identity" headers[HTTPHEADER.ACCEPT_ENCODING] = HTTP_ACCEPT_ENCODING_HEADER_VALUE if method != HTTPMETHOD.HEAD and kb.pageCompress else "identity"
headers[HTTPHEADER.HOST] = host or getHostHeader(url) headers[HTTPHEADER.HOST] = host or getHostHeader(url)
if post:
headers[HTTPHEADER.CONTENT_TYPE] = POST_HINT_CONTENT_TYPES.get(kb.postHint, DEFAULT_CONTENT_TYPE)
if auxHeaders: if auxHeaders:
for key, item in auxHeaders.items(): for key, item in auxHeaders.items():
headers[key] = item headers[key] = item
@ -308,9 +315,6 @@ class Connect:
requestHeaders += "\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies)) requestHeaders += "\n%s" % ("Cookie: %s" % ";".join("%s=%s" % (getUnicode(cookie.name), getUnicode(cookie.value)) for cookie in cookies))
if post: if post:
if not getRequestHeader(req, HTTPHEADER.CONTENT_TYPE):
requestHeaders += "\n%s: %s" % (string.capwords(HTTPHEADER.CONTENT_TYPE), "application/x-www-form-urlencoded")
if not getRequestHeader(req, HTTPHEADER.CONTENT_LENGTH): if not getRequestHeader(req, HTTPHEADER.CONTENT_LENGTH):
requestHeaders += "\n%s: %d" % (string.capwords(HTTPHEADER.CONTENT_LENGTH), len(post)) requestHeaders += "\n%s: %d" % (string.capwords(HTTPHEADER.CONTENT_LENGTH), len(post))
@ -578,10 +582,13 @@ class Connect:
logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload)) logger.log(CUSTOM_LOGGING.PAYLOAD, safecharencode(payload))
if place == PLACE.SOAP: if place == PLACE.CUSTOM_POST:
if kb.postHint == POST_HINT.SOAP:
# payloads in SOAP should have chars > and < replaced # payloads in SOAP should have chars > and < replaced
# with their HTML encoded counterparts # with their HTML encoded counterparts
payload = payload.replace('>', "&gt;").replace('<', "&lt;") payload = payload.replace('>', "&gt;").replace('<', "&lt;")
elif kb.postHint == POST_HINT.JSON:
payload = json.dumps(payload)[1:-1]
value = agent.replacePayload(value, payload) value = agent.replacePayload(value, payload)
else: else:
@ -608,9 +615,6 @@ class Connect:
if PLACE.CUSTOM_POST in conf.parameters: 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 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
if PLACE.COOKIE in conf.parameters: if PLACE.COOKIE in conf.parameters:
cookie = conf.parameters[PLACE.COOKIE] if place != PLACE.COOKIE or not value else value cookie = conf.parameters[PLACE.COOKIE] if place != PLACE.COOKIE or not value else value
@ -686,9 +690,9 @@ class Connect:
msg += "in this kind of situations? [Y/n]" msg += "in this kind of situations? [Y/n]"
skipUrlEncode = conf.skipUrlEncode = readInput(msg, default="Y").upper() != "N" skipUrlEncode = conf.skipUrlEncode = readInput(msg, default="Y").upper() != "N"
if place not in (PLACE.POST, PLACE.SOAP, PLACE.CUSTOM_POST) and hasattr(post, UNENCODED_ORIGINAL_VALUE): if place not in (PLACE.POST, PLACE.CUSTOM_POST) and hasattr(post, UNENCODED_ORIGINAL_VALUE):
post = getattr(post, UNENCODED_ORIGINAL_VALUE) post = getattr(post, UNENCODED_ORIGINAL_VALUE)
elif not skipUrlEncode and place not in (PLACE.SOAP,): elif not skipUrlEncode and kb.postHint not in (POST_HINT.JSON, POST_HINT.SOAP):
post = urlencode(post) post = urlencode(post)
if timeBasedCompare: if timeBasedCompare: