Object taint bypassing in DL and Fiddle in Ruby (CVE-2013-2065)
Posted by usa on 14 May 2013
There is a vulnerability in DL and Fiddle in Ruby where tainted strings can be used by system calls regardless of the $SAFE level set in Ruby. This vulnerability has been assigned the CVE identifier CVE-2013-2065.
Impact
Native functions exposed to Ruby with DL or Fiddle do not check the taint values set on the objects passed in. This can result in tainted objects being accepted as input when a SecurityError exception should be raised.
Impacted DL code will look something like this:
def my_function(user_input)
handle = DL.dlopen(nil)
sys_cfunc = DL::CFunc.new(handle['system'], DL::TYPE_INT, 'system')
sys = DL::Function.new(sys_cfunc, [DL::TYPE_VOIDP])
sys.call user_input
end
$SAFE = 1
my_function "uname -rs".taintImpacted Fiddle code will look something like this:
def my_function(user_input)
handle = DL.dlopen(nil)
sys = Fiddle::Function.new(handle['system'],
[Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
sys.call user_input
end
$SAFE = 1
my_function "uname -rs".taintAll users running an affected release should either upgrade or use one of the workarounds immediately.
Note that this does not prevent numeric memory offsets from being used as pointer values. Numbers cannot be tainted, so code passing a numeric memory offset cannot be checked. For example:
def my_function(input)
handle = DL.dlopen(nil)
sys = Fiddle::Function.new(handle['system'],
[Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
sys.call input
end
$SAFE = 1
user_input = "uname -rs".taint
my_function DL::CPtr[user_input].to_iIn this case, the memory location is passed, and taintedness of the object cannot be determined by DL / Fiddle. In this case, please check the tainting of the user input before passing the memory location:
user_input = "uname -rs".taint
raise if $SAFE >= 1 && user_input.tainted?
my_function DL::CPtr[user_input].to_iWorkarounds
If you cannot upgrade Ruby, this monkey patch can be used as a workaround:
class Fiddle::Function
alias :old_call :call
def call(*args)
if $SAFE >= 1 && args.any? { |x| x.tainted? }
raise SecurityError, "tainted parameter not allowed"
end
old_call(*args)
end
endAffected versions
- All ruby 1.9 versions prior to ruby 1.9.3 patchlevel 426
- All ruby 2.0 versions prior to ruby 2.0.0 patchlevel 195
- prior to trunk revision 40728
ruby 1.8 versions are not affected.
Credits
Thanks to Vit Ondruch for reporting this issue.
History
- Originally published at 2013-05-14 13:00:00 (UTC)
Recent News
Ruby 4.0.0 Released
We are pleased to announce the release of Ruby 4.0.0. Ruby 4.0 introduces “Ruby Box” and “ZJIT”, and adds many improvements.
Posted by naruse on 25 Dec 2025
A New Look for Ruby's Documentation
Following the ruby-lang.org redesign, we have more news to celebrate Ruby’s 30th anniversary: docs.ruby-lang.org has a completely new look with Aliki—RDoc’s new default theme.
Posted by Stan Lo on 23 Dec 2025
Redesign our Site Identity
We are excited to announce a comprehensive redesign of our site. The design for this update was created by Taeko Akatsuka.
Posted by Hiroshi SHIBATA on 22 Dec 2025
Ruby 4.0.0 preview3 Released
We are pleased to announce the release of Ruby 4.0.0-preview3. Ruby 4.0 introduces Ruby::Box and “ZJIT”, and adds many improvements.
Posted by naruse on 18 Dec 2025