All about Paperclip’s CVE-2017–0889 Server Side Request Forgery (SSRF) vulnerability

Some folks might have received an alert from Github on 01/22/2018 related to a high risk vulnerability (CVE-2017–0889) in the Paperclip gem. The fix for the issue is now available in version 5.2.0 of the Paperclip library. This post goes into the details of the vulnerability’s root cause, how its exploited, the impact and some recommendations for fixing the issue.

The blog post will not go into the specifics of Server Side Request Forgery (SSRF). If you would like to catch up on this type of vulnerability, I recommend the following references:


Issue Description

The Paperclip library has a concept of ‘IO adapters’ that provide multiple ways a ‘file’ can be passed to the Paperclip library so that it can do what it does best. The vulnerability affects two of Paperclip’s IO adapters that accept URLs as attachment data:

When these adapters are used, Paperclip acts as a proxy and downloads the file from the website URI that is passed in.

The ‘http_url_proxy_adapter.rb’ is invoked if the provided file/attachment data starts with ‘http://’ or ‘https://’. This behavior is not currently documented by Paperclip and the library does not perform any validation to protect against Server Side Request Forgery (SSRF) exploits.

Sample Vulnerable Rails code using Paperclip:

The following code example shows a Rails controller that takes in an ‘attachment’ request parameter and passes to the ‘GenericAttachment’ model that has declared Paperclip’s ‘has_attached_file’ method.

class GenericUploadController < ActionController::Base
..snip..
    def create
GenericAttachment.create(:attachment => params[:attachment])
..snip..
end
class GenericAttachment < ActiveRecord::Base
has_attached_file :attachment

Proof of Concept Exploit

Typically, the ‘attachment’ multipart form parameter would contain the file contents of the file (either binary or base64 encoded). To trigger the vulnerable adapter you would instead submit a URL.

POST /upload HTTP/1.1
Host: example.com
..snip..
— — — WebKitFormBoundaryGB91jEIcBxpCcDww
Content-Disposition: form-data; name=”attachment”;
http://169.254.169.254/latest/

The URL submitted in the ‘attachment’ parameter would be parsed by the ‘http_url_proxy_adapter’ and the AWS EC2 metadata IP, 169.254.169.254 (non-routable outside of the AWS EC2 instance), would be requested and stored as the attachment file. Depending on the vulnerable application and allowed content-types, it may be possible for an attacker to recover this response data by leveraging other features in the application that allow viewing of the file data.

The ‘uri_adapter’ IO adapter (http_url_proxy_adapter inherits from uri_adapter), is invoked when the attachment data is a ‘URI’ class type. A example snippet of vulnerable code would look something like the following:

class GenericUploadController < ActionController::Base
..snip..
    def create
GenericAttachment.create(:attachment =>
URI(params[:attachment]))
..snip..
end

Unlike the ‘http_url_proxy_adapter’, the ‘uri_adapter’ behavior is documented here. The adapter is susceptible to SSRF if an unvalidated URL is accepted from user input, set as a URI object and set as the attachment data on the model.

What’s the impact?

The ‘http_url_proxy_adapter’ can be invoked directly from HTTP request parameters and therefore all applications that use Paperclip without additional validation of user input would be susceptible to SSRF attacks by default. The ‘uri_adapter’ could still be exploited to perform SSRF attacks, but this vulnerability would need to be introduced by a developer since a URI object must be created and passed in as the attachment data.

Server-Side Request Forgery (SSRF) vulnerabilities could be exploited to:

  • Perform GET requests to hosts located on the internal network or local interfaces
  • Steal AWS access tokens by performing requests to EC2 metadata servers
  • Perform requests to hosts on the internal network that do not require authentication
  • Retrieve the requested data if the attachment is retrievable within the application
  • Perform GET requests to internet accessible systems using the vulnerable server’s egress IP
  • Port scan internal network

SSRF vulnerabilities give attackers a pivot point into the victim’s internal network and could typically be abused to expand access to the victim’s network and steal secrets.


So.. how do I fix it?

A commit was pushed on 01/23/2018 to the master branch that removes the ‘http_url_proxy_adapter’ and ‘uri_adapter’ by default. You should update your Paperclip gem to version 5.2.0 to pickup the security fix.

If updating the gem is not possible, you can monkey patch the library to remove the vulnerable URI adapters. The following code snippet was provided in an Issue Ticket in the Paperclip Github repo.

worrisome_adapters = [Paperclip::UriAdapter, Paperclip::HttpUrlProxyAdapter, Paperclip::DataUriAdapter]
Paperclip.io_adapters
.registered_handlers
.delete_if do |_block, handler_class|
worrisome_adapters.include?(handler_class)
end

What if my application needs to use these adapters?

If disabling the adapters is not an option since it’s currently used by your application. You should consider implementing strict URL validation routines on accepted URLs that:

  • Makes sure that you are properly handling DNS resolution for IP validation
  • Deny local, private or non-routable IPv4 and IPv6 CIDRs

I highly recommend not writing your own URL validation since it could lead to missing some edge cases and re-introducing the issue. Check out this helper library:

Unfortunately, URI validation alone is not sufficient to prevent exploitation. Paperclip’s URIAdapter class uses OpenURI.open to download the file content. The open call will seamlessly handle HTTP redirects and therefore provides attackers with a way to bypass your URL validation checks.

The bypass would look something like the following

POST /upload HTTP/1.1
Host: example.com
..snip..
— — — WebKitFormBoundaryGB91jEIcBxpCcDww
Content-Disposition: form-data; name=”attachment”;
http://www.somevalidurl.com

The submitted attachment URL would pass our validation check and Paperclip would request the URL to download the file content. However, the attacker controlled site could then return a 302 Redirect that points to an internal address.

HTTP/1.1 302 Moved Temporarily
..snip..
Location: http://169.254.169.254/latest/

OpenURI.open would seamlessly handle the redirect and your validation routine would not be applied to the value returned in the ‘Location’ header.

Patching the bypass will require an additional monkey patch. You can patch the download_content method with the following:

module PaperclipUriAdapterSafe
def download_content
RoutableUrlValidator.open_http(@target)
end
end

Paperclip::UriAdapter.send(:prepend, PaperclipUriAdapterSafe)

RoutableUrlValidator would contain the logic for validating the URI and any redirects performed by the server. The following code snippet shows an example of how you could validate redirects and also set a maximum number of redirects:

def open_http(name, *rest, &block)
max_redirect_tries = 3
options = rest.extract_options!
options[:redirect] = false
rest << options

begin
uri = validate_and_parse_url(name)
open(uri, *rest, &block)
rescue OpenURI::HTTPRedirect => redirect
name = redirect.uri.to_s
retry if (max_redirect_tries -= 1) > 0
raise
end
end

Wrap it up already!

I hope this blog post was helpful in clearing up some of the questions regarding the vulnerability and contains some useful tips for preventing exploitation in the event you need to use a URI adapter within your application.

Disclosure Timeline

03/28/2017 — Issue reported to Thoughtbot via their security@thoughtbot.com address. They acknowledged receiving the report and are having an internal thread to discuss it

03/31/2017 — Thoughbot responds that the next release of Paperclip will feature all adapters deactivated by default with a documentation update to be included. The updated documentation will include considerations for “privacy concerns” around the ‘uri_adapter’. They estimate that it will be finish in the coming weeks

04/18/2017 — We request CVE-2017–0889 through HackerOne

04/23/2017 — Pull request opened to fix the issue

06/23/2017 — We reach out to Thoughtbot for an update for the fix. We receive no response back

08/07/2017 — We reach out to Thoughtbot for an update again. We receive no response back

11/13/2017 — The CVE-ID is added as a comment to the Pull Request (PR) by HackerOne employee which now makes the vulnerability details public

11/17/2017 — I respond to the PR stating that the CVE details were made public due to this comment and ask for a release date since there is no information or fix available to users. We receive no response back

01/22/2018 — Github’s vulnerability scanning begins flagging the Paperclip gem in repositories due to the vulnerability details now being linked to the CVE-2017–0889.

1/23/2018 — Commit is pushed to master containing a fix that removes the URL based UI adapters. Release version 5.2.0 was tagged containing the security fix.

Shout outs to:

  • Jobert Abma (@jobertabma) from HackerOne for submitting the SSRF issue to us via our Bug Bounty program and helping to report the vulnerability
  • Juan C. Müller (@juancmuller) from Greenhouse Software for fixing the bug in our application and helping to report the vulnerability.