likes
comments
collection
share

手把手教你实现 XSS 攻击

作者站长头像
站长
· 阅读数 17

XSS 攻击理论知识学习完成的同学可以参考这篇一些攻击实现,理论背的再好还是要实践呀

手把手教你实现 XSS 攻击

您可以在线查看完整的示例源代码

攻击分类

XSS 攻击主要可以分为三类

  1. 存储型(持久型)(server端缺陷)
  2. 反射型(server 端缺陷)
  3. DOM 型(浏览器端缺陷)

接下来将从上面的攻击类型分别给出例子

反射型

通常一般教程先讲存储型,但是本篇文章基于实验先讲反射型,因为它和存储型有异曲同工之处,而且更底层

反射型通常出现于以下场景:常见于通过 URL 传递参数的功能,如网站搜索、跳转等

接下来就给出一个网站搜索的例子

下面是一个很常见的 search 页面例子

<!-- search.html -->
<body>
  <form
    action="http://localhost:3000/search"
    method="GET"
    enctype="application/json"
  >
    搜索:<input class="search-input" name="search" type="text" />
    <br />
    搜索内容:
    <br />
    <button class="confirm-button" type="submit">确认</button>
  </form>
</body>

效果如下

手把手教你实现 XSS 攻击

手把手教你实现 XSS 攻击

注意 搜索内容: 这部分的 DOM 内容以及 URL 的参数 search,这两个点其实就是 XSS 攻击的突破口

突破口

上面这个网站是一个由后端渲染的网站,那么它是怎么渲染的呢?

// user.js
router.get("/search", function (req, res, next) {
  const { search } = req.query;
  res.setHeader("Content-Type", "text/html");
  res.send(generateSearchHTML(search));
});
exports.generateSearchHTML = (search = "") => {
  return `
    .....
        搜索内容:${search}
    .....
  `;
};

其中 generateSearchHTML 将基于上面给出的 HTML 模板生成 HTML 文件内容,并返回给用户,并且会将用户输入的搜索内容,即 search 填充的 HTML 模板中,即 搜索内容:${search}

回到 XSS 攻击的本质就是执行恶意代码,换言之就是 <script></script> 的执行,回到上面的模板,如果转换成下面的结构,请问 <script></script> 是否会执行?

<body>
  搜索内容:<script></script>
</body>
<script></script>

不要拘束与传统的三段式写法而以为 <body> 中的脚本不会执行,上面的代码能够顺利执行,因此执行 XSS 攻击的关键就是编写代码到搜索框中,后台返回给用户时即完成 XSS 攻击,实操如下

  1. 输入 <script>alert("反射型 XSS 攻击")</script>
  2. 确认

手把手教你实现 XSS 攻击

反射型的 XSS 攻击我曾经在一个卖民用机械的网站上见过,技术栈估计是 JSP + SpringBoot 一把梭,其中的搜索框就有这个问题

那么如何吸引用户完成这个攻击呢?其实吸引用户点击具有上面这个 url 构成的链接即可,并不需要用户亲自去输入

存储型

了解到上面反射型的 XSS 攻击,我相信你们对 XSS 攻击已经有了初步了解,被攻击的原因是没有对用户的输入做任何处理就作为 HTML 的一部分返回,所以存储型的攻击也可以以此为基础

后端渲染

后端渲染,即由后端返回 HTML

存储型 XSS 攻击原理还是一样,像服务端传输恶意代码,并且服务端会将其拼接至字符串中

被攻击网站如下

手把手教你实现 XSS 攻击

简单的 HTML 代码

<body>
  评论:<span class="comment"></span>
  <input class="comment-input" type="text" style="display: none" />
  <br />
  <button class="button" onclick="handleModifyComment()">新增评论</button>
  <button
    class="confirm-button"
    onclick="confirmModifyComment()"
    style="display: none"
  >
    确认
  </button>
</body>
<script>
  const comment = document.querySelector(".comment");
  const commentInput = document.querySelector(".comment-input");
  const button = document.querySelector(".button");
  const confirmButton = document.querySelector(".confirm-button");
  const getComment = () => {
    // ..
  };
  const handleModifyComment = () => {
    // ...
  };
  const confirmModifyComment = () => {
    // ...
  };

  // 初次加载
  getComment();
</script>

后端代码

router.get("/v1/user", function (req, res, next) {
  const [name, setName] = useName();

  res.setHeader("Content-Type", "text/html");
  res.send(generateHTML(name));
});

router.get("/v1/name", function (req, res, next) {
  const [name, setName] = useName();

  res.send(name);
});

router.post("/v1/name", function (req, res, next) {
  const [name, setName] = useName();
  const { username: updateName } = req.body;

  res.send(setName(updateName));
});

// ../utils/util
exports.generateHTML = (username) => {
  return `
    <body>
      username: ${username}
    </body>
  `;
};

用一个闭包模拟存储(很合理吧?)

let originName = "杰尼龟";

const useName = () => {
  const name = originName;
  /**
   * 重新赋值用户名称
   * @param {string} [newName="杰尼龟"]
   */
  const setName = (newName = "杰尼龟") => {
    originName = newName;
    return originName;
  };

  return [name, setName];
};

module.exports = useName;

实操如下,基于用户名称一个致命的恶意代码

<script>alert("XSS 攻击")</script>

手把手教你实现 XSS 攻击

说实话,存储型 XSS 攻击的场景出现在展示部分(比如用户的评论、用户名称、用户信息。。。)在新的网站都少了很多,展示部分的 XSS 攻击更可能出现在后端渲染的网站上,因为后端渲染是通过拼接字符串成 HTML 然后再返回

前端渲染不必多说,Vue/React/Angular 都是前端渲染的代表,它们以 JS 为基础渲染,渲染和更新通过 Ajax 来实现,而底层是通过 Node.innerText | Node.innerHTML 此类的方法渲染和更新页面

而浏览器会将通过 Node.innerText | Node.innerHTML 动态插入的内容当成普通文本,不会维护到DOM里面,XSS 攻击自然也无从谈起,因为无法执行

不过不要以为新网站没有 XSS 攻击,像 Nuxt.jsNext.js 这些 SSR 框架其实就是后端渲染(服务端渲染),本质上也是 HTML 拼接,不过此类开源框架往往有避免手端

前端渲染

前面说过了前后端分离的情况下,大部分数据都是通过 Ajax 拿到的,而且框架会帮我们通过 Node.innerText | Node.innerHTML 这种方式渲染,不会有 XSS 攻击的可能,但这并不意味着前端渲染并不会有被攻击的危险,比如直接渲染字符串

Vue 中提供了一个指令,叫 v-html文档里很明确的指出了具有 XSS 攻击,但像我这种文档仅供 API 参考的人,当时完全没在意,但到后来背八股文的时候发现其潜在的危险性

比如下面这个富文本的例子

手把手教你实现 XSS 攻击

像现在市面上的富文本编辑器有的可以添加链接并展示(很合理吧?富文本编辑器应该还是很常见的)

<!-- http://localhost:3000/richText.html -->
<head>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
  <div id="app">
    <div>
      来吧展示!
      <div class="richText" v-html="richText"></div>
    </div>
    <br />
    <button @click="addLink">添加链接</button>
  </div>
</body>
<script>
  const app = new Vue({
    el: "#app",
    data() {
      return {
        richText: "<h1>Hello World</h1>",
      };
    },
    methods: {
      addLink() {
        const linkDialog = prompt("请输入链接", "");
        if (linkDialog !== null && linkDialog !== "") {
          this.richText += `<a href=${linkDialog}>一个你无法拒绝点击的链接名称</a>`;
        }
      },
    },
  });
</script>

假设啊假设啊,添加链接后会把 <a href=${linkDialog}>一个你无法拒绝点击的链接名称</a> 传入到后端,等用户下次访问 richText.html 就直接渲染后端返回的字符串,大概就是上面这个情况

如果我把链接变成下面这种

  1. javascript:alert(&#x27;XSS&#160;攻击&#x27;)
  2. '' onclick=alert(&#x27;XSS&#160;攻击&#x27;)

PS:&#x27; 表示 "(引号),&#160; 表示 (空格),皆为 HTML 编码,HTML 可以直接渲染,v-html 使用引号和空格需要转义,详情参考html中常见符号的代码表示

手把手教你实现 XSS 攻击

芭比Q了,被攻击了

上面的 12 两条恶意代码的渲染 DOM 结构如下

1

手把手教你实现 XSS 攻击

2

手把手教你实现 XSS 攻击

1 是利用浏览器 <a>href 的特性,而 2 是利用空格间断属性,和 SQL 注入都是同根同源的操作呀!

DOM 型

阅读完反射型和存储型其实你应该已经明白,所谓 XSS 攻击就是寻找具有直接渲染和注入恶意代码的漏洞

回到 DOMXSS 攻击和存储型以及反射型的区别,区别为 DOMXSS 攻击为浏览器端缺陷,无需后端交互,即为前端代码的缺陷,其实也就是前端过于相信用户内容,直接渲染

而如果把存储型里的前端渲染的 XSS 攻击例子,想象成预览富文本不就是 DOM 型攻击咯(绝对不是我偷懒!)

总结

从反射型到存储型再到 DOM 型,我一开始找例子发现都是使用 JSP 的旧网站才会有 XSS 攻击漏洞,我想是不是前后端分离的现在已经不会有 XSS 攻击了,但随着我进一步深入,发现 SSR 和富文本场景也会有 XSS 攻击的可能,这些场景对我来说其实算是新兴的前端方向(至少相对于 JSP),所以也许 XSS 攻击从未离开我们

参考资料

  1. 前端安全系列(一):如何防止XSS攻击?
  2. 原始 HTML - Vue2 Doc
  3. html中常见符号的代码表示
  4. web安全之XSS实例解析
转载自:https://juejin.cn/post/7176631728390275127
评论
请登录