Omijanie zaufania obiektu w DL i Fiddle w Ruby (CVE-2013-2065)

Wykryto lukę bezpieczeństwa w modułach DL i Fiddle w Ruby gdzie niepewne napisy (tainted strings - napisy, które są wprowadzane z zewnętrznych źródeł np. pliku, klawiatury, sieci itp. Są oznaczone specjalną flagą - Object#taint, Object#untaint, Object#tainted?) mogą być wykorzystane przez wywołania systemowe niezależnie od ustawionego poziomu $SAFE w Ruby. Luce tej został przypisany identyfikator CVE-2013-2065.

Wpływ

Natywne funkcje wystawione dla Rubiego z DL lub Fiddle nie sprawdzają wartości taint ustawionej w przekazywanych obiektach. Może to spowodować zaakceptowanie niepewnych obiektów jako zaufane dane wejściowe, w momencie gdy powinien zostać zgłoszony wyjątek SecurityError.

Przykładowy kod DL ilustrujący lukę będzie wyglądać mniej więcej tak:

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

Przykładowy kod Fiddle ilustrujący lukę będzie wyglądać mniej więcej tak:

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

Wszyscy użytkownicy podatnych wersji powinni natychmiast zaktualizować Ruby lub skorzystać z obejścia problemu.

Zauważ, że to nie powstrzymuje od użycia numerycznych offsetów pamięci jako wartości wskaźników. Numery nie mogą być tainted, więc kod przekazujący numeryczny offset pamięci nie może być sprawdzony. Dla przykładu:

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

W tym przypadku adres w pamięci jest przekazywany, więc zaufanie obiektu nie może być określone przez DL / Fiddle. W takim przypadku proszę sprawdzać zaufanie danych wprowadzanych przez użytkownika przed przekazaniem adresu w pamięci:

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

Obejście problemu

Gdy nie możesz zaktualizować Rubiego, ta łatka - monkey patch - może być użyta jako obejście problemu:

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

Dotyczy wersji

  • Dla 1.9 wszystkie wcześniejsze wersje od 1.9.3 patchlevel 426
  • Dla 2.0 wszystkie wcześniejsze wersje od 2.0.0 patchlevel 195
  • Wcześniej niż rewizja trunk 40728

Wersje 1.8 nie są podatne.

Podziękowania

Podziękowania dla Vita Ondrucha za zgłoszenie tego problemu.

Historia

  • Opublikowane 2013-05-14 13:00:00 (UTC)