7.5 High
CVSS2
Access Vector
NETWORK
Access Complexity
LOW
Authentication
NONE
Confidentiality Impact
PARTIAL
Integrity Impact
PARTIAL
Availability Impact
PARTIAL
AV:N/AC:L/Au:N/C:P/I:P/A:P
0.026 Low
EPSS
Percentile
89.1%
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:
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:
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
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.