聊聊 Wafw00f 源码及流量特征

wafw00f介绍

这不是聊聊流量本次的重点,相关介绍及使用方法相信大家已经了解 ,码及所以此处就直接引用其开发者对该工具的特征介绍。

To do 聊聊流量its magic, WAFW00F does the following:

Sends a normal HTTP request and analyses the response; this identifies a number of WAF solutions.通过发送正常的​HTTP​请求并且分析其返回包 ,判断其是码及否使用 WAF  ,若使用确认WAF类型If that is 特征not successful, it sends a number of (potentially malicious) HTTP requests and uses simple logic to deduce which WAF it is.若是无法通过正常的​HTTP​请求结果分析出是否使用WAF以及其类型,则构造恶意的聊聊流量请求通过简单的逻辑再次进行判断If that is also not successful, it analyses the responses previously returned and uses another simple algorithm to guess if a WAF or security solution is actively responding to our attacks.如果这也不成功,免费模板它会分析之前返回的码及响应 ,并使用另一种简单的特征算法来猜测​WAF或安全解决方案是否正在积极响应我们的攻击使用场景/方法

这不是本次的重点 ,想要具体了解其用法去其github主页即可

​​https://github.com/EnableSecurity/wafw00f​​

本工具的聊聊流量作用上面已经很详细的描述出来了,概括一下很简单:探测目标是码及否存在WAF ,也可以说wafw00f是特征一个Web应用防火墙(WAF)指纹识别的工具 。服务器租用

使用方法不难,聊聊流量此处不做介绍 ,码及若想进一步了解见 :WEB 防火墙探测工具 -- wafw00f 使用教程 - General 的特征个人博客

wafw00f 源码解析

所谓源码解析并不会完整分析其源码中的所有功能 ,比如 日志、输出 、展示 等功能不在我们分析的范围内,而其对 WAF 的检测逻辑 、所发流量的特征 、WAF 识别的指纹等将是重点要分析的源码库地方。

流程 :

解析源码含义  ,分析特征抓包观察源码文件结构

为了方便大家之后去看源码 ,此处简单描述一下源码的组成 ,其中没有描述的说明其用不大 ,大家自己去看就知道了,其中标注important是本工具的主要功能文件,后续将重点说明 。

binlib

asciiarts.py

evillib.py用于向目标建立连接发起请求( important )

plugins​用于判断各个WAF的指纹 ( important )

aliyundun.py阿里云盾的特征值匹配文件

huaweicloud.py****华为云的特征值匹配文件

baidu.py百度云加速的云计算特征匹配文件

……

还有很多特征文件大家自己去看就好

__init__.pymain.py该工具主要的检测 、判断功能的类与函数都在该文件中实现( important )manager.py​加载plugins​中的WAF​指纹判断文件识别目标WAF类型wafprio.py

上述非important的文件起到的作用多是一些起到 输出选项、连接 等功能的文件/函数,所以不做特殊介绍

基本流程

请求的流程并不复杂 ,和最开始介绍处的流程如出一辙 :

发送正常HTTP​请求,并判断是否存在WAF若存在根据指纹判断其类型若正常HTTP​检测不出WAF​,亿华云则附加恶意的请求尝试出发WAF并分析其类型

最后所谓的根据算法猜测,流量特征不明显 ,不在此次分析的范围内 ,实际用处也不大 。

请求运行流程( important)

以第一次的正常HTTP请求探测为例(其余的都一致)。

由于对 Python 并不是太了解 ,所以作用域的表示采用了C++中的::(若是不对请及时指出会做更改)。香港云服务器

main.py分析

完整的源码就不放在这占地方了,大家随时可到其github主页获取,此处直接分析其重点部分

由上述流程可以看出,所有的请求均是从class WAFW00F中发起 ,所以该类就是我们分析的重中之重 !

请求的具体实现(比如请求中携带了哪些内容)将在分析evillib.py文件是分析,此处主要分析何时要发出何种请求 。

从main()函数出发看其逻辑 :(解析参数等功能将直接略过)

没有指定其余参数

对输入的URL​做相关处理后放到target​中传入WAFW00F进行处理

复制attacker = WAFW00F(target, debuglevel=options.verbose, path=path,followredirect=options.followredirect, extraheaders=extraheaders,proxies=proxies)

# 若请求没有结果则说明目标网站或本地网络有问题

if attacker.rq is None:

log.error(Site %s appears to be down % hostname)

continue1.2.3.4.5.6. 由于第一次做的是常规探测,若此处就匹配到了WAF的指纹则输出结束即可,具体的输出等逻辑不是重点,略过;若无法分析出其是否存在WAF​或匹配不出WAF​则通过identwaf()函数进步拼凑恶意参数进行探测; 复制waf = attacker.identwaf(options.findall)log.info(Identified WAF: %s % waf)1.2. 进入identwaf()​函数后 ,便会尝试匹配各个WAF的特征; 复制def identwaf(self, findall=False): detected = list() try: self.attackres = self.performCheck(self.centralAttack) except RequestBlocked:

return detected

for wafvendor in self.checklist: self.log.info(Checking for %s % wafvendor) if self.wafdetections[wafvendor](self): detected.append(wafvendor) if not findall:

break

self.knowledge[wafname] =

detected

return detected1.2.3.4.5.6.7.8.9.10.11.12.13.14.

这就是基本的流程 。

解析来如果还没有确定出WAF便会进入其自己提供的一个算法 ,但这并不是我们对流量特征分析所要关注的地方,所以就不探讨了 。

下面到了激动人心的时刻  ,WAFW00F类中到底是如何实现的呢 ?

class WAFW00F

一上来就中了大奖 :

复制class WAFW00F(waftoolsengine): xsstring = <script>alert("XSS");</script> sqlistring = "UNION SELECT ALL FROM information_schema AND or SLEEP(5) or " lfistring = ../../../../etc/passwd rcestring = /bin/cat /etc/passwd; ping 127.0.0.1; curl google.com xxestring = <!ENTITY xxe SYSTEM "file:///etc/shadow">]><pwn>&hack;</pwn>1.2.3.4.5.6.7.

WAFW00F这个工具所要构造拼接的恶意代码在一开始就全部列出了 ,而且在之后的使用中绝无变化,要构造恶意的请求就是从上述5个字符串中选择1个或多个直接使用。

可以看到改用据用于判断是否存在WAF的语句就以下五类 :

XSS类                     <script>alert("XSS");</script>SQL注入类              UNION SELECT ALL FROM information_schema AND or SLEEP(5) or 遍历类                    ../../../../etc/passwd命令执行/拼接类    /bin/cat /etc/passwd; ping 127.0.0.1; curl google.comXXE类                     <!ENTITY xxe SYSTEM "file:///etc/shadow">]><pwn>&hack;</pwn>

那么有了这些语句,WAFW00F又该如何拼接呢?

复制def normalRequest(self): return self.Request()def customRequest(self, headers=None): return self.Request(headers=headers)def nonExistent(self): return self.Request(path=self.path + str(random.randrange(100, 999)) + .html)def xssAttack(self): return self.Request(path=self.path, params= { s: self.xsstring})def xxeAttack(self): return self.Request(path=self.path, params= { s: self.xxestring})def lfiAttack(self): return self.Request(path=self.path + self.lfistring)def centralAttack(self): return self.Request(path=self.path, params={ a: self.xsstring, b: self.sqlistring, c: self.lfistring})def sqliAttack(self): return self.Request(path=self.path, params= { s: self.sqlistring})def oscAttack(self): return self.Request(path=self.path, params= { s: self.rcestring})1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.

具体逻辑并不难理解 ,拿出几种几个来说一下 。

1 :def xssAttack(self)

复制def xssAttack(self): return self.Request(path=self.path, params= { s: self.xsstring})1.2.

例如给一个简单的例子:我们传给wafw00f的URL为 :http://127.0.0.1:9000则经过该参数拼接后的请求就为  :

http://127.0.0.1:9000/?s=<script>alert("XSS");</script>(URL编码前 ) 。

2 :def centralAttack(self):

复制def centralAttack(self): return self.Request(path=self.path, params={ a: self.xsstring, b: self.sqlistring, c: self.lfistring})1.2.

例如给一个简单的例子:我们传给wafw00f的URL为:http://127.0.0.1:9000则经过该参数拼接后的请求就为:

http://127.0.0.1:9000/?s=<script>alert("XSS");</script>&b=UNION SELECT ALL FROM information_schema AND or SLEEP(5) or &c=../../../../etc/passwd(URL编码前 )

从前面的流程可以看出,在def identwaf(self, findall=False):中调用的拼接的语句的方法就是本方法  ,拼接进三个语句 ,

其他的逻辑相同。

evillib.py分析

上面只是在上层对要拼接哪些参数进行构造 ,实际上组合成完整的 HTTP 报文调用requests.get()进行请求的是在该文件中 。

wafw00f中若没有通过—headers指定头部的话 ,会使用自己默认的—headers这个默认的 headers 就定义在该文件中 。

复制def_headers = { Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3, Accept-Encoding: gzip, deflate, Accept-Language: en-US,en;q=0.9, DNT : 1, # Do Not

Track request header

User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3770.100 Safari/537.36, Upgrade-Insecure-Requests: 1

#

}1.2.3.4.5.6.7.

默认情况下,接下来通过requests.get()请求时便会使用该头部  ,

复制def Request(self, headers=None, path=None, params={ }, delay=0, timeout=7): try: time.sleep(delay) if not headers: h = self.headers else: h =

headers

req = requests.get(self.target, proxies=self.proxies, headers=h, timeout=timeout, allow_redirects=self.allowredir, params=params, verify=False) self.log.info(Request Succeeded) self.log.debug(Headers: %s\\n % req.headers) self.log.debug(Content: %s\\n % req.content) self.requestnumber += 1

return req

except requests.exceptions.RequestException as e: self.log.error(Something went wrong %s % (e.__str__()))1.2.3.4.5.6.7.8.9.10.11.12.13.14.15. 指纹的识别

拿Huawei Cloud WAF的指纹来说

复制#!/usr/bin/

env python

Copyright (C) 2022, WAFW00F Developers.See the LICENSE file for copying permission.NAME = Huawei Cloud Firewall (Huawei)def is_waf(self): schemes = [

# 匹配 cookie

self.matchCookie(r^HWWAFSESID=),

# 匹配 header 中的 Server

self.matchHeader((Server, rHuaweiCloudWAF)),

# 匹配 body

self.matchContent(rhwclouds\\.com), self.matchContent(rhws_security@) ] if any(i for i in schemes): return True return False1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.

CSDN使用华为云防护

其余脚本见 :wafw00f/wafw00f/plugins at master · EnableSecurity/wafw00f

wafw00f 流量

本文作者 :Deutsh , 转载请注明来自FreeBuf.COM

滇ICP备2023006006号-51