Index
/file は query の file_id をそのまま使って、fetch(/api/filename/${fileId}) の結果を filenameEl.innerHTML に入れている。通常の filename は html.escape されるので直接XSSにはならないが、file_id=../file/<uuid> とするとfetch 先が /api/file/<uuid> になる。
つまり、アップロードしたファイル本文を /api/file/<uuid> から読ませ、その本文を innerHTML に挿入できる。本文に <img onerror=...> を入れておき、bot に /file?file_id=../file/<uuid> を report させると、bot が持つ flag cookie を外部Webhookへ送れる。
DNSの問題。named.conf を確認すると allow-transfer { any; }; になっている。
app 側は dig @dns にユーザー入力を足して実行するだけなので、alpaca.internal AXFR を投げれば全 record を取得できる。
jail.py は audit hook を入れる前に flag.txt を os.open しており、その fd は閉じられていない。sys.modules['os'] の module object は残っているため使える。
したがって sys.modules['os'].read(3, 100) (fd=3)などで、hook に聞かれないまま flag を読める。
chal.sh が root で flag.txt を作って 0400 にした後、nobody の shell を起動する。
共通の環境で接続ごとにchal.sh が実行されflag.txtを作り直すので、権限変更前に読める瞬間が発生することを利用する。
待ち受け
> nc 34.170.146.252 33024
$ while :; do cat flag.txt 2>/dev/null; done
誘発
> echo | nc 34.170.146.252 33024
今回は WORKDIR /home/alpaca で、useradd -m alpaca により /home/alpaca 自体が alpaca 所有になっている。flag.txt と chal.sh は root 所有 0400 だが、ディレクトリに write 権限があるので消して新規に作成することは可能。
1本目の接続では通常の chal.sh が root で flag.txt を作った後、alpaca shell に落ちる。この shell を閉じずに chal.sh を cat flag.txt だけの内容へ差し替える。2本目の接続を開くと、socat が root で bash chal.sh を実行するため、差し替えた script が既存の root-only flag.txt を root 権限で読んで出力する。
python3 -c "print(chr(ord('🦙')+1))"
/member?img=... は受け取った値に対して path.replace("../", "") を1回適用し、./images/ を前置して open() する。....// には元から ../ が含まれるが、置換後に残った文字が新しい ../ になるため、フィルタをすり抜けられる。