diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 0a923f71f..4bec5b3d7 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -38,6 +38,7 @@ from lib.core.common import readInput from lib.core.common import showStaticWords from lib.core.common import DynamicContentItem from lib.core.convert import md5hash +from lib.core.convert import urlencode from lib.core.data import conf from lib.core.data import kb from lib.core.data import logger @@ -105,9 +106,6 @@ def heuristicCheckSqlInjection(place, parameter, value): prefix = "" postfix = "" - if place == "URI": - return - if conf.prefix or conf.postfix: if conf.prefix: prefix = conf.prefix @@ -116,9 +114,11 @@ def heuristicCheckSqlInjection(place, parameter, value): postfix = conf.postfix payload = "%s%s%s" % (prefix, randomStr(length=10, alphabet=['"', '\'', ')', '(']), postfix) + if place == "URI": + payload = conf.paramDict[place][parameter].replace('*', payload) Request.queryPage(payload, place) result = kb.lastErrorPage and kb.lastErrorPage[0]==kb.lastRequestUID - infoMsg = "heuristics show that %s parameter '%s' is " % (place, parameter) + infoMsg = "(error based) heuristics show that %s parameter '%s' is " % (place, parameter) if result: infoMsg += "injectable" logger.info(infoMsg) diff --git a/lib/core/common.py b/lib/core/common.py index 0c3e05717..15b0603d9 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -663,6 +663,7 @@ def setPaths(): paths.SQLMAP_CONFIG = os.path.join(paths.SQLMAP_ROOT_PATH, "sqlmap-%s.conf" % randomStr()) paths.COMMON_OUTPUTS = os.path.join(paths.SQLMAP_TXT_PATH, 'common-outputs.txt') paths.COMMON_TABLES = os.path.join(paths.SQLMAP_TXT_PATH, "common-tables.txt") + paths.SQLKEYWORDS = os.path.join(paths.SQLMAP_TXT_PATH, "keywords.txt") paths.FUZZ_VECTORS = os.path.join(paths.SQLMAP_TXT_PATH, "fuzz_vectors.txt") paths.DETECTION_RULES_XML = os.path.join(paths.SQLMAP_XML_PATH, "detection.xml") paths.ERRORS_XML = os.path.join(paths.SQLMAP_XML_PATH, "errors.xml") diff --git a/lib/core/option.py b/lib/core/option.py index 824da4996..865930bb6 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -36,6 +36,7 @@ import urlparse from extra.keepalive import keepalive from extra.xmlobject import xmlobject from lib.core.common import getConsoleWidth +from lib.core.common import getFileItems from lib.core.common import getFileType from lib.core.common import normalizePath from lib.core.common import ntToPosixSlashes @@ -1057,12 +1058,13 @@ def __setKnowledgeBaseAttributes(): kb.lastErrorPage = None kb.headersCount = 0 kb.headersFp = {} + kb.hintValue = None kb.htmlFp = [] kb.injParameter = None kb.injPlace = None kb.injType = None kb.injections = xmlobject.XMLFile(path=paths.INJECTIONS_XML) - kb.hintValue = None + kb.keywords = getFileItems(paths.SQLKEYWORDS) kb.nullConnection = None # Back-end DBMS underlying operating system fingerprint via banner (-b) diff --git a/lib/request/connect.py b/lib/request/connect.py index 94155b730..416aa6763 100644 --- a/lib/request/connect.py +++ b/lib/request/connect.py @@ -307,13 +307,13 @@ class Connect: if not place: place = kb.injPlace - + if kb.tamperFunctions: for function in kb.tamperFunctions: value = function(place, value) if "GET" in conf.parameters: - get = conf.parameters["GET"] if place != "GET" or not value else value + get = conf.parameters["GET"] if place != "GET" or not value else value if "POST" in conf.parameters: post = conf.parameters["POST"] if place != "POST" or not value else value diff --git a/tamper/randomblanks.py b/tamper/randomblanks.py index 6316dc1ab..e46aa7148 100644 --- a/tamper/randomblanks.py +++ b/tamper/randomblanks.py @@ -2,12 +2,32 @@ import re import string from lib.core.common import randomRange -from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.core.convert import urldecode +from lib.core.convert import urlencode +from lib.core.data import kb """ value -> value with inserted random blanks (e.g., INSERT->IN/**/S/**/ERT) """ -#TODO: all -#TODO: only do it for deepness = 0 regarding '" def tamper(place, value): - return value + retVal = value + if value: + if place != "URI": + retVal = urldecode(retVal) + + for match in re.finditer(r"[A-Za-z_]+", retVal): + word = match.group() + + if len(word) < 2: + continue + + if word.upper() in kb.keywords: + newWord = word[0] + for i in xrange(1, len(word) - 1): + newWord += "%s%s" % ("/**/" if randomRange(0,1) else "", word[i]) + newWord += word[-1] + retVal = retVal.replace(word, newWord) + + if place != "URI": + retVal = urlencode(retVal) + return retVal diff --git a/tamper/randomcase.py b/tamper/randomcase.py index 2b34e187a..bc45d9dbd 100644 --- a/tamper/randomcase.py +++ b/tamper/randomcase.py @@ -2,19 +2,28 @@ import re import string from lib.core.common import randomRange -from lib.core.exception import sqlmapUnsupportedFeatureException +from lib.core.convert import urldecode +from lib.core.convert import urlencode +from lib.core.data import kb """ value -> chars from value with random case (e.g., INSERT->InsERt) """ -#TODO: only do it for deepness = 0 regarding '" def tamper(place, value): retVal = value if value: - retVal = "" - for i in xrange(len(value)): - if value[i].isalpha(): - retVal += value[i].upper() if randomRange(0,1) else value[i].lower() - else: - retVal += value[i] + if place != "URI": + retVal = urldecode(retVal) + + for match in re.finditer(r"[A-Za-z_]+", retVal): + word = match.group() + if word.upper() in kb.keywords: + newWord = str() + for i in xrange(len(word)): + newWord += word[i].upper() if randomRange(0,1) else word[i].lower() + retVal = retVal.replace(word, newWord) + + if place != "URI": + retVal = urlencode(retVal) + return retVal diff --git a/tamper/space2comment.py b/tamper/space2comment.py index 1fc269e95..cbc60e2e7 100644 --- a/tamper/space2comment.py +++ b/tamper/space2comment.py @@ -6,12 +6,28 @@ from lib.core.convert import urlencode """ ' ' -> /**/ (e.g., SELECT id FROM users->SELECT/**/id/**/FROM users) """ -#TODO: only do it for deepness = 0 regarding '" def tamper(place, value): + retVal = value if value: if place != "URI": value = urldecode(value) - value = value.replace(" ", "/**/") + + retVal = "" + qoute, doublequote, firstspace = False, False, False + + for i in xrange(len(value)): + if not firstspace: + firstspace = value[i].isspace() + elif value[i] == '\'': + qoute = not qoute + elif value[i] == '"': + doublequote = not doublequote + elif value[i]==" " and not doublequote and not qoute: + retVal += "/**/" + continue + retVal += value[i] + if place != "URI": - value = urlencode(value) - return value + retVal = urlencode(retVal) + return retVal + diff --git a/txt/keywords.txt b/txt/keywords.txt new file mode 100644 index 000000000..e34a2269f --- /dev/null +++ b/txt/keywords.txt @@ -0,0 +1,257 @@ +#SQL-92 keywords (reference: http://developer.mimer.com/validator/sql-reserved-words.tml) + +ABSOLUTE +ACTION +ADD +ALL +ALLOCATE +ALTER +AND +ANY +AREĀ  +AS +ASC +ASSERTION +AT +AUTHORIZATION +AVG +BEGIN +BETWEEN +BIT +BIT_LENGTH +BOTH +BY +CALL +CASCADE +CASCADED +CASE +CAST +CATALOG +CHAR +CHAR_LENGTH +CHARACTER +CHARACTER_LENGTH +CHECK +CLOSE +COALESCE +COLLATE +COLLATION +COLUMN +COMMIT +CONDITION +CONNECT +CONNECTION +CONSTRAINT +CONSTRAINTS +CONTAINS +CONTINUE +CONVERT +CORRESPONDING +COUNT +CREATE +CROSS +CURRENT +CURRENT_DATE +CURRENT_PATH +CURRENT_TIME +CURRENT_TIMESTAMP +CURRENT_USER +CURSOR +DATE +DAY +DEALLOCATE +DEC +DECIMAL +DECLARE +DEFAULT +DEFERRABLE +DEFERRED +DELETE +DESC +DESCRIBE +DESCRIPTOR +DETERMINISTIC +DIAGNOSTICS +DISCONNECT +DISTINCT +DO +DOMAIN +DOUBLE +DROP +ELSE +ELSEIF +END +ESCAPE +EXCEPT +EXCEPTION +EXEC +EXECUTE +EXISTS +EXIT +EXTERNAL +EXTRACT +FALSE +FETCH +FIRST +FLOAT +FOR +FOREIGN +FOUND +FROM +FULL +FUNCTION +GET +GLOBAL +GO +GOTO +GRANT +GROUP +HANDLER +HAVING +HOUR +IDENTITY +IF +IMMEDIATE +IN +INDICATOR +INITIALLY +INNER +INOUT +INPUT +INSENSITIVE +INSERT +INT +INTEGER +INTERSECT +INTERVAL +INTO +IS +ISOLATION +JOIN +KEY +LANGUAGE +LAST +LEADING +LEAVE +LEFT +LEVEL +LIKE +LOCAL +LOOP +LOWER +MATCH +MAX +MIN +MINUTE +MODULE +MONTH +NAMES +NATIONAL +NATURAL +NCHAR +NEXT +NO +NOT +NULL +NULLIF +NUMERIC +OCTET_LENGTH +OF +ON +ONLY +OPEN +OPTION +OR +ORDER +OUT +OUTER +OUTPUT +OVERLAPS +PAD +PARAMETER +PARTIAL +PATH +POSITION +PRECISION +PREPARE +PRESERVE +PRIMARY +PRIOR +PRIVILEGES +PROCEDURE +PUBLIC +READ +REAL +REFERENCES +RELATIVE +REPEAT +RESIGNAL +RESTRICT +RETURN +RETURNS +REVOKE +RIGHT +ROLLBACK +ROUTINE +ROWS +SCHEMA +SCROLL +SECOND +SECTION +SELECT +SESSION +SESSION_USER +SET +SIGNAL +SIZE +SMALLINT +SOME +SPACE +SPECIFIC +SQL +SQLCODE +SQLERROR +SQLEXCEPTION +SQLSTATE +SQLWARNING +SUBSTRING +SUM +SYSTEM_USER +TABLE +TEMPORARY +THEN +TIME +TIMESTAMP +TIMEZONE_HOUR +TIMEZONE_MINUTE +TO +TRAILING +TRANSACTION +TRANSLATE +TRANSLATION +TRIM +TRUE +UNDO +UNION +UNIQUE +UNKNOWN +UNTIL +UPDATE +UPPER +USAGE +USER +USING +VALUE +VALUES +VARCHAR +VARYING +VIEW +WHEN +WHENEVER +WHERE +WHILE +WITH +WORK +WRITE +YEAR +ZONE