8593


# きっかけ

> "x: 100, y: 200, z: 300" のような文字列を  
>  {x: 100, y: 200, z: 300}のようなハッシュに変換する方法  
>  ：
>
> ### パターン2 eval使う
>
> 超簡単(ruby怖っ)
[code]

>     def to_hash(str)
>       eval("{#{str}}")
>     end
>  
[/code]

>
> [Ruby 文字列をハッシュに変換 -
> Qiita](http://qiita.com/uplus_e10/items/65a50935250639bf8308)

を読んで、ふと  
「`eval` 族を使うのは怖いけど、 `BasicObject#instance_eval` であれば `str`に何らかのメソッドを使われたとしても
`NoMethodError` になるのではなかろうか。リテラルは使えるし、これって `eval` でのセキュリティ対策に使えるかも？」  
と思いました。

Rubyist の共通見解として「`eval` 族と `method_missing` は最終兵器」というのがあるかと思いますが、
`BasicObject#instance_eval` を使うと少し気楽に `eval` できるかも、と考えました（速度などは考慮してません）。

# 具体的にみてみる

## 上記例を実行

[code]

    $ pry
    [1] pry(main)> str = "x: 100, y: 200, z: 300"
    => "x: 100, y: 200, z: 300"
    [2] pry(main)> BasicObject.new.instance_eval("{#{str}}")
    => {:x=>100, :y=>200, :z=>300}
    
[/code]

問題なくハッシュを生成できました。

## インジェクションしてみる１：メソッド

[code]

    [3] pry(main)> str = "};puts 'hello!';{"
    => "};puts 'hello!';{"
    [4] pry(main)> BasicObject.new.instance_eval("{#{str}}")
    NoMethodError: undefined method `puts' for #<BasicObject:0x007fa980ca39c0>
    from (pry):4:in `instance_eval'
    
[/code]

無事に `NoMethodError` になりました。

## インジェクションしてみる２：コマンド出力

[code]

    [5] pry(main)> str = "};`echo hello`;{"
    => "};`echo hello`;{"
    [6] pry(main)> `echo hello`
    => "hello\n"
    [7] pry(main)> BasicObject.new.instance_eval("{#{str}}")
    NoMethodError: undefined method ``' for #<BasicObject:0x007fa9814fe0e8>
    from (pry):7:in `instance_eval'
    
[/code]

`str = "};%x[echo hello];{"` でも同じく `NoMethodError` でした。

## 定数設定

[code]

    [8] pry(main)> ARY
    NameError: uninitialized constant ARY
    from (pry):8:in `__pry__'
    [9] pry(main)> str = '};::ARY = %w[a b c];{'
    => "};::ARY = %w[a b c];{"
    [10] pry(main)> BasicObject.new.instance_eval("{#{str}}")
    => {}
    [11] pry(main)> ARY
    => ["a", "b", "c"]
    
[/code]

設定されてしまいました。

## グローバル変数設定

[code]

    [12] pry(main)> $ary
    => nil
    [13] pry(main)> str = '};$ary = %w[a b c];{'
    => "};$ary = %w[a b c];{"
    [14] pry(main)> BasicObject.new.instance_eval("{#{str}}")
    => {}
    [15] pry(main)> $ary
    => ["a", "b", "c"]
    
[/code]

グローバル変数も設定されてしまいました。

# 質問

`BasicObject#instance_eval`
だと定数とグローバル変数が犯されうることがわかりましたが、そのほかに危険性はあるでしょうか。具体的に攻撃文字列を挙げていただければ幸いです。  
また、定数とグローバル変数が犯されると、どのような危険性が考えられるでしょうか。

以上2点について、よろしくお願いいたします。


CC BY-SA 3.0
2015-03-31T04:23:01.093
8591
2015-03-31T04:37:17.200
4114
question
ruby security
ブランクスレート（BasicObject）のインスタンスでinstance_evalするのはセキュリティ対策になりうるか？


たとえば、puts は、

[code]

    str = "};::Kernel.puts 'hello';{"
    
[/code]

とやれば働きますし、同様に外部コマンドも次のようにして実行できちゃいます。

[code]

    str = "};::Kernel.system 'ls';{"
    
[/code]

また、定数を触れるということは、クラスも触れるということなので、クラスに対してなんでもできると思った方がいいと思います。


CC BY-SA 3.0
2015-03-31T04:37:17.200
8593
2015-03-31T04:37:17.200
3249
8591
answer