作者:【腾讯安全平台部】 lake2
公众号:腾讯安全应急响应中心
前言
两年兴起的大型网络攻防对抗比赛以实战的方式进行,这个举措非常好,以攻促防(“talk is cheap,show me the shell”),参赛大企业会更加关注实际的安全威胁并且想办法缓解,客观上也繁荣了安全行业,一时间相关的安全服务及安全产品畅销,特别是有实时检测和阻断能力的IDS/IPS大放异彩。
于是,大企业纷纷采购部署IDS、IPS、WAF这些安全防护产品,压缩了攻击面,那就带来两个问题:
-
渗透测试这个方向还有没有机会?也有渗透测试人员发的知乎热帖[1]对未来提出困惑;
-
部署了IDS/IPS设备,在设备后端被防护着的系统的安全漏洞是否还要修复?不要急着回答,且看后文。
(以下字数约4000字,阅读时长约10分钟)
一. IDS/IPS防护原理及绕过思路
IDS工作在网络层,旁路部署,通过抓取和分析网络流量来发现攻击;
IPS一般也是在网络层旁路,可以理解为具备阻断能力的IDS,是IDS的升级版(也有IDS检测到攻击通知阻断设备执行阻断动作的设备联动模式),可以覆盖网络层和应用层;
WAF是在应用层防护Web攻击的程序,一般是跟Web接入层对接,可旁路可串行,仅能覆盖应用层,详细的技术原理和实践可参考TSRC博客的这篇文章[2]。
本文为阐述方便,统一把网络层的旁路防护设备视为IPS。
安全专家们关于应用层WAF的绕过探讨已经很多了[3],TSRC博客近期也会推出推出一篇WAF绕过的文章,所以本文就不落窠臼,我们下沉到网络层来玩玩。
现在看来,IPS的旁路防护原理很简单,其经典代表如开源的Snort,就是在网络上分析流量,发现符合规则的流量则冒充服务端回包响应客户端实现阻断或者替换的目的,这是一种典型的链路劫持手法。常见的场景是封禁网站(如非法网站的封禁)、篡改网页内容(运营商插广告)、阻断端口扫描和漏洞攻击(IPS),实施链路劫持的人必须控制某段网络。
用旧作《某电商网站流量劫持案例分析与思考》[4]里面的一幅图来示意,攻击者嗅探到符合特征的流量后即伪造响应,这里是伪造了HTTP响应(为了篡改页面),如果只是阻断的话就是伪造rst包干扰TCP握手过程或者连接。
为什么IPS多是旁路很少串行?超大流量下的串行处理对设备的性能是巨大的挑战,你可以尝试下,相信你会回来同意我的意见的。
因为IPS是旁路部署,所以只能通过发伪造包的方式来达到干扰双方正常通信的目的,正常的包其实还是会到达客户端和服务端,只不过相同序号的包操作系统已经处理过了,这些包会被认为是错误的包从而丢掉。
所以,从原理分析,绕过IPS可以从两个方向着手:
-
检测上,如果IPS在流量里检测不到攻击特征,则不会有后续动作;
-
阻断上,正常包也会到达服务器,只是来晚了,如果有办法让伪造的包失效,则阻断不会发生。
二. IPS绕过实例
常见的IPS阻断场景有四种:
-
可以建立TCP连接,检查客户端发出的HTTP请求中的特征,如匹配则发rst阻断或HTTP响应替换,用于域名封禁或Web攻击防护;
-
不让建立TCP连接,即客户端发syn包时IPS直接回rst,同时后续如有ack包会双向回rst阻断,用于访问控制或端口封禁;
-
DNS查询场景,伪造DNS响应,劫持网站域名用于封禁或者钓鱼攻击;
-
UDP传输场景下,伪造ICMP响应,告知客户端UDP端口不可达。
对于1,可以从两方面入手:在应用层,一般利用IPS和Web Server对HTTP语法的理解差异;在网络层,一般利用IPS和操作系统协议栈对TCP/IP的处理方式差异,后文会讨论;
对于2,暴露面比较窄,绕过会困难一点,但也不是不可能;
对于3,换个DNS Server就行了,业界对DNS劫持也有很好的方案,这里不再赘述;
对于4,是阻断UDP访问,分析见后文。
下面按技术手法介绍一些常见的网络层绕过手法,均实测过:
1、TCP分片
一些IPS是字节级逐包检查的,并没有实现TCP分片重组能力,那就把关键字拆到两个TCP包里面就可以暗度陈仓。以下是Perl实现的TCP分片HTTP请求,关键字是“www.bad.com”,会拆分到两个TCP包发出。
use IO::Socket; $sock = new IO::Socket::INET (PeerAddr => '118.x.x.x', PeerPort => 'http(80)'); die "Couldn't create socket: $@" unless $sock; $sock->autoflush(1); # ========== case1 TCP frag bypass $sock->print("GET / HTTP/1.1\r\nHost: www.b"); sleep(4); $sock->print("ad.com\r\n\r\n"); $document = join('', $sock->getlines()); print "\n$document\n";
看图,TCP分片绕过,返回了200(如果不实施TCP分片,会返回302)。
慢!如果IPS实现了TCP分片重组怎么办?
这个问题问得好,仔细看代码,里面有个sleep,它并非是凑数的:有的IPS虽然有TCP分片重组能力,但是不会无限等待,会有个时间,超过这个时间就不会再检查了,所以设置这个sleep的时间超过IPS的分片重组超时值而又没有达到操作系统和Web Server的超时值,那就可以绕过。
2、IP分片
IP包也是支持分片的,原理类似,只是要构造IP包就需要用到Python下的组件Scapy,TCP三次握手后用Scapy的fragment函数按600字节一个拆分发送IP分片包(当然也可以把syn包也分片),代码如下:
看看效果:
IP分片重组超时绕过的原理与TCP一样,不多说。需要注意的是,TCP分片更通用,IP分片受到链路上网络设备的影响,有可能中途会被网关重组或者丢弃,达不到预期效果。
大名鼎鼎的扫描器nmap有一个规避FW/IDS的功能就是利用IP分片进行端口扫描,参数是-f,可以用--mtu设置分片大小,最小的值是8,但是实测有些路由会丢弃MTU比较小的IP包。
3、程序bug / 性能问题
程序总是有bug的,特别是这种流量处理程序,会瞬间并发处理非常多的数据包,遇到各种特殊情况,稍微不注意就会产生异常。为了保障业务连续性,防护设备一定会在发生异常时保证业务连续性,所以制造异常包让IPS响应不过来也是可以实现绕过的目的。以前我们的DDoS防护系统宙斯盾处理流量的时候,稍微考虑不周程序就core掉了,别说了,说多了都是泪。
测试某个IPS时,建立TCP连接后发送大量序号错误的ack包,然后再发送正确的包,结果IPS出现bug回了序号不对的rst包,产生了绕过。
还可以发送大量的无效包,消耗IPS性能,一旦IPS慢下来,他的包就会晚于正常包到达,也产生绕过的机会。
这种异常可以通过协议fuzz来发现[5],Scapy也是一个好的协议fuzz生成工具,可以一试。
另外,链路上很多网络设备,各自处理TCP/IP协议的实现不一样,也可能带来绕过或者其他问题。比如现在操作系统判断rst包会精确到序号,连在滑动窗口都不行,但是一些NAT设备仍然存在无视序号的rstblood问题。
4、伪造TCP状态
在测试一个IPS的时候发现这是一个久经考验的系统,前述各种方法绕过都失败了,应用层各种绕也不行,居然连bug也fuzz不到,而且它还不是包过滤的,而是基于状态跟踪的 —— 简单测试检测模式,不建立TCP连接直接发HTTP请求,如果还有响应的IPS就是包过滤,反之则是状态跟踪。
结果成也萧何败也萧何,它的破绽恰恰在状态跟踪上:
建立TCP连接后,我们可以发送一个定制TTL值的rst包,这个TTL值正好能够经过IPS但不会到达服务端,IPS基于状态跟踪会认为这个TCP连接已断开,后续的各种包都不会去检测,但服务端不受影响会继续等待ack包来。
经过测试这个当前环境里大概TTL设置为12左右rst包会路过IPS却不会到达服务端,所以我们的关键代码这样写:
完美绕过:
这个构造TTL值绕过的问题(我称之为“fake-TTL”攻击)可能是一些带状态跟踪的网络设备的通用问题。
5、阻止三次握手的缺陷
对于直接阻止TCP三次握手的情况,我们详细来看看。
客户端发起syn包,IPS冒充服务端给客户端回rst,假装端口关闭,但是实际上端口开放的话服务端的synack包是能到达客户端的,只是同序号rst先到,后到的synack被操作系统丢弃。简单,客户端丢掉伪造的rst包,接受synack包,然后向服务端发ack包建立三次握手,这时候IPS会双向回rst(如果你家IPS没动作,我建议你考虑换一个牌子的IPS……)。
注意,你的ack包一定比IPS的rst先到服务端并被应用程序执行 —— 我们可以在这个ack包把所有的内容都发了,是一次性盲打,适用于一定场景的漏洞探测和利用。
如果想完美绕过,就得想办法让第二个rst失效;想完美防护,就得让第一个rst生效。仔细研读TCP/IP,具体情况具体分析了,或者试着fuzz一下。
6、IPv6
随着IPv6的普及,会有越来越多的设备支持IPv6(过渡时期是IPv4/6双栈),但是正在服役的IDS/IPS/WAF不一定支持,于是可以畅行无阻。
浏览器支持这样的方式直接访问IPv6地址:https://[IPv6地址]/xxxx
现在的Windows操作系统都默认支持IPv6,但Windows防火墙的规则可能还是只配了IPv4,使用IPv6地址就如入无人之境。以下演示机器的防火墙限制了IPv4的全部端口的访问,用IPv6地址可以连通所有端口,登录远程桌面看看:
举一反三,大家得检查一下各种安全系统是否开启了对IPv6的支持。
7、加密协议
IPS一般是不能解SSL流量的,所以也检测不到SSL加密的流量,所以尝试通过HTTPS访问是绕过检查的一种方法 —— 这个场景下,基于应用层的WAF的优势就展现出来了。同理,HTTP/2、QUIC、WebSocket等新一代Web浏览协议也能规避IPS。
既然这些协议可以规避IPS,那就是可以通过服务端支持这些协议来防御链路劫持,既保护了通信过程不被篡改,又避免了黑客在网络中窃取用户敏感信息[5]。最佳实践就是部署HTTPS来防止链路劫持,虽说继续劫持HTTPS并非不可能,但是攻击难度提高了几个数量级。此次引起行业关注的github劫持事件,就是SSL证书不正确导致劫持被发现曝光的[6]。
8、阻断UDP
UDP通信是无连接状态的通信,阻断起来更困难。
IPS通过向客户端发送端口不可达(port unreachable)的ICMP包来实现UDP的阻断。但是效果有限:首先是客户端是否认可这个ICMP包,跟应用程序的代码有关[8];另外,很可能这个ICMP包不能活着通过链路上的各种路由及防火墙。
三. 修复方案
知道了具体的技术手段,优化方案就比较容易推导出来了,读者可以自行归纳一下,这就是“以攻促防”。当然,有问题不怕,怕的是不知道自己有问题、无视问题甚至拒绝正视问题。
后记
相信经过上文探讨,前言里面的问题已经有了答案,根据纵深防御原则,不管有无防护设备,安全漏洞都要及时修复。
当然,本文并不是想表达IPS很多问题,而是告诉大家,安全是一个动态过程,防御不是永远有效的,要不断地运营优化,要关注安全防护系统自身的有效性,红蓝对抗的时候不要只关注渗透目标,还要关注整个安全防御体系。感谢宙斯盾的球头人牛长一起测试这个代号“007”的项目,后面的流量分析就交给他来跟进。
最后老规矩,招人,基础安全各个领域,特别是流量安全分析方向,有意向的同志可投递简历。不提供联系方式,你肯定能找到我的。
【附录】
[1] 现在网站越来越难渗透了,渗透这个方向还有前途吗
[2] WAF建设运营及AI应用实践
[3] 全方位绕过软WAF思路
[4] 某电商网站流量劫持案例分析与思考
[5] Fuzz漏洞挖掘漫谈
[6] “流量劫持”整正窃取你的信息?!
[7] Github疑似遭中间人劫持,网友反馈访问报证书错误
[8] 关于UDP接收ICMP端口不可达(port unreachable)
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1173/