Ruby: Variant of CVE-2013-0269 (Denial of Service and Unsafe Object Creation Vulnerability in JSON)


During my recent keyword argument separation work on `rb_scan_args` in the master branch, I discovered what I now think is a vulnerability. While the CVE-2013-0269 change fixed most usage of `JSON.parse`, it ended up not fixing `Kernel#JSON`. The reason behind this is that internally, in `JSON::Parser#initialize` (in `cParser_initialize` in `ext/json/parser/parser.c`), there is a separate branch taken depending on whether an option hash was provided. The fix for CVE-2013-026 only fixed one of these branches (when a option hash is provided). It did not fix the other branch (when no option hash is provided). `Kernel#JSON` is able to easily hit the case where no option hash is provided, because it does: ```ruby def JSON(object, *args) if object.respond_to? :to_str JSON.parse(object.to_str, args.first) ``` In the common case, no extra arguments are provided, and `args.first` is `nil`. Historically, Ruby has allowed the `rb_scan_args` `:` character to handle a `nil` option hash like no option hash was provided. This is deprecated in the master branch, and a warning is issued, but it is still supported. I fixed this in the master branch in the `rb_scan_args` commit, as it was needed to avoid the warning: https://github.com/ruby/ruby/commit/80b5a0ff2a7709367178f29d4ebe1c54122b1c27#diff-59fb0f5411be4c22009691e1a7f5a185 . It was only later, when I was going to report this issue upstream that I realized the security implications. I believe all previously released versions of Ruby since 1.9 (when JSON was included in stdlib) are vulnerable to this. I think this fix should be backported to Ruby 2.4, 2.5, and 2.6, and another CVE issued. In addition to `Kernel#JSON`, there are some other vulnerable calls, though they are likely to be less common. Full example code: ```ruby require 'json' class A < Struct.new(:a) def self.json_create(object) new(*object['args']) end def to_json(*args) { 'json_class' => self.class.name, 'args' => [ a ], }.to_json(*args) end end js = A.new(1).to_json p JSON.parse(js) #=> {"json_class"=>"A", "args"=>[1]} p JSON(js) #=> #<struct A a=1> # Also vulnerable, resulting in #<struct A a=1> p JSON.parse(js, nil) p JSON[js, nil] p JSON::Parser.new(js).parse ``` ## Impact This highly depends on the application using in question. In order to be vulnerable, `Kernel#JSON` or one of the other vulnerable calls must be called with user provided input. I am not sure this results in denial of service since Ruby 2.2, due to the support of dynamic symbols. However, I have not analyzed the related JSON code to determine if it creates dynamic or static symbols when `create_additions` is used. Assuming that `Kernel#JSON` is called with user-provided input, this allows creation of arbitrary objects where there is a named class that has a `json_create` singleton method.. More precisely, this allows calling `json_create` methods on any named constant with arbitrary arguments (assuming the constant returns a true value for `json_createable?`). Many Ruby applications use libraries that have objects in constants that support `method_missing` and could possibly be vulnerable. However, I have not done any research into possible exploitability, which is why I listed severity as Medium. If any `json/add/*` files have been required, this could possibly be very dangerous, as those can allow the creation of arbitrary core/stdlib objects. For example `json/add/ostruct` being required, when combined with this vulnerability, allows the creation of arbitrary objects that support attacker-defined methods with attacker-defined values of any type supported by JSON. `json/add/regexp` allows the creation of arbitrary Regexps which could easily lead to denial of service, and combined with a vulnerability in the regexp engine (Onigmo), could potentially lead to remote code execution.