<errorregexp="valid MySQL result"/><!-- 匹配无效的MySQL查询结果的错误信息,用于识别查询结果无效的情况 -->
<errorregexp="check the manual that (corresponds to|fits) your MySQL server version"/><!-- 匹配与MySQL服务器版本不匹配的错误信息,提示用户检查手册 -->
<dbmsvalue="Oracle">
<errorregexp="check the manual that (corresponds to|fits) your MariaDB server version"fork="MariaDB"/><!-- 匹配与MariaDB服务器版本不匹配的错误信息,fork属性指定为MariaDB -->
<errorregexp="\bORA-\d{5}"/>
<errorregexp="check the manual that (corresponds to|fits) your Drizzle server version"fork="Drizzle"/><!-- 匹配与Drizzle服务器版本不匹配的错误信息,fork属性指定为Drizzle -->
<errorregexp="Oracle error"/>
<errorregexp="Unknown column '[^ ]+' in 'field list'"/><!-- 匹配未知列名的错误信息,用于识别字段列表中不存在的列 -->
@ -160,33 +142,27 @@ class ReqHandler(BaseHTTPRequestHandler):
code,output=OK,""
code,output=OK,""
try:
try:
# 处理回显参数
ifself.params.get("echo",""):
ifself.params.get("echo",""):
output+="%s<br>"%self.params["echo"]
output+="%s<br>"%self.params["echo"]
ifself.params.get("reflect",""):
ifself.params.get("reflect",""):
output+="%s<br>"%self.params.get("id")
output+="%s<br>"%self.params.get("id")
with_lock:# 使用锁来确保线程安全
with_lock:
if"query"inself.params:
if"query"inself.params:
# 执行任意 SQL 查询
_cursor.execute(self.params["query"])
_cursor.execute(self.params["query"])
elif"id"inself.params:
elif"id"inself.params:
# 通过 ID 查询用户
if"base64"inself.params:
if"base64"inself.params:
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1"%
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1"%base64.b64decode("%s==="%self.params["id"],altchars=self.params.get("altchars")).decode())
base64.b64decode("%s==="%self.params["id"],
altchars=self.params.get("altchars")).decode())
else:
else:
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1"%self.params["id"])
_cursor.execute("SELECT * FROM users WHERE id=%s LIMIT 0, 1"%self.params["id"])
results=_cursor.fetchall()# 获取查询结果
results=_cursor.fetchall()
output+="<b>SQL results:</b><br>\n"
output+="<b>SQL results:</b><br>\n"
# 根据查询结果决定响应的状态码和内容
ifself.params.get("code",""):
ifself.params.get("code",""):
ifnotresults:
ifnotresults:
code=INTERNAL_SERVER_ERROR# 出错
code=INTERNAL_SERVER_ERROR
else:
else:
ifresults:
ifresults:
output+="<table border=\"1\">\n"
output+="<table border=\"1\">\n"
@ -199,16 +175,15 @@ class ReqHandler(BaseHTTPRequestHandler):
checkBooleanExpression("%d%s%d"%(randInt1,INFERENCE_EQUALS_CHAR,randInt2))# just in case if DBMS hasn't properly recovered from previous delayed request
checkBooleanExpression("%d%s%d"%(randInt1,INFERENCE_EQUALS_CHAR,randInt2))# just in case if DBMS hasn't properly recovered from previous delayed request
# 如果满足布尔表达式,则retVal为False,跳出循环
ifcheckBooleanExpression("%d%s%d"%(randInt1,INFERENCE_EQUALS_CHAR,randInt3)):# this must not be evaluated to True
ifcheckBooleanExpression("%d%s%d"%(randInt1,INFERENCE_EQUALS_CHAR,randInt3)):# this must not be evaluated to True
retVal=False
retVal=False
break
break
# 如果满足布尔表达式,则retVal为False,跳出循环
elifcheckBooleanExpression("%d%s%d"%(randInt3,INFERENCE_EQUALS_CHAR,randInt2)):# this must not be evaluated to True
elifcheckBooleanExpression("%d%s%d"%(randInt3,INFERENCE_EQUALS_CHAR,randInt2)):# this must not be evaluated to True
retVal=False
retVal=False
break
break
# 如果不满足布尔表达式,则retVal为False,跳出循环
elifnotcheckBooleanExpression("%d%s%d"%(randInt2,INFERENCE_EQUALS_CHAR,randInt2)):# this must be evaluated to True
elifnotcheckBooleanExpression("%d%s%d"%(randInt2,INFERENCE_EQUALS_CHAR,randInt2)):# this must be evaluated to True
retVal=False
retVal=False
break
break
# 如果满足布尔表达式,则retVal为False,跳出循环
elifcheckBooleanExpression("%d%d"%(randInt3,randInt2)):# this must not be evaluated to True (invalid statement)
elifcheckBooleanExpression("%d%d"%(randInt3,randInt2)):# this must not be evaluated to True (invalid statement)
retVal=False
retVal=False
break
break
# 如果retVal为False,则记录警告信息
ifnotretVal:
ifnotretVal:
warnMsg="false positive or unexploitable injection point detected"
warnMsg="false positive or unexploitable injection point detected"
infoMsg="heuristic (XSS) test shows that %sparameter '%s' might be vulnerable to cross-site scripting (XSS) attacks"%("%s"%paramTypeifparamType!=parameterelse"",parameter)
infoMsg="heuristic (XSS) test shows that %sparameter '%s' might be vulnerable to cross-site scripting (XSS) attacks"%("%s"%paramTypeifparamType!=parameterelse"",parameter)
# 输出信息,表示参数可能存在XSS攻击
logger.info(infoMsg)
logger.info(infoMsg)
ifconf.beep:
ifconf.beep:
# 如果配置文件中设置了beep,则执行beep函数
beep()
beep()
formatchinre.finditer(FI_ERROR_REGEX,pageor""):
formatchinre.finditer(FI_ERROR_REGEX,pageor""):
# 在page中查找FI_ERROR_REGEX,如果找到,则执行以下代码
ifrandStr1.lower()inmatch.group(0).lower():
ifrandStr1.lower()inmatch.group(0).lower():
# 如果randStr1的小写字母在match.group(0)的小写字母中,则执行以下代码
infoMsg="heuristic (FI) test shows that %sparameter '%s' might be vulnerable to file inclusion (FI) attacks"%("%s"%paramTypeifparamType!=parameterelse"",parameter)
infoMsg="heuristic (FI) test shows that %sparameter '%s' might be vulnerable to file inclusion (FI) attacks"%("%s"%paramTypeifparamType!=parameterelse"",parameter)
@ -97,7 +92,6 @@ class DBMS_DIRECTORY_NAME(object):
VIRTUOSO="virtuoso"
VIRTUOSO="virtuoso"
classFORK(object):
classFORK(object):
# 定义分支数据库管理系统常量
MARIADB="MariaDB"
MARIADB="MariaDB"
MEMSQL="MemSQL"
MEMSQL="MemSQL"
PERCONA="Percona"
PERCONA="Percona"
@ -115,18 +109,15 @@ class FORK(object):
OPENGAUSS="OpenGauss"
OPENGAUSS="OpenGauss"
classCUSTOM_LOGGING(object):
classCUSTOM_LOGGING(object):
# 定义自定义日志常量
PAYLOAD=9
PAYLOAD=9
TRAFFIC_OUT=8
TRAFFIC_OUT=8
TRAFFIC_IN=7
TRAFFIC_IN=7
classOS(object):
classOS(object):
# 定义操作系统常量
LINUX="Linux"
LINUX="Linux"
WINDOWS="Windows"
WINDOWS="Windows"
classPLACE(object):
classPLACE(object):
# 定义位置常量
GET="GET"
GET="GET"
POST="POST"
POST="POST"
URI="URI"
URI="URI"
@ -138,7 +129,6 @@ class PLACE(object):
CUSTOM_HEADER="(custom) HEADER"
CUSTOM_HEADER="(custom) HEADER"
classPOST_HINT(object):
classPOST_HINT(object):
# 定义POST提示常量
SOAP="SOAP"
SOAP="SOAP"
JSON="JSON"
JSON="JSON"
JSON_LIKE="JSON-like"
JSON_LIKE="JSON-like"
@ -147,7 +137,6 @@ class POST_HINT(object):
ARRAY_LIKE="Array-like"
ARRAY_LIKE="Array-like"
classHTTPMETHOD(object):
classHTTPMETHOD(object):
# 定义HTTP方法常量
GET="GET"
GET="GET"
POST="POST"
POST="POST"
HEAD="HEAD"
HEAD="HEAD"
@ -159,18 +148,15 @@ class HTTPMETHOD(object):
PATCH="PATCH"
PATCH="PATCH"
classNULLCONNECTION(object):
classNULLCONNECTION(object):
# 定义空连接常量
HEAD="HEAD"
HEAD="HEAD"
RANGE="Range"
RANGE="Range"
SKIP_READ="skip-read"
SKIP_READ="skip-read"
classREFLECTIVE_COUNTER(object):
classREFLECTIVE_COUNTER(object):
# 定义反射计数器常量
MISS="MISS"
MISS="MISS"
HIT="HIT"
HIT="HIT"
classCHARSET_TYPE(object):
classCHARSET_TYPE(object):
# 定义字符集类型常量
BINARY=1
BINARY=1
DIGITS=2
DIGITS=2
HEXADECIMAL=3
HEXADECIMAL=3
@ -178,13 +164,11 @@ class CHARSET_TYPE(object):
ALPHANUM=5
ALPHANUM=5
classHEURISTIC_TEST(object):
classHEURISTIC_TEST(object):
# 定义启发式测试常量
CASTED=1
CASTED=1
NEGATIVE=2
NEGATIVE=2
POSITIVE=3
POSITIVE=3
classHASH(object):
classHASH(object):
# 定义哈希常量
MYSQL=r'(?i)\A\*[0-9a-f]{40}\Z'
MYSQL=r'(?i)\A\*[0-9a-f]{40}\Z'
MYSQL_OLD=r'(?i)\A(?![0-9]+\Z)[0-9a-f]{16}\Z'
MYSQL_OLD=r'(?i)\A(?![0-9]+\Z)[0-9a-f]{16}\Z'
POSTGRES=r'(?i)\Amd5[0-9a-f]{32}\Z'
POSTGRES=r'(?i)\Amd5[0-9a-f]{32}\Z'
@ -232,26 +216,22 @@ class MOBILES(object):
XIAOMI=("Xiaomi Mi 8 Pro","Mozilla/5.0 (Linux; Android 9; MI 8 Pro Build/PKQ1.180729.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.66 Mobile Safari/537.36")
XIAOMI=("Xiaomi Mi 8 Pro","Mozilla/5.0 (Linux; Android 9; MI 8 Pro Build/PKQ1.180729.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.66 Mobile Safari/537.36")
("--dummy",("all tested parameters do not appear to be injectable","does not seem to be injectable","there is not at least one","~might be injectable")),
("-u \"<url>&id2=1\" -p id2 -v 5 --flush-session --level=5 --text-only --test-filter=\"AND boolean-based blind - WHERE or HAVING clause (MySQL comment)\"",("~1AND",)),
("-l <log> --flush-session --keep-alive --skip-waf -vvvvv --technique=U --union-from=users --banner --parse-errors",("banner: '3.","ORDER BY term out of range","~xp_cmdshell","Connection: keep-alive")),
# When user provides DBMS credentials (with --dbms-cred), the
# command standard output is redirected to a temporary file
# The file needs to be copied to the support table,
# 'sqlmapoutput'
ifconf.dbmsCred:
ifconf.dbmsCred:
inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')"%(self.cmdTblName,self.tmpFile,randomStr(10),randomStr(10)))
inject.goStacked("BULK INSERT %s FROM '%s' WITH (CODEPAGE='RAW', FIELDTERMINATOR='%s', ROWTERMINATOR='%s')"%(self.cmdTblName,self.tmpFile,randomStr(10),randomStr(10)))
self.delRemoteFile(self.tmpFile)
self.delRemoteFile(self.tmpFile)
query="SELECT %s FROM %s ORDER BY id"%(self.tblField,self.cmdTblName)
query="SELECT %s FROM %s ORDER BY id"%(self.tblField,self.cmdTblName)
count=inject.getValue("SELECT COUNT(id) FROM %s"%self.cmdTblName,resumeValue=False,union=False,error=False,expected=EXPECTED.INT,charsetType=CHARSET_TYPE.DIGITS)
count=inject.getValue("SELECT COUNT(id) FROM %s"%self.cmdTblName,resumeValue=False,union=False,error=False,expected=EXPECTED.INT,charsetType=CHARSET_TYPE.DIGITS)
# We have to check if the SQL query might return multiple entries
# and in such case forge the SQL limiting the query output one
# entry at a time
# NOTE: we assume that only queries that get data from a table can
# return multiple entries
if(dumpand(conf.limitStartorconf.limitStop))or(" FROM "inexpression.upper()and((Backend.getIdentifiedDbms()notinFROM_DUMMY_TABLE)or(Backend.getIdentifiedDbms()inFROM_DUMMY_TABLEandnotexpression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()])))and("(CASE"notinexpression.upper()or("(CASE"inexpression.upper()and"WHEN use"inexpression)))andnotre.search(SQL_SCALAR_REGEX,expression,re.I):
if(dumpand(conf.limitStartorconf.limitStop))or(" FROM "inexpression.upper()and((Backend.getIdentifiedDbms()notinFROM_DUMMY_TABLE)or(Backend.getIdentifiedDbms()inFROM_DUMMY_TABLEandnotexpression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()])))and("(CASE"notinexpression.upper()or("(CASE"inexpression.upper()and"WHEN use"inexpression)))andnotre.search(SQL_SCALAR_REGEX,expression,re.I):
returnnotany(re.search(_,pageor"",re.I)andnotre.search(_,kb.pageTemplateor"",re.I)for_in("(warning|error):","order (by|clause)","unknown column","failed"))andnotkb.heavilyDynamicandcomparison(page,headers,code)orre.search(r"data types cannot be compared or sorted",pageor"",re.I)isnotNone
returnnotany(re.search(_,pageor"",re.I)andnotre.search(_,kb.pageTemplateor"",re.I)for_in("(warning|error):","order (by|clause)","unknown column","failed"))andnotkb.heavilyDynamicandcomparison(page,headers,code)orre.search(r"data types cannot be compared or sorted",pageor"",re.I)isnotNone
fromTable=" FROM (%s) AS %s"%(" UNION ".join("SELECT %d%s%s"%(_,FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(),"")," AS %s"%randomStr()if_==0else"")for_inxrange(LIMITED_ROWS_TEST_NUMBER)),randomStr())
fromTable=" FROM (%s) AS %s"%(" UNION ".join("SELECT %d%s%s"%(_,FROM_DUMMY_TABLE.get(Backend.getIdentifiedDbms(),"")," AS %s"%randomStr()if_==0else"")for_inxrange(LIMITED_ROWS_TEST_NUMBER)),randomStr())
elifkb.ncharandre.search(r" AS N(CHAR|VARCHAR)",agent.nullAndCastField(expression)):
elifkb.ncharandre.search(r" AS N(CHAR|VARCHAR)",agent.nullAndCastField(expression)):
debugMsg="关闭NATIONAL CHARACTER转换"
debugMsg="turning off NATIONAL CHARACTER casting"# NOTE: in some cases there are "known" incompatibilities between original columns and NCHAR (e.g. http://testphp.vulnweb.com/artists.php?artist=1)
# We have to check if the SQL query might return multiple entries
# if the technique is partial UNION query and in such case forge the
# SQL limiting the query output one entry at a time
# NOTE: we assume that only queries that get data from a table can
# return multiple entries
ifvalueisNoneand(kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where==PAYLOAD.WHERE.NEGATIVEorkb.forcePartialUnionorconf.forcePartialor(dumpand(conf.limitStartorconf.limitStop))or"LIMIT "inexpression.upper())and" FROM "inexpression.upper()and((Backend.getIdentifiedDbms()notinFROM_DUMMY_TABLE)or(Backend.getIdentifiedDbms()inFROM_DUMMY_TABLEandnotexpression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()])))andnotre.search(SQL_SCALAR_REGEX,expression,re.I):
ifvalueisNoneand(kb.injection.data[PAYLOAD.TECHNIQUE.UNION].where==PAYLOAD.WHERE.NEGATIVEorkb.forcePartialUnionorconf.forcePartialor(dumpand(conf.limitStartorconf.limitStop))or"LIMIT "inexpression.upper())and" FROM "inexpression.upper()and((Backend.getIdentifiedDbms()notinFROM_DUMMY_TABLE)or(Backend.getIdentifiedDbms()inFROM_DUMMY_TABLEandnotexpression.upper().endswith(FROM_DUMMY_TABLE[Backend.getIdentifiedDbms()])))andnotre.search(SQL_SCALAR_REGEX,expression,re.I):