# Class Pollution in Ruby: A Deep Dive into Exploiting Recursive Merges

Recursive merges on Ruby object attributes let attackers poison instance vars, parent class vars, and unrelated classes, enabling auth bypass and RCE.

- Payout: Research
- Program: Ruby
- Credited: Doyensec
- Disclosed: 2024-10-01
- Write-up quality: 8/10
- Original: https://blog.doyensec.com/2024/10/02/class-pollution-ruby.html
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-doyensec-com-2024-10-02-class-pollution-ruby-html
- Tags: domain:web3 impact:privesc impact:rce kind:research platform:research tech:ruby technique:chained technique:source-review vuln:auth-bypass vuln:prototype-pollution vuln:rce vuln:ssrf

**Target:** Ruby recursive merge / deep_merge (ActiveSupport, Hashie)
**Root cause:** Recursive merge implementations call instance_variable_set and attr_accessor on attacker-controlled keys, and follow respond_to?/public_send into class/superclass/subclasses, letting keys like 'class' or '_' escape the object into class-level state.
**Steps:**
1. Craft JSON with keys matching methods: `{"to_s":"Admin"}` merged into object overrides to_s return value, bypassing authorize.
2. For RCE, inject `{"protected_methods":["puts 1"]}` so health_check's instance_eval executes attacker code.
3. For Hashie, send `{"_":"Admin"}` so `_.to_s` reads injected Mash value instead of the real method.
4. To poison parent class: POST `{"class":{"superclass":{"url":"http://malicious.com"}}}` to /merge, then hit /launch-curl-command to trigger SSRF to attacker URL.
5. To poison unrelated class: loop POST `{"class":{"superclass":{"superclass":{"subclasses":{"sample":{"signing_key":"injected-signing-key"}}}}}}` until KeySigner's @@signing_key is replaced.
**How found:** Inspired by prototype pollution in JS/Python; author reasoned that Ruby's recursive merge using instance_variable_set/attr_accessor plus respond_to?/public_send could be walked via keys like 'class' and 'superclass' to escape object scope, then tested against ActiveSupport deep_merge and Hashie Mash.
**Exploitation:** Attacker submits JSON to a merge endpoint; recursive merge walks into class/superclass/subclasses via respond_to?/public_send, setting class variables. Poisoned @@url redirects server-side HTTP requests to attacker host; poisoned @@signing_key lets attacker forge signatures. Object-level pollution overrides to_s for auth bypass and protected_methods for instance_eval RCE.
**Escalation:** Chains object-level attribute pollution to class-variable pollution of parent and unrelated classes, persisting across requests and enabling SSRF and signing-key forgery.
**Insight:** When auditing Ruby merge helpers, check whether keys are passed to instance_variable_set/attr_accessor and whether the merge recurses via respond_to?/public_send — keys 'class', 'superclass', 'subclasses' let you pivot from an object to class variables of arbitrary classes; Hashie's '_' accessor returns a fresh Mash that bypasses method-override protection.
