/security_examples

Rails application to demo CSRF, XSS, and XXE vulnerabilities and how to avoid them

Primary LanguageRuby

Security is Broken: Understanding Common Vulnerabilities

This is an application to accompany my "Security is Broken: Understanding Common Vulnerabilties" talk. To learn more about where I'm giving this talk visit my website.

In the master branch it demonstrates three common, well-known vulnerabilities; CSRF, XSS, and XXE. Then in each "patched" branch I explain ways to fix and avoid the vulnerabilities.

These examples are relatively basic and are intended to be usable for those who don't necessarily use Rails often.

Setting Up the Application

You will need to have Ruby 2.2.2 installed because it's required by Rails 5. I used the most recent version of Rails to demonstrate these vulnerabilities because they are ones that have existed for a long time, but it is still relatively easy to accidently expose CSRF, XSS, and XXE attacks if you don't know about them.

You don't need any special databases because this application uses the default; SQLite3.

After you have forked and cloned the application to your environment run the following:

bundle install
rails db:setup

Start the server

rails s

CSRF: Cross-Site Request Forgery Attack

Exploiting

A CSRF attack uses a malicious site to trick a browser with an active session on another site into performing an unwanted action.

For example, you have a profile on a website that requires authentication to edit; for example Facebook or Twitter. If that website isn't checking the authenticity of requests made to sensitive endpoints, it's possible for an attacker to hijack a request and make changes to the victim's profile.

To exploit this with the provides application start the server with rails s.

Then open the file in public/csrf_attack.html in the browser.

Because the file is setup to automatically submit you should see a redirect to the localhost:3000 domain notifying you the email address was updated. The email address of the victim user, user with ID 2, will be changed from "victim@example.com" to "changed@example.com".

This is just a demonstration of how this could work. Because of browser security protocols and application defaults in other apps this may be difficult to reproduce on a real production application.

Mitigating

Mitigation is relatively easy if you're using an application like Rails. Many modern frameworks including Django, Java's Spring, Rails, and .net all have built in CSRF protection.

To enable CSRF protection in Rails add forgery protection to your ApplicationController to protect sensitive requests. Rails does not protect GET requests because sensitive requests should never be sent as GET requests.

Add the following to your ApplicationController

class ApplicationController < ActionController::Base
  protect_from_forgery, with: :exception
end

And add the meta tag to your HTML head

<%= csrf_meta_tags %>

Resources

OWASP Neal Poole's Blob
pentestmonkey.net
Per-form CSRF token PR for Rails

XSS: Cross-Site Scripting Attack

Exploiting

XSS attacks inject malicious JavaScript into trusted websites. XSS is easily exploitable if your application is displaying or evaluating user input that is not sanitized by the application.

For purposes of this demonstration we're going to focus on stored XSS. Stores XSS means the attacker inserts a malicious script into your database or filesystem that will be executed when the victim visits the page containing that script.

To exploit XSS with this demo application start the Rails server with rails s.

Visit to "hacker"'s profile page at http://localhost:3000/users/1 and you will see an alert on visit that comes from the users name. The reason this is exploitable is becasue we are running html_safe on the user inputted string.

There is a second XSS example on the page in the user's webiste. Using the javascript:// scheme the attacker can execute JavaScript when the victim clicks the website in the profile URL. Because we are using a link_to to obscure the real url the victim may not realize that this specially encoded URL is harmful. Clicking "website" will execute the javascript after the %0A encoding which signals a line feed, aka move to the next line.

javascript://example.com/%0Aalert(1)

Mitigating

By default Rails tries to protect programmers from allowing XSS injection, but if you purposely enable it, Rails can't protect you.

Never run html_safe on user inputted strings without some kind of santizing first.

If you must allow HTML in use input, utilize a sanitizing library like the one that comes default with ActiveSupport.

<%= sanitize(user.name) %>

This will allow a customizable whitelist of tags but also continue to protect you from XSS by ensuring that the img tag does not allow the onerror attribute.

You can also validate user supplied data before inserting it into the database.

The following validator for the user website checks that the only schemes that our application allows are http and https. Any websites with another scheme will be rejected with an error. You should always use a URI parsing library over a regex for this because it's easy to get regex wrong, but a parsing library is widely used and tested.

class User < ActiveRecord::Base
  WHITELISTED_URI_SCHEMES = %w( http https )

  validate :check_uri_scheme

  private
    def check_uri_scheme
      begin
        uri = URI.parse(website)
        unless WHITELISTED_URI_SCHEMES.include?(uri.scheme.downcase)
          errors.add :website, 'is not an allowed URI scheme'
        end
      rescue URI::InvalidURIError
        errors .add :website, 'is not a valid URI'
      end
    end
end

Resources

OWASP
OWASP Cheat Sheet
Blackhat Presentation by Eduardo Vela and David Lindsay

XXE: XML eXternal Entity Attack

Exploiting

If an application has an endpoint that parses XML an attacker could send a specially crafted payload to the server and obtain sensitive files. The files the attacker is able to obtain depend heavily on how your system is setup and user permissions are implemented.

Using the xxe.xml payload file in this repository we can send a cURL request to the create users endpoint which accepts XML.

curl -X 'POST' \
     -H 'Content-Type: application/xml' \
     -d @xxe.xml \
     http://localhost:3000/users.xml

The payload will collect the secrets.yml file and set it as the user's name when the request is sent. The secrets.yml file will be inserted into the database as the users name and returned to the attacker.

Mitigating

  • Don't parse XML if it's not an application requirement
  • Don't use a library that has supports entity replacement (LibXML). Use the built in default instead; REXML
  • Ensure entity replacement is disabled. New versions of LibXML make it hard to enable entity replacement. You may still be vulnerable to a DoS attack when using LibXML.
>> LibXML::XML.default_substitute_entities
>> false
  • Whitelist known external entities

Resources

OWASP
Software Engineering Institute, Carnegie Mellon
LibXML Example
SANS Hands-On XML External Entity Vulnerability Training
ColeSec Security