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".taint

Impacted 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".taint

All 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_i

In 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_i

Workarounds

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
end

Affected 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

More News...