一切都像是现实,一切又都像是梦境,他的梦和现实像是交融那样拆解不开。
在扫描器开发过程中,对传入的网址进行waf检测识别是很重要的,参考sqlmap源码后,直接移植其中的waf识别功能。
- 优点:直接移植,简单方便
- 缺点:sqlmap中大多数waf都是国外的
- 补充: 移植代码后,可拓展性非常高,阅读完此文小学生坐在马桶上都会
代码阅读
在sqlmap的waf目录下,有45个py文件,除了一个初始化文件其他的都是waf检测插件,随便打开几个看看。
分析代码
可以发现所有的函数都传入一个值get_page,然后定义retval为假。
上面这两个比较好理解,即传入一个网页,获取这个网页的内容和头部信息,然后retval这个值是用作判断是否存在这个waf,如果retval为真就说明存在此waf,如何才能让retval为真呢?自然是在网页的内容和头部信息中检测了,检测确认存在该waf的判别方式。
移植思路
在sqlmap中检测waf的方式是传入一个网址,获取网址内容与头部信息,然后检测是否存在该waf的特征值,如果存在,就让retval为真并且返回这个值。因为一个waf的检测方法有好几种,比如在网页中匹配特征码,或者在网页的头部信息中匹配特征码,那么对应的waf字典数据结构应该是这样的。
{'360':[
'retval = re.search(r"wangzhan\.360\.cn", headers_get, re.I)',
'retval = "/wzws-waf-cgi/" in (page_get)'
],
'airlock':[
'retval = re.search(r"\AAL[_-]?(SESS|LB)=",headers_get, re.I)'
],
'anquanbao':[
'retval = re.search(r"MISS", headers_get, re.I)',
'retval = "/aqb_cc/error/" in (page_get)'
],
'armor':[
'retval = "This request has been blocked by website protection from Armor" in (page_get)'
]}
即在字典中,waf的名字是键,对应的检测方法为值,并且把检测方法的结果赋值给retval,如果检测waf存在,那么retval就为真。
完成代码
其实看完waf检测的字典就清楚我的思路是什么,循环迭代键值,如果返回的值(retval)为真,就说明存在改waf,这个时候在返回字典的键也就是waf名字。
其中headers_get是传入网页的头部信息,page_get是传入网页的内容。
正常的页面中一般不可能出现waf关键词的,但是让页面报错的话,就能检测出waf的关键词,这就好比打开一个网站,随便输入一些错误的字符串,在返回的body或者headers会反馈waf信息。
提及一下稍微有一个小知识点,python的exec与eval,都是把字符串当代码执行,但是前者可以进行一些深度的运算,比如计算数值加减,正则匹配等等,后者只能进行打印,即前者的权限比较大,什么都可以执行,后者只能执行一些普通的操作。
详细代码如下,如果要套进扫描器的话。可以把代码封装在一个函数里面,只接受一个参数(正常的网址)即可。
在poc-T找到部分waf的相关返回结果,对代码重新整理一下。
# -*- coding: utf-8 -*-
# @Author : Langzi
# @Blog: [url]www.langzi.fun[/url]
# @File: dic.py
# @Software: PyCharm
import sys
import requests
import re
import time
def scan_waf(uul):
url = uul+'/langzi52zh' if uul.startswith('http') else 'http://' + uul + '/langzi52zh'
try:
r = requests.get(url=url,timeout=5)
page_get = str(r.content)
headers_get = str(r.headers)
except:
print unicode('访问目标网址失败','utf-8')
time.sleep(10)
reload(sys)
sys.setdefaultencoding('utf-8')
waf_dic ={'360':[
'retval = re.search(r"wangzhan\.360\.cn", headers_get, re.I)',
'retval = "/wzws-waf-cgi/" in (page_get)'
],
'airlock':[
'retval = re.search(r"\AAL[_-]?(SESS|LB)=",headers_get, re.I)'
],
'anquanbao':[
'retval = re.search(r"MISS", headers_get, re.I)',
'retval = "/aqb_cc/error/" in (page_get)'
],
'armor':[
'retval = "This request has been blocked by website protection from Armor" in (page_get)'
],
'aws':[
'retval = re.search(r"\bAWS", headers_get,re.I)'
],
'baidu':[
'retval = re.search(r"fhl", headers_get, re.I)',
'retval = re.search(r"yunjiasu-nginx", headers_get,re.I)'
],
'barracuda':[
'retval = re.search(r"\Abarra_counter_session=",headers_get, re.I)',
'retval = re.search(r"(\A|\b)barracuda_",headers_get, re.I)'
],
'bigip':[
'retval = re.search(r"\ATS\w{4,}=",headers_get, re.I)',
'retval = re.search(r"BigIP|BIGipServer",headers_get, re.I)',
'retval = re.search(r"BigIP|BIGipServer", headers_get,re.I)',
'retval = re.search(r"\AF5\Z", headers_get,re.I)'
],
'binarysec':[
'retval = re.search(r"BinarySec", headers_get,re.I)'
],
'blockdos':[
'retval = re.search(r"BlockDos\.net", headers_get,re.I)'
],
'ciscoacexml':[
'retval = re.search(r"ACE XML Gateway", headers_get,re.I)'
],
'cloudflare':[
'retval = re.search(r"cloudflare-nginx", headers_get,re.I)',
'retval = re.search(r"\A__cfduid=",headers_get, re.I)',
'retval = re.search(r"CloudFlare Ray ID:|var CloudFlare=", page_get)'
],
'cloudfront':[
'retval = re.search(r"cloudfront", headers_get,re.I)',
'retval = re.search(r"cloudfront", headers_get,re.I)'
],
'comodo':[
'retval = re.search(r"Protected by COMODO WAF", headers_get,re.I)'
],
'datapower':[
'retval = re.search(r"\A(OK|FAIL)", headers_get, re.I)'
],
'denyall':[
'retval = re.search(r"\Asessioncookie=",headers_get, re.I)',
'retval = re.search(r"\ACondition Intercepted", page_get, re.I)'
],
'dotdefender':[
'retval = "dotDefender Blocked Your Request" in (page_get)'
],
'edgecast':[
'retval = re.search(r"\AECDF", headers_get,re.I)'
],
'expressionengine':[
'retval = "Invalid GET Data" in (page_get)'
],
'fortiweb':[
'retval = re.search(r"\AFORTIWAFSID=",headers_get, re.I)'
],
'hyperguard':[
'retval = re.search(r"\AODSESSION=",headers_get, re.I)'
],
'incapsula':[
'retval = re.search(r"incap_ses|visid_incap",headers_get, re.I)',
'retval = re.search(r"Incapsula", headers_get, re.I)',
'retval = "Incapsula incident ID" in (page_get)'
],
'isaserver':[
'retval = "The server denied the specified Uniform Resource Locator (URL). Contact the server administrator." in (page_get)',
'retval = "The ISA Server denied the specified Uniform Resource Locator (URL)" in (page_get)'
],
'jiasule':[
'retval = re.search(r"jiasule-WAF", headers_get,re.I)',
'retval = re.search(r"__jsluid=",headers_get, re.I)',
'retval = re.search(r"jsl_tracking",headers_get, re.I)',
'retval = re.search(r"static\.jiasule\.com/static/js/http_error\.js", page_get, re.I)',
'retval = "notice-jiasule" in (page_get)'
],
'kona':[
'retval = re.search(r"Reference #[0-9a-f.]+", page_get, re.I)',
'retval = re.search(r"AkamaiGHost", headers_get,re.I)'
],
'modsecurity':[
'retval = re.search(r"Mod_Security|NOYB", headers_get,re.I)',
'retval = "This error was generated by Mod_Security" in (page_get)'
],
'netcontinuum':[
'retval = re.search(r"\ANCI__SessionId=",headers_get, re.I)'
],
'netscaler':[
'retval = re.search(r"\Aclose", headers_get,re.I)',
'retval = re.search(r"\A(ns_af=|citrix_ns_id|NSC_)",headers_get, re.I)',
'retval = re.search(r"\ANS-CACHE",headers_get,re.I)'
],
'newdefend':[
'retval = re.search(r"newdefend", headers_get,re.I)'
],
'nsfocus':[
'retval = re.search(r"NSFocus", headers_get,re.I)'
],
'paloalto':[
'retval = re.search(r"Access[^<]+has been blocked in accordance with company policy", page_get, re.I)'
],
'profense':[
'retval = re.search(r"\APLBSID=",headers_get, re.I)',
'retval = re.search(r"Profense", headers_get,re.I)'
],
'radware':[
'retval = re.search(r"Unauthorized Activity Has Been Detected.+Case Number:", page_get, re.I | re.S)'
],
'requestvalidationmode':[
'retval = "ASP.NET has detected data in the request that is potentially dangerous" in (page_get)',
'retval = "Request Validation has detected a potentially dangerous client input value" in (page_get)'
],
'safe3':[
'retval = re.search(r"Safe3WAF",headers_get, re.I)',
'retval = re.search(r"Safe3 Web Firewall", headers_get,re.I)'
],
'safedog':[
'retval = re.search(r"WAF/2\.0",headers_get, re.I)',
'retval = re.search(r"Safedog", headers_get,re.I)',
'retval = re.search(r"safedog",headers_get, re.I)'
],
'secureiis':[
'retval = re.search(r"SecureIIS[^<]+Web Server Protection", page_get)',
'retval = "http://www.eeye.com/SecureIIS/" in (page_get)',
'retval = re.search(r"\?subject=[^>]*SecureIIS Error", page_get)'
],
'senginx':[
'retval = "SENGINX-ROBOT-MITIGATION" in (page_get)',
],
'sitelock':[
'retval = "SiteLock Incident ID" in (page_get)'
],
'sonicwall':[
'retval = "This request is blocked by the SonicWALL" in (page_get)',
'retval = re.search(r"Web Site Blocked.+\bnsa_banner", page_get, re.I)',
'retval = re.search(r"SonicWALL", headers_get,re.I)'
],
'sophos':[
'retval = "Powered by UTM Web Protection" in (page_get)'
],
'stingray':[
'retval = re.search(r"\AX-Mapping-",headers_get, re.I)'
],
'sucuri':[
'retval = re.search(r"Sucuri/Cloudproxy", headers_get,re.I)',
'retval = "Sucuri WebSite Firewall - CloudProxy - Access Denied" in (page_get)',
'retval = re.search(r"Questions\?.+cloudproxy@sucuri\.net", (page_get))'
],
'tencent':[
'retval = "waf.tencent-cloud.com" in (page_get)'
],
'teros':[
'retval = re.search(r"\Ast8(id|_wat|_wlf)",headers_get, re.I)'
],
'trafficshield':[
'retval = re.search(r"F5-TrafficShield", headers_get,re.I)',
'retval = re.search(r"\AASINFO=",headers_get, re.I)'
],
'urlscan':[
'retval = re.search(r"Rejected-By-UrlScan",headers_get, re.I)',
'retval = re.search(r"/Rejected-By-UrlScan", page_get, re.I)'
],
'uspses':[
'retval = re.search(r"Secure Entry Server", headers_get,re.I)'
],
'varnish':[
'retval = re.search(r"varnish\Z",headers_get,re.I)',
'retval = re.search(r"varnish", headers_get,re.I)',
'retval = re.search(r"\bXID: \d+", page_get)'
],
'wallarm':[
'retval = re.search(r"nginx-wallarm", headers_get,re.I)'
],
'webknight':[
'retval = re.search(r"WebKnight", headers_get,re.I)'
],
'yundun':[
'retval = re.search(r"YUNDUN", headers_get,re.I)',
'retval = re.search(r"YUNDUN", headers_get,re.I)'
],
'yunsuo':[
'retval = re.search(r"<img class=\"yunsuologo\"", page_get, re.I)',
'retval = re.search(r"yunsuo_session",headers_get, re.I)'
]}
dd = ''
for k,v in waf_dic.iteritems():
for x in v:
try:
exec(x)
if retval:
dd = retval
return unicode('发现WAF : ','utf-8') + k
except :
pass
if dd == '':
print uul + unicode(' : 未能发现WAF', 'utf-8')
print scan_waf('http://butian.360.cn/')
好吧WAF指纹数量还是太少了,收集waf指纹这种工作还是需要团队来做才行,一个人忙不过来
1.0 批量检测
支持导入数据,校验WAF,所用功能都是基于sqlmap,因为个人能力有限没法收集更多的国产waf指纹,所以只能校验这么多了
自动生成两个结果,result.txt中是不存在waf的,log为日志文件
代码
代码已上传 GITHUB