Posted by usa on 14 May 2013
DL および Fiddle に、$SAFE レベルの設定に関わらず、汚染された文字列をシステム呼び出しに使用できる脆弱性が報告されました。 この脆弱性は CVE-2013-2065 として CVE に登録されています。
影響
DL または Fiddle を用いて Ruby からネイティブ関数を呼び出す際、渡されるオブジェクトの汚染フラグがチェックされていませんでした。 そのため、本来 SecurityError 例外が送出されるべきであるにもかかわらず、汚染されたオブジェクトをそのまま受け付けてしまいます。
DL の場合、以下のようなコードが影響を受けます:
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
また、Fiddle の場合、以下のようなコードが影響を受けます:
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
影響を受けるバージョンの Ruby を使用している全てのユーザーは、速やかに Ruby を更新するか、下に示す回避策を適用して下さい。
なお、Ruby を更新あるいは回避策を適用した場合でも、ポインタ値をメモリアドレスの数値として使用する場合は一切の保護が なされません 。 Ruby では数値オブジェクトは汚染フラグを持つことができないため、メモリアドレスを数値として渡された場合、チェックができないためです。 例えば、以下のようなコードが問題となります:
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
この例では、引数となるべき元の文字列は汚染されていますが、そのポインタを数値化した時点で、DL / Fiddle からは汚染の有無を判定できなくなっています。 このようなコードを書かないといけない場合は、ポインタを数値化する前に、事前に入力値の汚染フラグを確認するようにして下さい:
user_input = "uname -rs".taint
raise if $SAFE >= 1 && user_input.tainted?
my_function DL::CPtr[user_input].to_i
回避策
Ruby を更新できない場合、回避策として以下のモンキーパッチが使用できます:
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
影響を受けるバージョン
- ruby 1.9.3 patchlevel 426 より前の全ての ruby 1.9 系列
- ruby 2.0.0 patchlevel 195 より前の全ての ruby 2.0 系列
- revision 40728 より前の trunk
なお、ruby 1.8 系列に今回の脆弱性はありません。
クレジット
この脆弱性は Vit Ondruch によって報告されました。
更新履歴
- 2013-05-14 22:00:00 (JST) 初版