access_fingerprint

pull/18/head
Warmlight 3 months ago
parent 7033a8c210
commit bb4ebc7514

@ -7,94 +7,109 @@ See the file 'LICENSE' for copying permission
import re import re
from lib.core.common import Backend # 1. 从库中引入需要的模块和类
from lib.core.common import Format from lib.core.common import Backend # 后端数据库信息
from lib.core.common import getCurrentThreadData from lib.core.common import Format # 格式化输出
from lib.core.common import randomStr from lib.core.common import getCurrentThreadData # 获取当前线程的数据
from lib.core.common import wasLastResponseDBMSError from lib.core.common import randomStr # 生成随机字符串
from lib.core.data import conf from lib.core.common import wasLastResponseDBMSError # 判断最后响应是否包含数据库错误
from lib.core.data import kb from lib.core.data import conf # 全局配置信息
from lib.core.data import logger from lib.core.data import kb # 全局知识库
from lib.core.enums import DBMS from lib.core.data import logger # 日志记录器
from lib.core.session import setDbms from lib.core.enums import DBMS # 数据库类型枚举
from lib.core.settings import ACCESS_ALIASES from lib.core.session import setDbms # 设置当前数据库类型
from lib.core.settings import METADB_SUFFIX from lib.core.settings import ACCESS_ALIASES # ACCESS 数据库的别名
from lib.request import inject from lib.core.settings import METADB_SUFFIX # 元数据表后缀
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint from lib.request import inject # 注入相关函数
from plugins.generic.fingerprint import Fingerprint as GenericFingerprint # 通用指纹识别类
# 2. 定义一个类 Fingerprint继承自 GenericFingerprint
class Fingerprint(GenericFingerprint): class Fingerprint(GenericFingerprint):
# 3. 构造函数,初始化数据库类型
def __init__(self): def __init__(self):
GenericFingerprint.__init__(self, DBMS.ACCESS) GenericFingerprint.__init__(self, DBMS.ACCESS)
# 4. 私有方法,检查是否在沙盒中运行
def _sandBoxCheck(self): def _sandBoxCheck(self):
# Reference: http://milw0rm.com/papers/198 # 参考链接: http://milw0rm.com/papers/198
retVal = None retVal = None
table = None table = None
# 5. 根据 Access 版本设置需要查询的表名
if Backend.isVersionWithin(("97", "2000")): if Backend.isVersionWithin(("97", "2000")):
table = "MSysAccessObjects" table = "MSysAccessObjects"
elif Backend.isVersionWithin(("2002-2003", "2007")): elif Backend.isVersionWithin(("2002-2003", "2007")):
table = "MSysAccessStorage" table = "MSysAccessStorage"
# 6. 如果有对应的表名,则执行查询判断是否处于沙盒环境
if table is not None: if table is not None:
result = inject.checkBooleanExpression("EXISTS(SELECT CURDIR() FROM %s)" % table) result = inject.checkBooleanExpression("EXISTS(SELECT CURDIR() FROM %s)" % table)
retVal = "not sandboxed" if result else "sandboxed" retVal = "not sandboxed" if result else "sandboxed"
# 7. 返回检测结果
return retVal return retVal
# 8. 私有方法,检查系统表是否存在
def _sysTablesCheck(self): def _sysTablesCheck(self):
infoMsg = "executing system table(s) existence fingerprint" infoMsg = "executing system table(s) existence fingerprint"
logger.info(infoMsg) logger.info(infoMsg)
# Microsoft Access table reference updated on 01/2010 # 9. 定义不同版本 Access 需要检查的系统表
sysTables = { sysTables = {
"97": ("MSysModules2", "MSysAccessObjects"), "97": ("MSysModules2", "MSysAccessObjects"),
"2000": ("!MSysModules2", "MSysAccessObjects"), "2000": ("!MSysModules2", "MSysAccessObjects"),
"2002-2003": ("MSysAccessStorage", "!MSysNavPaneObjectIDs"), "2002-2003": ("MSysAccessStorage", "!MSysNavPaneObjectIDs"),
"2007": ("MSysAccessStorage", "MSysNavPaneObjectIDs"), "2007": ("MSysAccessStorage", "MSysNavPaneObjectIDs"),
} }
# MSysAccessXML 是不稳定的系统表,因为它并非总是存在
# MSysAccessXML is not a reliable system table because it doesn't always exist # 10. 遍历系统表,进行检查
# ("Access through Access", p6, should be "normally doesn't exist" instead of "is normally empty")
for version, tables in sysTables.items(): for version, tables in sysTables.items():
exist = True exist = True
for table in tables: for table in tables:
negate = False negate = False
# 11. 如果表名以 ! 开头,表示该表应该不存在
if table[0] == '!': if table[0] == '!':
negate = True negate = True
table = table[1:] table = table[1:]
# 12. 执行 SQL 查询,检查表是否存在
result = inject.checkBooleanExpression("EXISTS(SELECT * FROM %s WHERE [RANDNUM]=[RANDNUM])" % table) result = inject.checkBooleanExpression("EXISTS(SELECT * FROM %s WHERE [RANDNUM]=[RANDNUM])" % table)
if result is None: if result is None:
result = False result = False
# 13. 如果表不应该存在,则取反
if negate: if negate:
result = not result result = not result
# 14. 对所有表的结果进行与运算,只有都满足才能认为当前版本匹配
exist &= result exist &= result
if not exist: if not exist:
break break
# 15. 如果当前版本匹配,则返回版本号
if exist: if exist:
return version return version
# 16. 如果所有版本都不匹配,则返回 None
return None return None
# 17. 私有方法,获取数据库所在目录
def _getDatabaseDir(self): def _getDatabaseDir(self):
retVal = None retVal = None
infoMsg = "searching for database directory" infoMsg = "searching for database directory"
logger.info(infoMsg) logger.info(infoMsg)
# 18. 生成随机字符串
randStr = randomStr() randStr = randomStr()
# 19. 执行 SQL 查询,尝试触发错误,获取数据库目录
inject.checkBooleanExpression("EXISTS(SELECT * FROM %s.%s WHERE [RANDNUM]=[RANDNUM])" % (randStr, randStr)) inject.checkBooleanExpression("EXISTS(SELECT * FROM %s.%s WHERE [RANDNUM]=[RANDNUM])" % (randStr, randStr))
# 20. 如果最后响应包含数据库错误
if wasLastResponseDBMSError(): if wasLastResponseDBMSError():
threadData = getCurrentThreadData() threadData = getCurrentThreadData()
# 21. 从错误信息中提取数据库目录
match = re.search(r"Could not find file\s+'([^']+?)'", threadData.lastErrorPage[1]) match = re.search(r"Could not find file\s+'([^']+?)'", threadData.lastErrorPage[1])
if match: if match:
@ -102,92 +117,115 @@ class Fingerprint(GenericFingerprint):
if retVal.endswith('\\'): if retVal.endswith('\\'):
retVal = retVal[:-1] retVal = retVal[:-1]
# 22. 返回数据库目录
return retVal return retVal
# 23. 获取指纹信息
def getFingerprint(self): def getFingerprint(self):
value = "" value = ""
# 24. 获取 Web 服务器操作系统指纹
wsOsFp = Format.getOs("web server", kb.headersFp) wsOsFp = Format.getOs("web server", kb.headersFp)
# 25. 将 Web 服务器操作系统指纹添加到输出
if wsOsFp: if wsOsFp:
value += "%s\n" % wsOsFp value += "%s\
" % wsOsFp
# 26. 如果有数据库 Banner 信息
if kb.data.banner: if kb.data.banner:
# 27. 获取后端数据库操作系统指纹
dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp) dbmsOsFp = Format.getOs("back-end DBMS", kb.bannerFp)
# 28. 将后端数据库操作系统指纹添加到输出
if dbmsOsFp: if dbmsOsFp:
value += "%s\n" % dbmsOsFp value += "%s\
" % dbmsOsFp
value += "back-end DBMS: " value += "back-end DBMS: "
# 29. 如果不是详细指纹,则返回数据库类型
if not conf.extensiveFp: if not conf.extensiveFp:
value += DBMS.ACCESS value += DBMS.ACCESS
return value return value
# 30. 获取活动的指纹信息
actVer = Format.getDbms() + " (%s)" % (self._sandBoxCheck()) actVer = Format.getDbms() + " (%s)" % (self._sandBoxCheck())
blank = " " * 15 blank = " " * 15
value += "active fingerprint: %s" % actVer value += "active fingerprint: %s" % actVer
# 31. 如果有 Banner 解析指纹
if kb.bannerFp: if kb.bannerFp:
banVer = kb.bannerFp.get("dbmsVersion") banVer = kb.bannerFp.get("dbmsVersion")
# 32. 如果有 Banner 版本号
if banVer: if banVer:
# 33. 如果 Banner 信息包含 -log则表示启用日志
if re.search(r"-log$", kb.data.banner or ""): if re.search(r"-log$", kb.data.banner or ""):
banVer += ", logging enabled" banVer += ", logging enabled"
# 34. 格式化 Banner 版本号
banVer = Format.getDbms([banVer]) banVer = Format.getDbms([banVer])
value += "\n%sbanner parsing fingerprint: %s" % (blank, banVer) value += "\
%sbanner parsing fingerprint: %s" % (blank, banVer)
# 35. 获取 HTML 错误指纹
htmlErrorFp = Format.getErrorParsedDBMSes() htmlErrorFp = Format.getErrorParsedDBMSes()
# 36. 将 HTML 错误指纹添加到输出
if htmlErrorFp: if htmlErrorFp:
value += "\n%shtml error message fingerprint: %s" % (blank, htmlErrorFp) value += "\
%shtml error message fingerprint: %s" % (blank, htmlErrorFp)
value += "\ndatabase directory: '%s'" % self._getDatabaseDir() # 37. 获取数据库目录并添加到输出
value += "\
database directory: '%s'" % self._getDatabaseDir()
# 38. 返回完整的指纹信息
return value return value
# 39. 检查数据库类型是否为 Access
def checkDbms(self): def checkDbms(self):
# 40. 如果不是详细指纹,且当前数据库类型属于 Access 别名,则设置数据库类型为 Access 并返回 True
if not conf.extensiveFp and Backend.isDbmsWithin(ACCESS_ALIASES): if not conf.extensiveFp and Backend.isDbmsWithin(ACCESS_ALIASES):
setDbms(DBMS.ACCESS) setDbms(DBMS.ACCESS)
return True return True
# 41. 输出正在测试的数据库类型
infoMsg = "testing %s" % DBMS.ACCESS infoMsg = "testing %s" % DBMS.ACCESS
logger.info(infoMsg) logger.info(infoMsg)
# 42. 执行 SQL 查询,检查数据库类型是否为 Access
result = inject.checkBooleanExpression("VAL(CVAR(1))=1") result = inject.checkBooleanExpression("VAL(CVAR(1))=1")
# 43. 如果查询成功
if result: if result:
infoMsg = "confirming %s" % DBMS.ACCESS infoMsg = "confirming %s" % DBMS.ACCESS
logger.info(infoMsg) logger.info(infoMsg)
# 44. 执行 SQL 查询,再次确认数据库类型是否为 Access
result = inject.checkBooleanExpression("IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0") result = inject.checkBooleanExpression("IIF(ATN(2)>0,1,0) BETWEEN 2 AND 0")
# 45. 如果再次确认失败,则输出警告信息,并返回 False
if not result: if not result:
warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS
logger.warning(warnMsg) logger.warning(warnMsg)
return False return False
# 46. 设置数据库类型为 Access
setDbms(DBMS.ACCESS) setDbms(DBMS.ACCESS)
# 47. 如果不是详细指纹,则返回 True
if not conf.extensiveFp: if not conf.extensiveFp:
return True return True
# 48. 输出正在进行详细指纹识别
infoMsg = "actively fingerprinting %s" % DBMS.ACCESS infoMsg = "actively fingerprinting %s" % DBMS.ACCESS
logger.info(infoMsg) logger.info(infoMsg)
# 49. 执行系统表检查,获取 Access 版本
version = self._sysTablesCheck() version = self._sysTablesCheck()
# 50. 如果获取到版本信息,则设置数据库版本
if version is not None: if version is not None:
Backend.setVersion(version) Backend.setVersion(version)
# 51. 返回 True
return True return True
# 52. 如果第一次查询失败,则输出警告信息,并返回 False
else: else:
warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS warnMsg = "the back-end DBMS is not %s" % DBMS.ACCESS
logger.warning(warnMsg) logger.warning(warnMsg)
return False return False
# 53. 强制枚举数据库类型
def forceDbmsEnum(self): def forceDbmsEnum(self):
# 54. 设置数据库名称
conf.db = ("%s%s" % (DBMS.ACCESS, METADB_SUFFIX)).replace(' ', '_') conf.db = ("%s%s" % (DBMS.ACCESS, METADB_SUFFIX)).replace(' ', '_')
Loading…
Cancel
Save