ERB Deserialization Bypass via def_module/def_method/def_class
A deserialization vulnerability exists in Ruby ERB versions before 4.0.3.1, version 4.0.4, ERB versions 5.0.0 before 6.0.1.1, and ERB versions 6.0.2 before 6.0.4. The `@_init` instance variable guard in `ERB#result` and `ERB#run` can be bypassed via `ERB#def_module`, `ERB#def_method`, and `ERB#def_class`, allowing arbitrary code execution when an ERB object is reconstructed via `Marshal.load` on untrusted data.
Ruby versions before ERB 2.2.0 implemented an @_init instance variable guard in ERB#result and ERB#run to prevent code execution upon deserialization via Marshal.load. This guard is intended to block execution when an ERB object is reconstructed from untrusted data. However, the methods ERB#def_method, ERB#def_module, and ERB#def_class were not given the same protection, creating a bypass. An attacker capable of triggering Marshal.load on untrusted data in a Ruby application with the erb gem loaded can exploit ERB#def_module (using its zero-argument, default-parameter form) as a code execution sink. This bypass impacts Ruby on Rails applications that import untrusted serialized data, Ruby tools employing Marshal.load for caching or IPC, and legacy Rails applications (pre-7.0) utilizing Marshal for cookie session serialization. This bypass renders the @_init mitigation ineffective across all ERB versions from 2.2.0 through 6.0.3. Combined with the DeprecatedInstanceVariableProxy gadget (present in all ActiveSupport versions through 7.2.3), this enables a universal RCE gadget chain for Ruby 3.2+ applications using Rails. The vulnerability is identified as CVE-2026-41316.
Attack Chain
- The attacker crafts a malicious Ruby object containing an
ERBinstance and/or anActiveSupport::Deprecation::DeprecatedInstanceVariableProxyinstance. - The
ERBinstance has its@srcinstance variable set to a string containing malicious code with the “end\nsystem(‘id’)\ndef x” payload. - The vulnerable application calls
Marshal.loadon the crafted object, triggering deserialization. - During deserialization, the
DeprecatedInstanceVariableProxyis instantiated (if used), which then invokes theERB#def_modulemethod viamethod_missing. - The
ERB#def_modulemethod callsERB#def_methodwithout checking the@_initguard. - Inside
ERB#def_method, the malicious code in@srcis wrapped in a method definition and evaluated viamodule_eval. - The “end\nsystem(‘id’)\ndef x” payload causes the
system('id')command to execute during themodule_evalcall, bypassing the intended deserialization protection. - The attacker achieves arbitrary code execution on the target system, gaining the ability to perform malicious actions.
Impact
Successful exploitation allows an attacker to execute arbitrary code on the target system. This affects Ruby applications, including Ruby on Rails, which use Marshal.load on untrusted data. Specific impact includes potential compromise of web servers and the ability to read sensitive files, modify data, or install malware. Vulnerable applications include those using Marshal.load for caching, data import, or IPC, and legacy Rails applications (pre-7.0) using Marshal for cookie session serialization. This bypass renders the @_init mitigation ineffective across all ERB versions from 2.2.0 through 6.0.3.
Recommendation
- Upgrade your erb gem to version 4.0.3.1, 4.0.4.1, 6.0.1.1, or 6.0.4 to patch the vulnerability as described in the “Patches” section.
- Avoid using
Marshal.loadon untrusted data, as it is inherently unsafe. Consider using alternative serialization formats like JSON or YAML. - Deploy the “Detect ERB def_module Code Execution via Deserialization” Sigma rule to detect exploitation attempts.
Detection coverage 2
Detect ERB def_module Code Execution via Deserialization
criticalDetects code execution via ERB's def_module during deserialization, exploiting the @_init guard bypass.
Detect Suspicious Marshal.load with ActiveSupport::Deprecation
highDetects potentially malicious deserialization of ActiveSupport::Deprecation objects, which may indicate exploitation.
Detection queries are kept inside the platform. Get full rules →