渗透测试-浅析WAF绕过

前言

WAF 是什么?

全称 Web Application Firewall (WEB 应用防护系统),与传统的 Firewall (防火墙) 不同,WAF 针对的是应用层。

WAF是通过执行一系列针对HTTP/HTTPS的安全策略来专门为Web应用提供保护的一种网络安全产品。WAF可以增大攻击者的攻击难度和攻击成本,但是不是100%安全的。
在这里插入图片描述
目前市场上的WAF主要有以下几类:

  1. 基于硬件WAF:绿盟WAF;

  2. 基于软件WAF:以安全狗为代表;

  3. 部署在云端的WAF:百度加速乐、安全宝等。

我们谈绕过WAF,其实就是谈如何绕过过滤机制。

WAF绕过

绕过WAF的技术可分为以下几类:

  1. 大小写混合
  2. 替换关键字
  3. 使用编码
  4. 使用注释
  5. 参数污染
  6. 缓冲区溢出
  7. 性能因素
  8. 整合绕过

本文接下来对这几类方法进行介绍。

大小写绕过

大小写绕过用于只针对小写或大写的关键字匹配技术,正则表达式/express/i 大小写不敏感即无法绕过,这是最简单的绕过技术。

z.com/index.php?page_id=-15 uNIoN sELecT 1,2,3,4 

这个大家都很熟悉,对于一些太垃圾的WAF效果显著,比如拦截了union,那就使用Union、UnIoN等等绕过。

替换关键字

1、关键字双写

这种情况下大小写转化无法绕过,正则表达式会替换或删除select、union这些关键字,如果只匹配一次就很容易绕过。

z.com/index.php?page_id=-15 UNIunionON SELselectECT 1,2,3,4

有些时候甚至构造得更复杂:SeLSeselectleCTecT,不过不建议对此抱太大期望。

2、同价词替换

  • and和or有可能不能使用,或者可以试下&&||能不能用;
  • =不能使用的情况,可以考虑尝试<、>,因为如果不小于又不大于,那就是等于了;
  • 空格不能使用(常见),可以使用如下符号进行替换:%20 %09 %0a %0b %0c %0d %a0 /**/

【注释】 在mysql中%0a是换行,可以代替空格,这个方法也可以部分绕过最新版本的安全狗。而在sqlserver中可以用/**/来代替空格。

3、特殊字符拼接

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

使用编码

1、URL编码

在Chrome中输入一个连接,非保留字的字符浏览器会对其URL编码,如空格变为%20、单引号%27、左括号%28、右括号%29等。普通的URL编码可能无法实现绕过,还存在一种情况URL编码只进行了一次过滤,可以用两次编码绕过:

page.php?id=?id=1/**/UNION/**/SELECT
经过两次解码:
page.php?id=1%252f%252a*/UNION%252f%252a*/SELECT 

2、十六进制编码

 z.com/index.php?page_id=-15/*!u%6eion*/ /*!se%6cect*/ 1,2,3,4…
 SELECT(extractvalue(0x3C613E61646D696E3C2F613E,0x2f61))

示例代码中,前者是对单个字符十六进制编码,后者则是对整个字符串编码,使用上来说较少见一点。

3、Unicode编码

看一下常用的几个符号的一些Unicode编码:

  • 单引号: %u0027
  • 空格:%u0020
  • 左括号:%u0028
  • 右括号:%u0029

常见的编码当然还有二进制、八进制,它们不一定都派得上用场,但后面会提到使用二进制的例子。

使用注释

看一下常见的用于注释的符号有哪些://, -- , /**/, #, --+,-- -, ;,--a

1、普通注释

z.com/index.php?page_id=-15 %55nION/**/%53ElecT 1,2,3,4   
'union%a0select pass from users#

/**/在构造得查询语句中插入注释,规避对空格的依赖或关键字识别。#、--+用于终结语句的查询。

2、内联注释

相比普通注释,内联注释用的更多,它有一个特性/!**/只有MySQL能识别(/*!*/表示注释里面的语句会被执行)。

index.php?page_id=-15 /*!UNION*/ /*!SELECT*/ 1,2,3
?page_id=null%0A/**//*!50000%55nIOn*//*yoyu*/all/**/%0A/*!%53eLEct*/%0A/*nnaa*/+1,2,3,4

两个示例中前者使用内联注释,后者还用到了普通注释。使用注释一个很有用的做法便是对关键字的拆分,要做到这一点后面讨论的特殊符号也能实现,当然前提是包括/、*在内的这些字符能正常使用。

参数污染

HPP(HTTP Parameter Polution)又称做重复参数污染,最简单的就是?uid=1&uid=2&uid=3,对于这种情况,不同的Web服务器处理方式。
在这里插入图片描述
示例代码:

/?id=1;select+1,2,3+from+users+where+id=1/?id=1;select+1&id=2,3+from+users+where+id=1/?id=1/**/union/*&id=*/select/*&id=*/pwd/*&id=*/from/*&id=*/users

具体WAF如何处理,要看其设置的规则,不过就示例中最后一个来看有较大可能绕过。

缓冲区溢出

缓冲区溢出用于对付WAF,有不少WAF是C语言写的,而C语言自身没有缓冲区保护机制,因此如果WAF在处理测试向量时超出了其缓冲区长度,就会引发bug从而实现绕过。

举个例子:

?id=1 and (select 1)=(Select 0xA*1000)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26

示例0xA*1000指0xA后面”A"重复1000次,一般来说对应用软件构成缓冲区溢出都需要较大的测试长度,这里1000只做参考,在某些情况下可能不需要这么长也能溢出。

性能因素

  1. WAF在设计的时候都会考虑到性能问题,检测数据包的包长或检测数据流长度,有一个限制。因此在设计WAF的时候可能就有一个默认值,默认多少个字节的流大小,或是多少个数据包。此时可以向HTTP POST添加填充数据,达到一定数目之后,POST中的sql注入恶意代码没有被检测了,达到了bypass的目的。
  2. WAF的业务影响问题,一些传统硬件防护设备为了避免在高负载的时候影响用户体验,如延时等问题,会在高负载的时候关掉WAF防护功能,在低负载的时候又开启WAF功能。此方法,主要针对软件WAF——可将一个payload,使用脚本并发发送多次,发现有些通过了WAF,有些被WAF拦截了

整合绕过

整合的意思是结合使用前面谈到的各种绕过技术,单一的技术可能无法绕过过滤机制,但是多种技术的配合使用成功的可能性就会增加不少了。

举个例子:

id=1/*!UnIoN*/+SeLeCT+1,2,concat(/*!table_name*/)+FrOM /*!information_schema*/.tables /*!WHERE */+/*!TaBlE_ScHeMa*/+like+database()-
?id=-725+/*!UNION*/+/*!SELECT*/+1,GrOUp_COnCaT(COLUMN_NAME),3,4,5+FROM+/*!INFORMATION_SCHEM*/.COLUMNS+WHERE+TABLE_NAME=0x41646d696e--  
index.php?page_id=-15+and+(select 1)=(Select 0xAA[..(add about 1000 "A")..])+/*!uNIOn*/+/*!SeLECt*/+1,2,3,4

分块传输

本次实验采用的是win10下面的phpstudy+安全狗。安全狗开启http安全监测,可以看到会过滤and or等关键词:
在这里插入图片描述
在根目录,写一个最简单的php页面如下:

<?php
header("Content-Type: text/html;charset=utf-8");
$id = $_REQUEST["id"];
if ($id){
    echo "接收到的id为" . $id;
    }
else{
    echo "No id";
}
?>

采用$_REQUEST既可以接收post参数也可以接收get参数,我们来试试安全狗有没有用,尝试加关键字and:
在这里插入图片描述可以看到,请求中含有SQL注入关键字的都会被拦截。

分块编码

在通过http传输文件的时候,通常会有一个Content-Length用来指定文件的长度,比如传输图片,静态页面,客户端也以Content-Length作为接收内容结束的标志,接收完毕后就可以断开连接了。但是有时候发送方并不能确定内容的长度,造成的影响就是:接收方无法通过Content-Length得到报文体的长度,也就无法得知什么时候应该中断连接。

为此我们需要一个新的机制:不依赖头部的长度信息,也能知道实体的边界。

HTTP 1.1引入了分块传输编码的方式。只要在header头部加入 Transfer-Encoding: chunked ,就代表这个报文采用了分块编码。此时不用指定Content-Length接收方也可以知道什么时候传输结束了,只需要约定一个信号即可,比如,接收方只要接收到一个长度为0内容为0的分块,则代表传输完毕。

那么怎么样将一个普通的报文改编成分块传输形式的呢?

报文中的实体需要改为用一系列分块来传输。每个分块包含十六进制的长度值和数据,长度值独占一行,长度不包括它结尾的CRLF(rn),也不包括分块数据结尾的 CRLF。最后一个分块长度值必须为 0,对应的分块数据没有内容(两个空行),表示实体结束。

下面用一个最简单的例子,向本地post一个数据包,为id=123455:
在这里插入图片描述可以实验一下,如果关闭burp在repeater选项里面自带的content-length补全功能,然后去掉Content-Length,就无法接收到id。
在这里插入图片描述接下来我们将其改为分块传输的方式:
在这里插入图片描述通过上图可以看到,即使没有Content-Length,我们也可以采用分块传输的方式,分多少块,每块多大都不是唯一的,但是最后的结尾需要一个长度为0内容为空的块(内容为两个空行)。

WAF绕过

当我们将传输的内容分块时,处理后的HTTP请求由于和已知的payload相差较大,所以可以起到一定的绕过WAF的效果。比如我们来试试安全狗:
在这里插入图片描述可以看到当对id=1 and 1=1进行分块后传输,可以绕过安全狗的检查。

进阶绕过

但是有一些如Imperva的,360等比较好的WAF已经对传输编码的分块传输做了处理,可以把分块组合成完整的HTTP数据包,这时直接使用常规的分块传输方法尝试绕过的话,会被WAF直接识别并阻断。

这个时候我们可以在每个分块长度标识处加上分号“;”作为注释,如下所示:
在这里插入图片描述几乎所有可以识别传输编码数据包的WAF,都没有处理分块数据包中长度标识处的注释,导致在分块数据包中加入注释的话,WAF就识别不出这个数据包了。

此外构造畸形的分块数据包还可以绕过ModSecurity,具体可以看参考文章,这里本地没有复现。关于该绕过方法的局限性,chunk只能在POST方法中使用,如果是GET方法的注入点就无法绕过。

对于burpsuite,已经有人做出了将普通的post数据包转变为分块数据包的插件,地址为:BurpSuite分块编码插件。

发布了117 篇原创文章 · 获赞 84 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/weixin_39190897/article/details/103583673