脆弱性のあるWebアプリを直すイベントに参加した
先日リクルートが開催する 脆弱性を直して学ぶ!Webセキュリティハンズオン
に参加しました。
コロナという事情もありオンラインで開催していたので、地方の自分としては嬉しかったです。
このイベントでは、事前課題として脆弱性のあるWEBアプリを直すというものが渡され当日はその解説を行うというものでした。
この脆弱性のあるアプリは、昨年の新人研修で行ったWebセキュリティの研修で使ったらしいです。
直していくアプリケーションはコードが公開されておりこちらです→GitHub - ommadawn46/badsns2019: Bad SNS 2019 Web Application Hardening (Boot Camp 2019) - Speaker Deck
今回せっかく学んだことを忘れないためにもこのブログに残したいと思います。
ただ、脆弱性が20個もあり全てを書くととても長くなるので、一部のみ書いていこうと思います。
今回書くこと
今回は以下の3点について書いていこうと思います。
- パストラバーサル
- SSRF
- 暗号利用モード
パストラバーサル
今回のアプリケーションではパストラバーサルが発生するような状況になっていました。
このアプリはユーザ登録時にアイコンが設定できるようになっています。
また、アプリは以下の仕様を持っています。
新規登録時にアイコンは非同期でアップロードを行い、アイコンが保存できればファイル名を返す
ファイル名は任意にファイル名をパラメータとして送ることが出来る
攻撃シナリオ
新規登録時にファイルをアップロード(APIからファイル名
A.png
が返ってくる)攻撃者はファイル名を
../../../../../../../etc/passwd
などに変更/users/:id/icon
にアクセスするとサーバ上の/etc/passwd
が取得できる
修正が必要な箇所は、アイコン画像取得のAPIで修正前のコードはこちらです。 badsns2019/users_controller.rb at master · ommadawn46/badsns2019 · GitHub
良くない修正例
アイコンのファイル名に対して以下のようなバリデーションを新規登録時にかけることによって、不正なファイル名を防ぐようにしました。
files/sns/app/model/user.rb
before_save :verify_valid_user def verify_valid_user if self.icon_file_name != sanitize_filename(self.icon_file_name) errors.add(:base, 'アイコン名が不正です') end return false if errors.any? end def sanitize_filename(filename) return unless filename filename.gsub! /^.*(\\|\/)/, '' filename.gsub! /[^A-Za-z0-9\.\-]/, '_' end
../
などの文字をフィルターで変換して防御するパターンは、攻撃不能か確認するのが難しく完璧に防ごうとするとロジックが複雑になるという欠点を持っています。
なので、このようなパストラバーサルに対しての修正は、カノニカルパス(RealPath)を使用した検証を推奨
というアドバイスをいただきました。
より良い修正例
Rubyでは、realpathを返してくれるメソッドFile.realpath (Ruby 2.7.0 リファレンスマニュアル)があるのでそちらを使って実装します。
What is directory traversal, and how to prevent it? | Web Security Academy を参考に実装しました。
Below is an example of some simple Java code to validate the canonical path of a file based on user input:
File file = new File(BASE_DIRECTORY, userInput);
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) { // process file }
files/sns/app/controllers/users_controller.rb
def icon user = User.find_by id: params[:id] send_data File.read "#{Rails.root}/public/images/default.png", disposition: 'inline' and return if user.nil? begin # 追加 base_dir = "#{Rails.root}/public/icons/" raise unless File.realpath(user[:icon_file_name], base_dir).start_with?(base_dir) send_data File.read "#{Rails.root}/public/icons/#{user[:icon_file_name]}", disposition: 'inline' rescue presets = Dir["#{Rails.root}/public/icons/presets/*"].sort send_data File.read presets[user.id % presets.length], disposition: 'inline' end end
加えたのは、こちらの2行です。
base_dir = "#{Rails.root}/public/icons/" raise unless File.realpath(user[:icon_file_name], base_dir).start_with?(base_dir)
File.realpath(user[:icon_file_name], base_dir)
で絶対パスを取ることによってディレクトリを決定し、
その時のディレクトリがbase_dirから始まるものでなければ例外を発生させるという処理です。
このようにすることでパストラバーサルを回避することが出来ました。
SSRF
この脆弱性については聞いたことすらありませんでした。
アプリケーションサーバからOSコマンドインジェクションなどを行うことで、内部のネットワークのサーバに対してアクセス出来るという脆弱性だそうです。 今回の場合だとOpenGraphを取得してくる処理が問題で起きるようです。
該当コード: badsns2019/open_graph_controller.rb at master · ommadawn46/badsns2019 · GitHub
ユーザからの投稿したURLからOGタグを取得するのですが、こちらから内部のftpサーバにアクセスも出来るようでそこを突いた脆弱性でした。 (当日のイベントではSSRF自体聞いたことなく、メモを取る暇がなかったのでコードの修正は割愛させていただきます🙇♂️)
1, 2年前にEC2やGCEからcredentialsが取得できると話題になった脆弱性らしいです。
SSRF脆弱性を利用したGCE/GKEインスタンスへの攻撃例 | “>はい
SSRF(Server Side Request Forgery)徹底入門 | 徳丸浩の日記
暗号利用モード
該当コード: badsns2019/password_reset_controller.rb at master · ommadawn46/badsns2019 · GitHub
この OpenSSL::Cipher.new(‘aes-256-ecb’)
が問題のコードです。
class OpenSSL::Cipher (Ruby 2.7.0 リファレンスマニュアル) によるとブロック暗号の方式は
“CBC”
“CFB”
“ECB”
“OFB”
の4パターンが利用できるようですが、今回のこのアプリケーションでは、ECBモードが採用されていました。
このECBモードは、同じ平文ブロックに対しては、同じ暗号ブロックに変換するという仕様で暗号化は出来ているものの(他の暗号モードと比べ)見破りやすくなっています。
暗号を使う時には公式のドキュメントを見て、正しく暗号化のモードを理解しなければいけないなと思った脆弱性でした。
まとめ
今回紹介した脆弱性以外にも、SQLインジェクションやXSSなどなど…様々な脆弱性を解説していただきました。 大変勉強になりました、ありがとうございました!
今回のイベントで、セキュリティについての興味を持ち勉強する良いきっかけになりました。 次回似たようなイベントが開催された時には、全部解けるぐらいの知識をつけられるよう勉強していきたいと思います。
Burp Suite - Cybersecurity Software from PortSwigger (このツールは便利そうだったので、使ってみたい…)