イギリス時間の2013年10月25日16時から27日21時まで、オンラインで開催された「NotSoSecure CTF」に挑戦した筆者によるWrite Upです。
この記事は、ラックの「NotSoSecure CTF参戦記」を、一部修正、加筆して転載したものです。
「NotSoSecure CTF」は、NotSoSecure Labsがイギリス時間の2013年10月25日16時から27日21時まで開催したCTFで、競技はオンラインで行われました。
公式の発表によると、約1500人が登録し、25を超える国からの挑戦があり、最終的に2問とも正解をした参加者が25人いたとのことです。http://ctf.notsosecure.com/leaderboard/にてスコアボードが公開されています。
今回、私もこのCTFに挑戦しました。ここでは、各問の正解に至った道筋について解説をしたいと思います。
私は、ラックという情報セキュリティサービスを提供する企業で、Webアプリケーションのセキュリティ診断や、Webアプリケーションファイアウォールのシグネチャ開発などの業務を担当しています。
国内最大のセキュリティコンテスト「SECCON 2013」では、Webアプリケーションの問題を中心に、CTF出題者側として運営に参加しています。また、優秀な若手セキュリティ人材の発掘を目指す「セキュリティ・キャンプ2013」にも、CTFオーガナイザーとして参加しました。
そんな私ですが、実は自身がCTFに参加した経験はほとんどありません。「CTFチャレンジジャパン」や「Stripe CTF」など数えるほどです。それゆえに「他のCTFとは違う、日本ならではの問題を出題できるのではないか」と自負していたのですが、今回は情報収集のために「NotSoSecure CTF」に参加してみることとしました。
CTFの開催案内ページに名前とメールアドレスを登録しておくと、折り返し案内が届く仕組みとなっていました。届いたメールには、ルールとして「他の参加者に迷惑となるようなブルートフォース攻撃を禁止する」などの案内や、全問正解後の連絡先、そして競技サイトのURLが記載されていました。
早速URLにアクセスして、競技開始です。
私は普段、Webアプリケーションのセキュリティ診断を業務としていますので、本当であれば、使い慣れた自社製のツールを活用したいところなのですが、今回は土日の開催で、かつオンラインCTFのため自宅からの参戦となり、それらが使えません。このため、フリーのProxyツールである「Fiddler」と己の腕一本で立ち向かうこととなりました。
さて、URLにアクセスしたところ、以下のログインページが表示されました。
何しろ、「SQLiLab CTF」という別名も付いていましたので、まずはSQLインジェクションだろうと目星をつけ、UsernameやPasswordの入力欄その他に、「'」などの文字列を送信して、脆弱性が存在する個所の特定を試みました。ところが、どう見てもSQLインジェクションの反応は出てきませんでした。
これは困った……ということで、SQLインジェクション以外の手法にも視野を広げて挑戦を続けました。
いろいろな手法を試し尽くして行き詰まり、ダメ元でスタイルシートなどをのぞき始めた頃、運営からヒントがツイートされていることに気付きました。
そ、そういうことか……! ログイン画面にパラメータを送った後、error.phpに転送されることには気付いていたのですが、そのメッセージ部分はすっかり見逃していました。CTF慣れしていないせいだ、と言ってしまえばそうですし、宝探しゲーム的なものではなく、もっと実践的なものだと勝手に思い込んでいたために、見逃してしまっていたようです。
あらためてレスポンスを眺めてみると、何やら怪しい「7365637265745f72656769737465722e68746d6c」というメッセージが表示されていました。16進数の羅列と見てデコードしてみたところ、「secret_register.html」という文字列が得られました。早速これを用いてアクセスすると、秘密の(?)ユーザー登録画面が表示されました!
しめしめ、とユーザー登録画面でユーザーを登録して、その会員でログインをしてみたところ、ようやくログイン後のページが表示されました。しかし……
「You are not Admin!」
このアカウントではダメなようです。試しに、ユーザー名を「Admin」として登録を試みてみましたが、「User Already Exist」と怒られてしまいました。
とりあえず、これはやはりSQLインジェクションの出番ではないか? と思い、ユーザー登録画面に対して、Adminを意味する文字列を作る「Ad'||'min」「Ad'+'min」をはじめとして、その他いろいろとSQLインジェクションの攻撃文字列を雨あられと降らせました(大げさ)が、芳しい成果が得られません。
その時、ふと、“このユーザー登録画面でSQLインジェクションに対する反応がないということは、ユーザー登録自体は成功している。その成功したユーザーでログインをしたらどうなるのだろう?”と思いつきました。実際、ユーザー登録からログインまでの流れを試してみると、ログイン成功後の画面の内容に変わりはありませんでしたが、その際に発行されるCookieの値が変化していることに気付きました。
私が登録したユーザーでログインすると、
Set-Cookie: session_id=a2VpZ29AZXhhbXBsZS5qcA%3D%3D (base64でデコードすると、「keigo@example.jp」)
というCookieが発行されますが、SQL文として正しくない文字列をユーザー名に入れた場合、
Set-Cookie: session_id=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT
という反応となりました。
また、全件一致するような文字列(「' or 'a'='a」に類するもの)を送信した場合は、
Set-Cookie: session_id=YWRtaW5Ac3FsaWxhYnMuY29t (base64でデコードすると、「admin@sqlilabs.com」)
というように、他人のメールアドレス(恐らく「Admin」の)が表示されました。
この応答から、この部分にいわゆる「セカンドオーダーSQLインジェクション」の問題が存在していることが分かりました。
あとは地道にデータを取得していきます。詳しい手法は省きますが、SQLインジェクションの常道に沿って、まず「order by」を使ってカラム数を特定して、次に「union select」を使って、テーブル内の情報を1つずつ抜き出して表示させました。
この手法で、システムテーブルからテーブルの一覧を抜き出したところ、「users」といういかにもなテーブルが存在していることが分かり、次に、「users」テーブルに、「id」「name」「password」「email」というカラムが存在していることが分かりました。
そして、最後に、「union select password,1 from users where name='admin'」を実行したところ
Set-Cookie: session_id=c3FsaWxhYlJvY0tzISE%3D (base64でデコードすると、「sqlilabRocKs!!」)
と、ユーザー「admin」のパスワードが「sqlilabRocKs!!」であることが分かりました。これを用いてログインしてみたところ……無事に1つ目の問題の正解が表示されました!
Copyright © ITmedia, Inc. All Rights Reserved.