Hacker News new | ask | show | jobs
by xentronium 4909 days ago
http://www.insinuator.net/2013/01/rails-yaml/

Some explanation why YAML user input is evil.

It works like this

    1.9.3p327 :001 > id = YAML.load("--- !ruby/string:Arel::Nodes::SqlLiteral \"1 --\"\n") # if user input can contain arbitrary YAML
    "1 --"
It looks like string, but it's not.

    1.9.3p327 :002 > Keyword.where(:id => id).first
      Keyword Load (0.3ms)  SELECT `keywords`.* FROM `keywords` WHERE `keywords`.`id` = 1 -- LIMIT 1
4 comments

Posting the gory details this early on is not a nice thing to do. It's probably best to hold off for a while until everyone has had a reasonable chance to upgrade.
At this stage with the vulnerability publicly and widely reported - demonstrating an attack vector that involves seemingly harmless code is perfectly acceptable. Not everyone understands the magic involved and it would be able to spot exploitable code.
A harmless payload can be absolutely trivially turned into a malicious payload.

I intend to share some details about this later on, but not so soon after the vulnerability is announced. There has to be a reasonable amount of time allowed for people to patch their servers.

Do you think not selling guns on an open market stops criminals from obtaining them as well?
If anybody thinks we're solving vulnerability full disclosure once and for all on an HN thread about a Rails vulnerability, that person is pretty naive.

We've now officially captured both sides of the argument and can safely move on.

If you want to get into silly analogies, compare the US to Australia. Tight firearms restrictions in AU makes it significantly harder for criminals to obtain guns.
Hmm, looking around I am thinking that if you don't want random object instantiation, this monkey-patch:

  module YAML; @@tagged_classes.delete('tag:ruby.yaml.org,2002:object'); end
makes user-supplied YAML a lot less dangerous. I am going to poke this into a production application and see if anything breaks - it really really shouldn't <g>
It breaks YAML deserialization in other places. You could enable and disable it on demand in the XML parser, but a more sensible solution is just to get YAML the hell out of the XML processor. Trying to make YAML safer is probably not the right approach.
It's meant to partially break YAML deserialization :) My apps do care about YAML, so I've an interest in cleaning this up. Is there some unintended consequence? You can still instantiate some Ruby classes (Regexp, Symbol etc.) in the YAML loader, or you can go through @@tagged_classes and pick out any other types you don't want.

But by taking out Object, YAML is only left with a whitelist of types that are safe, anything else will get turned into a YAML::DomainType.

What I mean by that is, this workaround breaks application code that depends on other portions of Rails that use XmlMini. In exchange, it allows you to potentially expose YAML to HTTP requests, which is still an extremely bad idea.
I don't see why YAML is a dangerous serialization format - the other type deserializations in the code seem sane and limited enough. (I wouldn't use YAML over e.g. JSON these days but I'm fixing up quite old projects)
This doesn't stop instantiating with !ruby/object
How doesn't it?

  irb(main):001:0> YAML::parse("!ruby/object:File 123").transform
  => #<File:0x7f2427848e68>
  irb(main):002:0>  module YAML; @@tagged_classes.delete('tag:ruby.yaml.org,2002:object'); end
  => Object
  irb(main):003:0> YAML::parse("!ruby/object:File 123").transform
  => #<YAML::DomainType:0x7f242783a840 @domain="ruby.yaml.org,2002", @type_id="object:File", @value="123">
That's neutered what it'll do without causing the parser to blow up.
For all its reputation as overly strict and pedantic, one of the good things Java did was make sure that if something looked like a string it was going to be a string...
I just read this today and it is clever and horrific at the same time: http://websec.wordpress.com/2012/01/04/multiple-vulnerabilit...
sql injection is possible, but how to work around to get remote code execution