top | item 5167508

Anatomy of an Exploit: An In-depth Look at the Rails YAML Vulnerability

50 points| thinkbohemian | 13 years ago |rubysource.com | reply

24 comments

order
[+] wyuenho|13 years ago|reply
Ok, at risk of turning this post into a flame war, I feel I'm obligated to call out what it is:

    Ruby devs: stop making excuses!
Security is hard, but there are also a lot of very simple and effective ways to minimize vulnerabilities, e.g. a mere excising of your cerebral cortex for a few minutes before you make a decision is a very good way in preventing security vulnerabilities from being introduced in the first place.

The mere fact of deciding to default to using YAML as the default JSON parser without disabling arbitrary object construction is a mind-numbingly dumb decision. What's more fucked up is the fact that this commit actually made it into the code base, and it's not even a recent decision. Apparently it's been in the code base as far back as the 2.3 days. Basically, thousands and thousands of Rails devs have looked and used this "feature" and believed it's not a bug. This is a systematic and cultural failure and crime against reason perpetrated by the Rails community, which also happens to be a vast majority of the Ruby community. It's time you guys learn some proper security 101.

[+] bradleyland|13 years ago|reply
I think you're being downvoted because you set up a bit of a strawman there. By my read, the central focus of this article is to explain a very dangerous vulnerability in an accessible way. It didn't come across as having made a lot of excuses.
[+] derricki|13 years ago|reply
One of the best lines is: "Security vulnerabilities are bugs at [frameworks' and languages'] core, and very difficult to detect." It's a good illustration of why "security" can almost never be a "feature" added on top of software. I try to help my teams understand that security is an emergent property of the system. Thus, if a system is to be secure, it must be accounted for throughout the design and development of a product.

I once saw an application that used Java's standard PRNG to create session IDs used for authentication after a user logged in. They tried to fix the fundamental security flaw by appending a time stamp, and then hashing that value. The powers at the time didn't believe it was a vulnerability until I did some white hat hacking. I wrote a simple app that retrieved a couple of session IDs and calculated the seed of the random number generator from them. It was a simple step then to guess session IDs of subsequent logins and impersonate those users. They quickly moved to replace the whole design with a fundamentally more secure solution.

[+] DHowett|13 years ago|reply
> This is desired functionality by the creators of YAML, since it gives developers the ability to write and read Ruby objects to disk, like an object database.

It is? I don't think the creators of YAML exactly had Ruby in mind.

This isn't a problem with YAML, it is a problem with how the Ruby/Rails stack handles YAML.

[+] thinkbohemian|13 years ago|reply
"In contrast, YAML’s foremost design goals are human readability and support for serializing arbitrary native data structures."

- YAML Spec: http://yaml.org/spec/1.2/spec.html#id2763035

It isn't an issue with Ruby's implementation, but rather the spec itself.

You're splitting hairs here, and ignoring the rest of the article. YAML isn't evil or a "problem" per say. It's only when you pair YAML with un-sanitized user input that we get into problems.

[+] zopa|13 years ago|reply
> This behavior isn’t inherently unsafe, after all we had to manually build our exploit string and manually instantiate our class. The problem only comes when we put all of these things together.

This is very much the wrong way to think about security. Weakness will be exploited. An attacker only has to find one path past your defenses; you have to guard every possible door. Saying, "look how strange and unlikely the interaction of these weaknesses was," misses the point. Code interacts lots of ways, and if it hadn't been this interaction, it could have been another.

Treating user data as untrustworthy is a good start, and you should do that. But you also ought to make each piece of your code base secure and robust, even if you can't think of how it would be exploited, now.

[+] thinkbohemian|13 years ago|reply
That was my implied point of the whole article. An attacker can use your code against you in unexpected ways.
[+] jiggy2011|13 years ago|reply
It's probably difficult to think of all of the ways in which your code base can be exploited from the get-go.
[+] jiggy2011|13 years ago|reply
I'm a ruby noob, but if I read this right it means that when you deserialize a YAML string into an object it will also create methods from ruby code inside the YAML string?

Does a serialization format really need this functionality? it strikes me as something that is clearly dangerous and you would be better simply to deserialize data members?

Is it a common requirement to pass code around as strings?

[+] Cushman|13 years ago|reply
You aren't exactly reading it right. The string is deserialized as a string, specifically as a hash key. However, there are a number of classes in Rails (and presumably other Ruby projects of similar complexity) which can be tricked into evaluating string properties as code when they are created, and ActionDispatch::Routing::RouteSet::NamedRouteCollection is one.

The general lesson taken is not that it is dangerous to ever use eval in any class ever, because that is really useful, but that it is dangerous to allow arbitrary instantiation of classes, because that's pretty obvious even without this exploit. For example, even without code execution you could use this same trick to DoS the server by instantiating a bunch of heavy classes which won't get garbage collected.

[+] rykov|13 years ago|reply
Rather than creating new methods for a class, YAML.load can be used to call one of a few specific methods ([]=, init_with, or yaml_initialize) for the specified class. This exploit found a class where string arguments to the []= method are inserted into an eval() block, thus becoming code.

A few more details here: http://blog.gemfury.com/post/42259456238/rubygems-vulnerabil...

[+] static_typed|13 years ago|reply
Security and good software engineering principles are perhaps not the first focus in the Ruby community, and that is a shame. Yes it is nice to have cool frameworks with lots of magic, and we can create a blog in one command. But none of that matters when you end up in infinite patch cycle. Remember poor design choices mean technical debt for a long time.
[+] hawleyal|13 years ago|reply
def double(number) return number * 2 end

double(10) # => 20

double("foo") # => "foofoo"

It's called polymorphism. `*` is a method.

Learn how the language works before you write an article about it proclaiming your ignorance.