Umgehung von Object#taint in DL und Fiddle in Ruby (CVE-2013-2065)

Es besteht eine Verwundbarkeit in DL und Fiddle in Ruby, wodurch als “tainted” eingestufte Strings unabhängig von dem durch Ruby festgelegten $SAFE-Level in Systemaufrufen verwendet werden können. Dieser Schwachstelle wurde die CVE-Nummer CVE-2013-2065 zugewiesen.

Auswirkungen

Native Funktionen, die mittels DL oder Fiddle von Ruby aus aufgerufen werden, überprüfen nicht den “taint”-Status der ihnen übergebenen Objekte. Dies kann dazu führen, dass als “tainted” eingestufte Objekte als Eingabe akzeptiert werden anstatt eine SecurityError-Ausnahme auszulösen.

Betroffener Code für DL kann etwa folgendermaßen aussehen:

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

Betroffener Code für Fiddle kann etwa folgendermaßen aussehen:

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

Alle Nutzer eines betroffenen Releases sollten dringend aktualisieren oder einen der folgenden Workarounds anwenden.

Beachten Sie, dass dies eine Verwendung von numerischen Memory-Offsets als Pointer-Werte nicht verhindert. Zahlen können nicht als “tainted” markiert werden, so dass Code, der einen numerischen Memory-Offset übergibt, nicht überprüft werden kann. Zum Beispiel:

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 diesem Fall wird der Speicherort übergeben und der “taint”-Status des Objekts kann von DL / Fiddle nicht festgestellt werden. Überprüfen Sie in solchen Fällen den “taint”-Status der Benutzereingabe vor der Übergabe des Speicherorts:

user_input = "uname -rs".taint
raise if $SAFE >= 1 && user_input.tainted?
my_function DL::CPtr[user_input].to_i

Workarounds

Wenn ein Upgrade von Ruby nicht möglich ist, kann der folgende Monkeypatch als Notlösung verwendet werden:

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

Betroffene Versionen

  • Alle Versionen von Ruby 1.9 vor Ruby 1.9.3 patchlevel 426
  • Alle Versionen von Ruby 2.0 vor Ruby 2.0.0 patchlevel 195
  • Alle Trunk-Versionen vor Revision 40728

Ruby 1.8 ist nicht betroffen.

Danksagung

Vielen Dank an Vit Ondruch für das Melden dieses Problems.

Verlauf

  • Erstmals veröffentlicht am 2013-05-14 13:00:00 (UTC)