Six security vulnerabilities from a year of HackerOne

HackerOne stats as of 6/27/2017

We launched our HackerOne program a year ago to increase the security of Flexport. HackerOne allows us to provide hobbyist and professional penetration testers a means to find vulnerabilities and motivation to do so through bounties. Since then we have received nearly 200 reports ranging from removing server tokens from nginx headers to XSS vulnerabilities. Here are 6 of the most interesting vulnerabilities from those 200 reports.

1. XSS in delete buttons

When launching our bug bounty problem, we did not expect to have any valid reports about XSS. After all, React has built-in protections that prevent such vulnerabilities, right? Unfortunately, the first non-trivial report we received was about a stored XSS vulnerability.

Credit to Geekboy for the image

The cause:

At the time we were using Bootbox as a quick and dirty tool to display error messages and create confirmation dialogs. Bootbox manages its DOM elements independently of React and therefore doesn’t benefit from React’s XSS protection. As a result, when directly putting user input into the confirmation dialog, we opened ourselves up to attacks.

The fix:

The short-term fix was to strip all possible XSS-related tags before passing any user input to Bootbox (JSXSS provides a node module to make this part easy). A longer-term, in-progress solution is to move away from Bootbox and to a React-based confirmation modal.

The lesson learned:

Just because React stops XSS doesn’t mean that all code is safe. Be distrustful of all libraries that work outside of React and avoid them if at all possible.

2. XSS in Markdown processing

After fixing Bootbox and checking all our other similar libraries, we received our next report of an XSS vulnerability — this time in our Markdown rendering.

The cause:

We supported Markdown in our text boxes with <div dangerouslySetInnerHtml={{__html: marked(text)}} />. In retrospect, this was clearly a bad idea.

The fix:

Wrap all text passed in to dangerouslySetInnerHtml with an XSS filter and create a Lint rule to enforce this in the future.

The lesson learned:

When using any feature that has the word dangerous in it, treat it seriously…

3. Target=“_blank”

Of all the reports we have received from HackerOne, the most surprising came from normal use of standard HTML tags.

Credit to Geekboy for the image

The cause:

When you open a link in a new tab ( <a target="_blank"> ), the page that opens in the new tab can access the initial tab and change its location using the window.opener property. An attacker can then set the original page to be a login page or anything else. This can only be mitigated by adding rel="noopener noreferrer" to the <a> tag.

The fix:

We fixed the original issues by accompanying all uses of target="_blank" with rel="noopener noreferrer" so that the new window cannot change the original. In addition, we submitted a Lint rule to ESLint to prevent us and others from making the same mistake in the future.

The lesson learned:

The key takeaway from this vulnerability is that security is hard. It’s very tempting to trust standards like HTML, and it is important to remain vigilant and skeptical.

4. Wordpress woes

After fixing the aforementioned vulnerabilities, we didn’t receive any more front end–related vulnerabilities. Our HackerOne journey, however, continued on with our blog. Our company blog runs on Wordpress, and we received various vulnerability reports exploiting that fact.

The cause:

Every blog vulnerability boiled down to the same problem: Out-of-date libraries are vulnerable libraries. For example, JetPack, a widely used (3+ million installs) and recommended plugin that promises to “keep any WordPress site secure, increase traffic, and engage your readers” has had many XSS and other vulnerabilities over the past few years.

Taken from https://wpvulndb.com

The fix:

Like any software, the least vulnerable code is non-existent code followed by up-to-date code. We removed the vast majority of the Wordpress plugins (for most of them, nobody knew when/why they were installed), updated the rest, and subscribed to https://wpvulndb.com/ to keep up to date with new reports.

5. Brute-forcing 2FA

Moving to our Ruby on Rails back-end, we received 2 reports worth noting, both involving our two-factor authentication. First, we received a report showing a brute force attack to gain access to a compromised account.

The cause:

We use Authy for our 2FA partner, and their rails gem does not include any built-in rate limiting.

The fix:

This fix was pretty straightforward: We added rate limiting that locks the account after too many incorrect attempts.

6. Bypassing 2FA

Finally, we received a report showing a complete bypass of our 2FA, rendering the second factor completely useless. All the attacker had to do was ignore the 2FA page and navigate to a different link.

The cause:

Of all the bugs in this post, this was the hardest to track down. The Authy rails gem hooks into Devise (a popular Rails authentication/user management library), and used the following code to require 2FA after logging in:

def check_request_and_redirect_to_verify_token
...
id = warden.session(resource_name)[:id]
warden.logout
warden.reset_session!
session["#{resource_name}_id"] = id
...
redirect_to verify_authy_path_for(resource_name)
end

In theory, this code would, after a successful login, log the user out and redirect them to the verify second factor authentication page. In practice, however, the Devise check to see if a user is authenticated (which runs after the code presented here) makes a call to authenticate?:

def authenticate?(*args)
result = !!authenticate(*args) # Try to log the user in
yield if result && block_given?
result
end

Which will log the user back in.

The fix:

Changing the warden.logout line to sign_out fixes the issue since sign_out has additional code to clear out the login. We fixed the issue locally and filed a pull request with Authy to fix it for everyone.

The lesson learned:

Even reputable security companies get it wrong sometimes, and there is no good substitute for penetration testing. For us, the most cost-effective approach continues to be HackerOne. We have found the reports to be of high value to Flexport and our security.