From 54752a9101b043fc03bf02be4693193fd0683669 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 11:44:58 +0000 Subject: [PATCH 1/9] typo fix --- xml/livetests.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 8393d0022..28e229b90 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -304,7 +304,7 @@ - + From 9149d77cc81f6cdf2683780f53d7ddd629412de9 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 12:17:56 +0000 Subject: [PATCH 2/9] removed duplicate code - fixes issue #310 --- lib/core/agent.py | 65 +++++++++++++++++++++++++++++++++++ lib/request/inject.py | 67 ++++--------------------------------- lib/techniques/error/use.py | 66 +++--------------------------------- lib/techniques/union/use.py | 65 +++-------------------------------- 4 files changed, 80 insertions(+), 183 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index c97401f62..487eaf174 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -675,6 +675,71 @@ class Agent(object): return unionQuery + def limitCondition(self, expression, dump=False): + startLimit = 0 + stopLimit = None + limitCond = True + + limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) + limitRegExp2 = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query2, expression, re.I) + topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) + + if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): + limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query + + if limitGroupStart.isdigit(): + if limitRegExp2: + startLimit = 0 + stopLimit = limitRegExp2.group(int(limitGroupStart)) + else: + startLimit = int(limitRegExp.group(int(limitGroupStart))) + stopLimit = limitRegExp.group(int(limitGroupStop)) + limitCond = int(stopLimit) > 1 + + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + if limitRegExp: + limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query + limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query + + if limitGroupStart.isdigit(): + startLimit = int(limitRegExp.group(int(limitGroupStart))) + + stopLimit = limitRegExp.group(int(limitGroupStop)) + limitCond = int(stopLimit) > 1 + elif topLimit: + startLimit = 0 + stopLimit = int(topLimit.group(1)) + limitCond = int(stopLimit) > 1 + + elif Backend.isDbms(DBMS.ORACLE): + limitCond = False + + # We assume that only queries NOT containing a "LIMIT #, 1" + # (or equivalent depending on the back-end DBMS) can return + # multiple entries + if limitCond: + if (limitRegExp or limitRegExp2) and stopLimit is not None: + stopLimit = int(stopLimit) + + # From now on we need only the expression until the " LIMIT " + # (or equivalent, depending on the back-end DBMS) word + if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): + stopLimit += startLimit + _ = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) + expression = expression[:_] + + elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): + stopLimit += startLimit + elif dump: + if conf.limitStart: + startLimit = conf.limitStart - 1 + if conf.limitStop: + stopLimit = conf.limitStop + + return expression, limitCond, topLimit, startLimit, stopLimit + def limitQuery(self, num, query, field=None, uniqueField=None): """ Take in input a query string and return its limited query string. diff --git a/lib/request/inject.py b/lib/request/inject.py index 82dae2431..bab81c138 100644 --- a/lib/request/inject.py +++ b/lib/request/inject.py @@ -139,8 +139,6 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char startLimit = 0 stopLimit = None outputs = BigArray() - untilLimitChar = None - untilOrderChar = None if not unpack: return _goInference(payload, expression, charsetType, firstChar, lastChar, dump) @@ -160,69 +158,18 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char # If we have been here from SQL query/shell we have to check if # the SQL query might return multiple entries and in such case - # forge the SQL limiting the query output one entry per time - # NOTE: I assume that only queries that get data from a table + # forge the SQL limiting the query output one entry at a time + # NOTE: we assume that only queries that get data from a table # can return multiple entries if fromUser and " FROM " in expression.upper() and ((Backend.getIdentifiedDbms() \ not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and not \ expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): + expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression) - limitCond = True - limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) - limitRegExp2 = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query2, expression, re.I) - topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) - - if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query - limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query - - if limitGroupStart.isdigit(): - if limitRegExp2: - startLimit = 0 - stopLimit = limitRegExp2.group(int(limitGroupStart)) - else: - startLimit = int(limitRegExp.group(int(limitGroupStart))) - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - if limitRegExp: - limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query - limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query - - if limitGroupStart.isdigit(): - startLimit = int(limitRegExp.group(int(limitGroupStart))) - - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - elif topLimit: - startLimit = 0 - stopLimit = int(topLimit.group(1)) - limitCond = int(stopLimit) > 1 - - elif Backend.isDbms(DBMS.ORACLE): - limitCond = False - - # We assume that only queries NOT containing a "LIMIT #, 1" - # (or equivalent depending on the back-end DBMS) can return - # multiple entries if limitCond: - if (limitRegExp or limitRegExp2) and stopLimit is not None: - stopLimit = int(stopLimit) - - # From now on we need only the expression until the " LIMIT " - # (or equivalent, depending on the back-end DBMS) word - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - stopLimit += startLimit - untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) - expression = expression[:untilLimitChar] - - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - stopLimit += startLimit - test = True + if not stopLimit or stopLimit <= 1: if Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE and expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]): test = False @@ -232,9 +179,9 @@ def _goInferenceProxy(expression, fromUser=False, batch=False, unpack=True, char countFirstField = queries[Backend.getIdentifiedDbms()].count.query % expressionFieldsList[0] countedExpression = expression.replace(expressionFields, countFirstField, 1) - if re.search(" ORDER BY ", expression, re.I): - untilOrderChar = countedExpression.index(" ORDER BY ") - countedExpression = countedExpression[:untilOrderChar] + if " ORDER BY " in expression.upper(): + _ = countedExpression.upper().rindex(" ORDER BY ") + countedExpression = countedExpression[:_] if not stopLimit: count = _goInference(payload, countedExpression, charsetType=CHARSET_TYPE.DIGITS, firstChar=firstChar, lastChar=lastChar) diff --git a/lib/techniques/error/use.py b/lib/techniques/error/use.py index 7c997665d..4d2e9e4d8 100644 --- a/lib/techniques/error/use.py +++ b/lib/techniques/error/use.py @@ -238,14 +238,13 @@ def errorUse(expression, dump=False): stopLimit = None output = None outputs = None - untilLimitChar = None _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(expression) # We have to check if the SQL query might return multiple entries # and in such case forge the SQL limiting the query output one - # entry per time - # NOTE: I assume that only queries that get data from a table can + # entry at a time + # NOTE: we assume that only queries that get data from a table can # return multiple entries if (dump and (conf.limitStart or conf.limitStop)) or (" FROM " in \ expression.upper() and ((Backend.getIdentifiedDbms() not in FROM_DUMMY_TABLE) \ @@ -253,70 +252,13 @@ def errorUse(expression, dump=False): expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and ("(CASE" not in expression.upper() or ("(CASE" in expression.upper() and "WHEN use" in expression))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): + expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) - limitCond = True - limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) - limitRegExp2 = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query2, expression, re.I) - topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) - - if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query - limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query - - if limitGroupStart.isdigit(): - if limitRegExp2: - startLimit = 0 - stopLimit = limitRegExp2.group(int(limitGroupStart)) - else: - startLimit = int(limitRegExp.group(int(limitGroupStart))) - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - if limitRegExp: - limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query - limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query - - if limitGroupStart.isdigit(): - startLimit = int(limitRegExp.group(int(limitGroupStart))) - - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - elif topLimit: - startLimit = 0 - stopLimit = int(topLimit.group(1)) - limitCond = int(stopLimit) > 1 - - elif Backend.isDbms(DBMS.ORACLE): - limitCond = False - - # I assume that only queries NOT containing a "LIMIT #, 1" - # (or equivalent depending on the back-end DBMS) can return - # multiple entries if limitCond: - if (limitRegExp or limitRegExp2) and stopLimit is not None: - stopLimit = int(stopLimit) - - # From now on we need only the expression until the " LIMIT " - # (or equivalent, depending on the back-end DBMS) word - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - stopLimit += startLimit - untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) - expression = expression[:untilLimitChar] - - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - stopLimit += startLimit - elif dump: - if conf.limitStart: - startLimit = conf.limitStart - 1 - if conf.limitStop: - stopLimit = conf.limitStop - # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) - if " ORDER BY " in expression: + if " ORDER BY " in expression.upper(): _ = countedExpression.upper().rindex(" ORDER BY ") countedExpression = countedExpression[:_] diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 0512972c7..ce14b19c5 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -164,9 +164,9 @@ def unionUse(expression, unpack=True, dump=False): expression = expression[:expression.upper().rindex(" ORDER BY ")] # We have to check if the SQL query might return multiple entries - # and in such case forge the SQL limiting the query output one - # entry per time - # NOTE: I assume that only queries that get data from a table can + # if the technique is partial UNION query and in such case forge the + # SQL limiting the query output one entry at a time + # NOTE: we assume that only queries that get data from a table can # return multiple entries if (kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where == PAYLOAD.WHERE.NEGATIVE or \ (dump and (conf.limitStart or conf.limitStop))) and \ @@ -174,66 +174,9 @@ def unionUse(expression, unpack=True, dump=False): not in FROM_DUMMY_TABLE) or (Backend.getIdentifiedDbms() in FROM_DUMMY_TABLE \ and not expression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()]))) \ and not re.search(SQL_SCALAR_REGEX, expression, re.I): + expression, limitCond, topLimit, startLimit, stopLimit = agent.limitCondition(expression, dump) - limitCond = True - limitRegExp = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query, expression, re.I) - limitRegExp2 = re.search(queries[Backend.getIdentifiedDbms()].limitregexp.query2, expression, re.I) - topLimit = re.search("TOP\s+([\d]+)\s+", expression, re.I) - - if (limitRegExp or limitRegExp2) or (Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE) and topLimit): - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query - limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query - - if limitGroupStart.isdigit(): - if limitRegExp2: - startLimit = 0 - stopLimit = limitRegExp2.group(int(limitGroupStart)) - else: - startLimit = int(limitRegExp.group(int(limitGroupStart))) - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - if limitRegExp: - limitGroupStart = queries[Backend.getIdentifiedDbms()].limitgroupstart.query - limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query - - if limitGroupStart.isdigit(): - startLimit = int(limitRegExp.group(int(limitGroupStart))) - - stopLimit = limitRegExp.group(int(limitGroupStop)) - limitCond = int(stopLimit) > 1 - elif topLimit: - startLimit = 0 - stopLimit = int(topLimit.group(1)) - limitCond = int(stopLimit) > 1 - - elif Backend.isDbms(DBMS.ORACLE): - limitCond = False - - # I assume that only queries NOT containing a "LIMIT #, 1" - # (or equivalent depending on the back-end DBMS) can return - # multiple entries if limitCond: - if (limitRegExp or limitRegExp2) and stopLimit is not None: - stopLimit = int(stopLimit) - - # From now on we need only the expression until the " LIMIT " - # (or equivalent, depending on the back-end DBMS) word - if Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE): - stopLimit += startLimit - untilLimitChar = expression.index(queries[Backend.getIdentifiedDbms()].limitstring.query) - expression = expression[:untilLimitChar] - - elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): - stopLimit += startLimit - elif dump: - if conf.limitStart: - startLimit = conf.limitStart - 1 - if conf.limitStop: - stopLimit = conf.limitStop - # Count the number of SQL query entries output countedExpression = expression.replace(expressionFields, queries[Backend.getIdentifiedDbms()].count.query % ('*' if len(expressionFieldsList) > 1 else expressionFields), 1) From 5ceadf02aefc09c6f116012bd19868a4a2adc8e8 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 12:22:45 +0000 Subject: [PATCH 3/9] fixed test cases now that MySQL test db has two more tables and removed old test cases, soon to be replaced with new ones for other DBMSes --- xml/livetests.xml | 419 ++-------------------------------------------- 1 file changed, 12 insertions(+), 407 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index 28e229b90..e573c347a 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -44,7 +44,7 @@ - + @@ -87,7 +87,7 @@ - + @@ -130,7 +130,7 @@ - + @@ -173,7 +173,7 @@ - + @@ -216,7 +216,7 @@ - + @@ -259,7 +259,7 @@ - + @@ -369,7 +369,7 @@ - + @@ -383,7 +383,7 @@ - + @@ -397,7 +397,7 @@ - + @@ -411,7 +411,7 @@ - + @@ -424,7 +424,7 @@ - + @@ -437,7 +437,7 @@ - + @@ -654,399 +654,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 2bc2c0431c14fdba5cb741a088a50ffd984a4306 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 12:33:37 +0000 Subject: [PATCH 4/9] fixed test cases --- lib/core/testing.py | 2 +- xml/livetests.xml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/core/testing.py b/lib/core/testing.py index 1dc9337ff..906865a9d 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -206,7 +206,7 @@ def runCase(switches=None, parse=None): retVal = False if parse and retVal: - ifile = open(conf.dumper.getOutputFile(), 'r') + ifile = open(conf.dumper.getOutputFile(), "rb") content = ifile.read() ifile.close() for item in parse: diff --git a/xml/livetests.xml b/xml/livetests.xml index e573c347a..f365a3d59 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -366,10 +366,10 @@ - + - + @@ -380,10 +380,10 @@ - + - + @@ -394,10 +394,10 @@ - + - + @@ -411,7 +411,7 @@ - + @@ -424,7 +424,7 @@ - + @@ -437,7 +437,7 @@ - + From e583ba6826c1f899a0c8173504db7967c5ef7060 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 12:35:36 +0000 Subject: [PATCH 5/9] no point retesting all for time-based too as it uses same engine of boolean-based --- xml/livetests.xml | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/xml/livetests.xml b/xml/livetests.xml index f365a3d59..2617b5a06 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -190,18 +190,6 @@ - - - - - - - - - - - - @@ -211,15 +199,6 @@ - - - - - - - - - From b91c829103e5230efb6146c1884bb74fd8f41897 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 12:42:31 +0000 Subject: [PATCH 6/9] minor bug fix (issue #310) --- lib/core/agent.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core/agent.py b/lib/core/agent.py index 487eaf174..f9b72b397 100644 --- a/lib/core/agent.py +++ b/lib/core/agent.py @@ -690,12 +690,12 @@ class Agent(object): limitGroupStop = queries[Backend.getIdentifiedDbms()].limitgroupstop.query if limitGroupStart.isdigit(): - if limitRegExp2: - startLimit = 0 - stopLimit = limitRegExp2.group(int(limitGroupStart)) - else: + if limitRegExp: startLimit = int(limitRegExp.group(int(limitGroupStart))) stopLimit = limitRegExp.group(int(limitGroupStop)) + elif limitRegExp2: + startLimit = 0 + stopLimit = limitRegExp2.group(int(limitGroupStart)) limitCond = int(stopLimit) > 1 elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE): From 128597ee7ea4cf570afa3c6f09bafe621f2a60a7 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 12:45:46 +0000 Subject: [PATCH 7/9] --run-case is now case insensitive --- lib/core/testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/testing.py b/lib/core/testing.py index 906865a9d..fdb036530 100644 --- a/lib/core/testing.py +++ b/lib/core/testing.py @@ -129,7 +129,7 @@ def liveTest(): if case.hasAttribute("name"): name = case.getAttribute("name") - if conf.runCase and ((conf.runCase.isdigit() and conf.runCase != count) or not re.search(conf.runCase, name, re.DOTALL)): + if conf.runCase and ((conf.runCase.isdigit() and conf.runCase != count) or not re.search(conf.runCase, name, re.DOTALL | re.I)): continue if case.getElementsByTagName("switches"): From 259b345f1f208fb6ff7522f0b8630d631cde8ba3 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 13:10:54 +0000 Subject: [PATCH 8/9] catch ImportError exception if libmagic is not installed --- lib/techniques/union/use.py | 6 +- thirdparty/magic/magic.py | 145 ++++++++++++++++++------------------ 2 files changed, 74 insertions(+), 77 deletions(-) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index ce14b19c5..02ff14191 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -159,10 +159,6 @@ def unionUse(expression, unpack=True, dump=False): _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) - if expressionFieldsList and len(expressionFieldsList) > 1 and " ORDER BY " in expression.upper(): - # No need for it in multicolumn dumps (one row is retrieved per request) and just slowing down on large table dumps - expression = expression[:expression.upper().rindex(" ORDER BY ")] - # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time @@ -305,7 +301,7 @@ def unionUse(expression, unpack=True, dump=False): kb.suppressResumeInfo = False if not value and not abortedFlag: - expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) # full union doesn't play well with ORDER BY + expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) # full union does not play well with ORDER BY value = _oneShotUnionUse(expression, unpack) duration = calculateDeltaSeconds(start) diff --git a/thirdparty/magic/magic.py b/thirdparty/magic/magic.py index 9df32a108..08a4e0633 100644 --- a/thirdparty/magic/magic.py +++ b/thirdparty/magic/magic.py @@ -106,99 +106,100 @@ def from_buffer(buffer, mime=False): +try: + libmagic = None + # Let's try to find magic or magic1 + dll = ctypes.util.find_library('magic') or ctypes.util.find_library('magic1') -libmagic = None -# Let's try to find magic or magic1 -dll = ctypes.util.find_library('magic') or ctypes.util.find_library('magic1') + # This is necessary because find_library returns None if it doesn't find the library + if dll: + libmagic = ctypes.CDLL(dll) -# This is necessary because find_library returns None if it doesn't find the library -if dll: - libmagic = ctypes.CDLL(dll) + if not libmagic or not libmagic._name: + import sys + platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib', + '/usr/local/lib/libmagic.dylib', + '/usr/local/Cellar/libmagic/5.10/lib/libmagic.dylib'], + 'win32': ['magic1.dll']} + for dll in platform_to_lib.get(sys.platform, []): + try: + libmagic = ctypes.CDLL(dll) + except OSError: + pass -if not libmagic or not libmagic._name: - import sys - platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib', - '/usr/local/lib/libmagic.dylib', - '/usr/local/Cellar/libmagic/5.10/lib/libmagic.dylib'], - 'win32': ['magic1.dll']} - for dll in platform_to_lib.get(sys.platform, []): - try: - libmagic = ctypes.CDLL(dll) - except OSError: - pass + if not libmagic or not libmagic._name: + # It is better to raise an ImportError since we are importing magic module + raise ImportError('failed to find libmagic. Check your installation') -if not libmagic or not libmagic._name: - # It is better to raise an ImportError since we are importing magic module - raise ImportError('failed to find libmagic. Check your installation') + magic_t = ctypes.c_void_p -magic_t = ctypes.c_void_p + def errorcheck(result, func, args): + err = magic_error(args[0]) + if err is not None: + raise MagicException(err) + else: + return result -def errorcheck(result, func, args): - err = magic_error(args[0]) - if err is not None: - raise MagicException(err) - else: - return result + def coerce_filename(filename): + if filename is None: + return None + return filename.encode(sys.getfilesystemencoding()) -def coerce_filename(filename): - if filename is None: - return None - return filename.encode(sys.getfilesystemencoding()) + magic_open = libmagic.magic_open + magic_open.restype = magic_t + magic_open.argtypes = [c_int] -magic_open = libmagic.magic_open -magic_open.restype = magic_t -magic_open.argtypes = [c_int] + magic_close = libmagic.magic_close + magic_close.restype = None + magic_close.argtypes = [magic_t] -magic_close = libmagic.magic_close -magic_close.restype = None -magic_close.argtypes = [magic_t] + magic_error = libmagic.magic_error + magic_error.restype = c_char_p + magic_error.argtypes = [magic_t] -magic_error = libmagic.magic_error -magic_error.restype = c_char_p -magic_error.argtypes = [magic_t] + magic_errno = libmagic.magic_errno + magic_errno.restype = c_int + magic_errno.argtypes = [magic_t] -magic_errno = libmagic.magic_errno -magic_errno.restype = c_int -magic_errno.argtypes = [magic_t] + _magic_file = libmagic.magic_file + _magic_file.restype = c_char_p + _magic_file.argtypes = [magic_t, c_char_p] + _magic_file.errcheck = errorcheck -_magic_file = libmagic.magic_file -_magic_file.restype = c_char_p -_magic_file.argtypes = [magic_t, c_char_p] -_magic_file.errcheck = errorcheck + def magic_file(cookie, filename): + return _magic_file(cookie, coerce_filename(filename)) -def magic_file(cookie, filename): - return _magic_file(cookie, coerce_filename(filename)) - -_magic_buffer = libmagic.magic_buffer -_magic_buffer.restype = c_char_p -_magic_buffer.argtypes = [magic_t, c_void_p, c_size_t] -_magic_buffer.errcheck = errorcheck + _magic_buffer = libmagic.magic_buffer + _magic_buffer.restype = c_char_p + _magic_buffer.argtypes = [magic_t, c_void_p, c_size_t] + _magic_buffer.errcheck = errorcheck -def magic_buffer(cookie, buf): - return _magic_buffer(cookie, buf, len(buf)) + def magic_buffer(cookie, buf): + return _magic_buffer(cookie, buf, len(buf)) -_magic_load = libmagic.magic_load -_magic_load.restype = c_int -_magic_load.argtypes = [magic_t, c_char_p] -_magic_load.errcheck = errorcheck + _magic_load = libmagic.magic_load + _magic_load.restype = c_int + _magic_load.argtypes = [magic_t, c_char_p] + _magic_load.errcheck = errorcheck -def magic_load(cookie, filename): - return _magic_load(cookie, coerce_filename(filename)) + def magic_load(cookie, filename): + return _magic_load(cookie, coerce_filename(filename)) -magic_setflags = libmagic.magic_setflags -magic_setflags.restype = c_int -magic_setflags.argtypes = [magic_t, c_int] + magic_setflags = libmagic.magic_setflags + magic_setflags.restype = c_int + magic_setflags.argtypes = [magic_t, c_int] -magic_check = libmagic.magic_check -magic_check.restype = c_int -magic_check.argtypes = [magic_t, c_char_p] - -magic_compile = libmagic.magic_compile -magic_compile.restype = c_int -magic_compile.argtypes = [magic_t, c_char_p] + magic_check = libmagic.magic_check + magic_check.restype = c_int + magic_check.argtypes = [magic_t, c_char_p] + magic_compile = libmagic.magic_compile + magic_compile.restype = c_int + magic_compile.argtypes = [magic_t, c_char_p] +except ImportError: + pass MAGIC_NONE = 0x000000 # No flags From 282aeb734fe5d74db492a4898075d98e3af17e58 Mon Sep 17 00:00:00 2001 From: Bernardo Damele Date: Wed, 19 Dec 2012 13:21:16 +0000 Subject: [PATCH 9/9] ORDER BY does not play well with UNION query SQLi (related to issue #313) --- lib/core/common.py | 3 +++ lib/techniques/union/use.py | 9 ++++++++- xml/livetests.xml | 39 ++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index fe23b0f18..56a838875 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -661,6 +661,9 @@ def filePathToString(filePath): return strRepl +def singleTimeDebugMessage(message): + singleTimeLogMessage(message, logging.DEBUG) + def singleTimeWarnMessage(message): singleTimeLogMessage(message, logging.WARN) diff --git a/lib/techniques/union/use.py b/lib/techniques/union/use.py index 02ff14191..85e810fd1 100644 --- a/lib/techniques/union/use.py +++ b/lib/techniques/union/use.py @@ -29,6 +29,7 @@ from lib.core.common import isNumPosStrValue from lib.core.common import listToStrValue from lib.core.common import parseUnionPage from lib.core.common import removeReflectiveValues +from lib.core.common import singleTimeDebugMessage from lib.core.common import singleTimeWarnMessage from lib.core.common import wasLastRequestDBMSError from lib.core.convert import htmlunescape @@ -159,6 +160,13 @@ def unionUse(expression, unpack=True, dump=False): _, _, _, _, _, expressionFieldsList, expressionFields, _ = agent.getFields(origExpr) + if expressionFieldsList and len(expressionFieldsList) > 1 and "ORDER BY" in expression.upper(): + # Removed ORDER BY clause because UNION does not play well with it + expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) + debugMsg = "stripping ORDER BY clause from statement because " + debugMsg += "it does not play well with UNION query SQL injection" + singleTimeDebugMessage(debugMsg) + # We have to check if the SQL query might return multiple entries # if the technique is partial UNION query and in such case forge the # SQL limiting the query output one entry at a time @@ -301,7 +309,6 @@ def unionUse(expression, unpack=True, dump=False): kb.suppressResumeInfo = False if not value and not abortedFlag: - expression = re.sub("\s*ORDER BY\s+[\w,]+", "", expression, re.I) # full union does not play well with ORDER BY value = _oneShotUnionUse(expression, unpack) duration = calculateDeltaSeconds(start) diff --git a/xml/livetests.xml b/xml/livetests.xml index 2617b5a06..2ffa01e19 100644 --- a/xml/livetests.xml +++ b/xml/livetests.xml @@ -283,11 +283,14 @@ - + + + @@ -632,5 +635,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +