Cybersecurity-Projects/PROJECTS/beginner/deserialization-gadget-lab/test/support/matrix_probe.rb

118 lines
2.8 KiB
Ruby

# ©AngelaMos | 2026
# matrix_probe.rb
require "json"
GADGET_CLASSES = %w[
Gem::Requirement
Gem::Version
Gem::SpecFetcher
Gem::Source
Gem::Source::Git
Gem::RequestSet::Lockfile
Gem::URI
Gem::URI::HTTP
Gem::Package::TarReader
Net::BufferedIO
Net::WriteAdapter
UncaughtThrowError
].freeze
ERB_GUARD_ANCHOR = :def_method
ERB_DELEGATING_METHODS = %i[def_module def_class].freeze
GUARD_TOKEN = "@_init"
DELEGATION_TOKEN = "def_method"
PATCH_TOKEN = "git_command"
VULNERABLE_TOKEN = "popen(@git"
BINARIES = %w[make rake git].freeze
def gem_version(name)
spec = Gem.loaded_specs[name]
return spec.version.to_s if spec
Gem::Specification.find_all_by_name(name).map(&:version).max&.to_s || "absent"
rescue StandardError
"unknown"
end
def constant_present?(name)
Object.const_get(name)
true
rescue StandardError, ScriptError
false
end
def binary_present?(name)
ENV.fetch("PATH", "").split(File::PATH_SEPARATOR).any? do |dir|
File.executable?(File.join(dir, name))
end
end
def git_gadget_state
require "rubygems/source/git"
path = Gem::Source::Git.instance_method(:cache).source_location&.first
return "unknown" unless path && File.readable?(path)
body = File.read(path)
return "patched" if body.include?(PATCH_TOKEN)
return "vulnerable" if body.include?(VULNERABLE_TOKEN)
"unknown"
rescue StandardError, ScriptError
"unknown"
end
def safe_marshal_state
require "rubygems/safe_marshal"
path = Gem::SafeMarshal.method(:safe_load).source_location&.first
return "unknown" unless path && File.readable?(path)
File.read(path).include?("Date") ? "permits_date" : "no_date"
rescue StandardError, ScriptError
"absent"
end
def erb_state
require "erb"
{
"guarded" => erb_method_body(ERB_GUARD_ANCHOR)&.include?(GUARD_TOKEN),
"delegating" => ERB_DELEGATING_METHODS.select do |name|
erb_method_body(name)&.include?(DELEGATION_TOKEN)
end.map(&:to_s)
}
rescue StandardError, ScriptError
{ "guarded" => nil, "delegating" => [] }
end
def erb_method_body(name)
path, line = ERB.instance_method(name).source_location
return nil unless path && File.readable?(path)
File.readlines(path)[line - 1, 10].to_a.join
rescue StandardError, ScriptError
nil
end
def marshal_format
blob = Marshal.dump(nil)
"#{blob.getbyte(0)}.#{blob.getbyte(1)}"
end
report = {
"image" => ENV.fetch("MATRIX_IMAGE", "unknown"),
"ruby" => RUBY_VERSION,
"rubygems" => Gem::VERSION,
"psych" => gem_version("psych"),
"erb" => gem_version("erb"),
"json" => gem_version("json"),
"marshal_format" => marshal_format,
"binaries" => BINARIES.to_h { |name| [name, binary_present?(name)] },
"classes_baseline" => GADGET_CLASSES.to_h { |name| [name, constant_present?(name)] },
"git_gadget" => git_gadget_state,
"safe_marshal" => safe_marshal_state,
"erb_guard" => erb_state
}
puts JSON.generate(report)