Perhaps this issue gets less press because every language calls this problem something different. In ruby, people call this mass assignment. In .NET and Java it’s often referred to as reflection binding. Regardless of name, it is how the object obtains it’s data which is of concern.
In ruby, vulnerable code might look like this:
@foo = Foo.new(params[:foo])
The params call wants to make life easy and will automagically map any form data that matches the object’s parameters for you—unless you say otherwise. This is a very common convention used in MVC frameworks, because manually mapping a form POST to an object is annoying. The problem here is that it makes no difference to the controller whether you’ve exposed that field in the presentation layer. It just has to exist on the object.
In other words-- if you were updating a product quantity for your shopping cart, you might be able to change the price by guessing that a price field exists. Just add the price field to your POST parameters and it might override the value. This approach can be effective—but it is mostly a guessing game at that point. Some frameworks let you throw tons of arbitrary data and whatever sticks, sticks. Others will barf on invalid parameters.
There is a second route, however, which is why vulnerability deserves more attention. When I said that you are allowed to map to anything on the object, I meant it. You can map complex objects to other complex objects, as far as they related to each other. Lets look at an example in C#:
public class Foo {
public string name { get; set;}
public Bar myBar { get; set;}
}
public class Bar {
public string name { get; set;}
public bool is_admin = { get; set;}
}
Two basic classes, foo and bar. Foo has a reference to bar. In MVC.Net, you bind a controller action to “create” Foo as such:
public class FooController {
[HttpPost]
public ActionResult Create (Foo foo){
/* save Foo to database */
return View();
}
}
Behind the scenes, the framework maps all of the form data directly into the foo object. Developers also sometimes do this directly by calling the UpdateModel() function. In either usage, if someone sent a malicious POST to the “Create” view:
Foo.Bar.name=“hello”&Foo.Bar.is_admin=true&Foo.name=“myfoo”;
You’d end up with a full fleshed out object where:
Foo.name = “myfoo”
Foo.Bar.name = “hello”
Foo.Bar.is_admin = true
The Bar object is instantiated automatically through it’s empty constructor, and it’s properties are mapped as well. Any reference the exposed object has, you can bind to. This also works for arrays of simple or complex types too. If instead of a single instance you had an array or List<Bar>
Foo.Bar[0].name=“hello”&Foo.Bar[0].is_admin=true
With out any other validations, this is all kosher.
In the wild I’ve used this attack to escalate privileges by updating my profile and walking down to a permissions table. I’ve also run across places where you could register every user to come to an event. And another instance where you could take over other people’s blog posts simply by editing your own profile.
If you search for this during tests, here are some key things I’ve learned:
- This vulnerability is best identified with access to source code—and very few developers seem to protect against it.
- When reviewing code, pay attention to how the constructor works and how fields are set on the object. Some properties are set via functions and you can’t bind them directly. Other objects don’t have empty constructors. This causes the attack to fail.
- I frequently find this vulnerability on “update” and “create” controller actions.
- You can, and I have, found this w/o source—its just harder. You do so by creating a loose type map through browsing the site.
- Going to the object's “create” page and note all the form fields that are there. That is your basic “object”. As you see these objects in other places on the site, they might reveal more about their structure.
- The site will guide you in what you need to know about object relationships. If you are looking at your cart, and it has a list of products & their details-- the cart object has a list of products.
- For everything else, there are common object relationships you can just assert. Carts do generally have products, just as people generally have permissions. Take some time and look over common object models on the interwebs.
It is true that developers can prevent this by white listing specific fields to bind—but they don’t. The whole point of the convenience functions is convenience. If you’ve built an MVC application and didn’t go out of your way to protect against this—you are most likely vulnerable to it.
Happy hunting.
-kuzushi