Friday, May 31, 2013

Rails Strong Parameters



     It is well known in the Rails world, how big of an issue mass-assignment is. It is the vulnerability that led to the hack of Github last year. Normal interactions with an ActiveRecord model can lead to mass-assignment. A hacker can abuse an Model.new or Model.update_attributes, etc to change more attributes than expected by the developer. The most obvious example is a column that defines a user's role. If an attacker is able to mass-assign this value they could make themselves an admin. More information about mass-assignment is in a RailsCast linked below.

Creating a user without using mass-assignment.


An example of a mass-assignable create action.





















In Rails 2 and 3 to protect against mass-assignment you would set attr_accessible on your model. This would allow you to specify which attributes of a model could be mass-assigned.


Example model with attr_accessible set.








The example above will prevent mass-assignment of all attributes except for the name attribute. Hackers can no longer mass-assign themselves as admins! When used correctly, this solution has done a good job of protecting Rails applications from mass-assignment. While attr_accessible has proved to be a good solution to the issue but it's not as flexible as developers would like it to be. There are times when you want to update all attributes of a model, like when an admin is updating a user's record. In those cases, developers would have to do funky things with :as admin and pass additional parameters to ActiveRecord calls.

Complex models with varying authorizations would become hard to maintain. To address this and other issues David Heinemeier Hansson (@dhh) created strong_parameters. Strong_parameters tries to address the issue of mass-assignment in the controller instead of the model. Strong_parameters is available as a gem for Rails 2 & 3 but will be the default protection in Rails 4.


Creating user with strong_parameters enabled.










Now with strong_parameters instead of defining attr_accessible on the model you use the .permit during a call to an ActiveRecord call. As shown in the example above, we're permitting the assignment of name and admin (a boolean column). This protection is enabled on ActionController::Params. Many developers prefer this type of control to be in the controller and it reduces the complexity of the model. RailsCasts has a great pro episode that covers how to use strong_parameters.

The issue with strong_parameters is where this protection is enforced. Only ActionController::Params are protected with strong_parameters. Any user parameters that come through a controller will have this protection enforce. The security concern is when data comes from users and does not go through a controller and enters a model mass-assignment is possible. Any user parameters outside of a controller that are introduced to the model can be used to mass-assign.










In the example above a controller action is accepting user params in JSON format, parsing them and then using them in a call to User.new. The example above could be in a controller action used as an API endpoint that accepts parameters in JSON format. The issue is that when we parse the parameters we lose the protection of strong_parameters. We no longer need to call .permit on them to use them in a model and are now vulnerable to mass-assignment. Another example that could lead to mass-assignment is a file upload feature. A user provided CSV file could mass-assign attributes if the values aren't dealt with carefully.

Possible Protections

The most obvious choice to protect your application from mass-assignment when using strong_parameters is to wrap all user data in ActionController::Parameters before use in any models. While this is very easy to do, it makes the developer responsible for remembering to do this on every use of parameters. This could be easily forgotten in the heat of a quick patch. Other options for protecting against mass-assignment outside of controllers is strict parsing of incoming data. For instance calling .slice on a hash and selecting only the data you need. This again puts the onus on the developer to handle user data carefully.

Rails 4 and strong_parameters help developers create more tightly defined authorization rules for interacting with models but at the cost of some security. We can no longer rely on ActiveRecord to defend us against mass-assignment and must be very aware of what data is passed to models. While this is a step forward for usability, it seems like a step backwards for security.


Thanks to Brendon Murphy for bringing this issue to light.





Mike

Thursday, May 23, 2013

Funky Juniper URLs


If you've ever tested any clients that have Juniper VPNs you've probable seen the ol: 

http://[target]/dana-na/auth/url_default/welcome.cgi URL.

@infosecmafia and I mentioned in our DerbyCon talk on how you can sometimes find extra or test URLs that are also valid URLs for the Juniper VPN. The example we used was where the url_default required secret questions but url_8 or whatever did not because it was a test URL the admins had set up.

Soooooooo, its worth running a quick check if you come across one. I wrote a  Metasploit auxiliary module to do this. Pretty simple, it just runs thru url_0 through url_100 and prints out the 200 replies. looks like so:

–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_0/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_1/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_2/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_3/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_4/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_5/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_6/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_8/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_9/welcome.cgi
–[+] 192.168.1.1:443 Received a HTTP 200 with  bytes for /dana-na/auth/url_12/welcome.cgi

Seeing these doesn't ALWAYS mean you have a multi-factor bypass but its worth checking out if the main site is multi-factor.

Random example:
url_default

url_3

url_8

url_10


Available on my github repo until I get around to doing a pull request.

-CG
CG