[HackerNotes Ep. 61] A Hacker on Wall Street - JR0ch17

Justin is joined by Jasmin Landry to share some cool exploit chains, OAuth bugs and unexpected configuration file injection.

Hacker TLDR;

  • DOM Purify + Unpatched Chrome Bug: Chrome currently has an unpatched bug that applies referrer policies set in non-DOM attached <meta> tags. If you find a HTMLi or XSS and can put a meta tag in setting an insecure referrer configuration, even if it gets stripped by validation (such as DOM Purify) it may still be applied.

  • Unexpected OAuth Bugs: Hitting a refresh token endpoint without a refresh token at exactly the same time as another user requesting a refresh token can inadvertently leak the refresh token to both users.

  • Arbitrary ATO Via GraphQL: A broken access control in a GraphQL query which resulted in a chain allowing you to go from an external user from tenant A, take over tenant A (if you want to) and then pivot to other tenants and take them over. Always check access controls on GraphQL mutations, queries, and subscriptions if they are supported!

  • SSTI to RCE (Ansible Expressions): Sometimes, an SSTI can hide itself as a configuration file injection. Using SSTI payloads when fuzzing resulted in an injection within an Ansible configuration file, resulting in RCE.

  • MOAB (Mother Of All Bugs): Reading application documentation does pay off. Finding references of lesser-known W3 specs and content types in documentation resulted in mass XXE across a target. Full writeups are below!

If you aren’t familiar with Jasmin Landry aka JR0ch17, he’s a senior director at Nasdaq and a bug bounty hunter. Unexpected mix right?

He’s released some really good content for the community under BugCrowd and has done many bug bounty talks. If you haven’t seen them, you can check them out here and on Bugcrowd’s YouTube channel.

Not only that, he’s found some really neat bugs throughout his journey of bug bounty. Let’s jump in!

DOM Purify + Unpatched Chrome Bug

So, JR0ch17 found an HTMLI in a few places on a target but the app used the latest version of DOM purify. Trying to pop an XSS was tough, and resulted in JR0ch17 focusing efforts on DOM purify.

After figuring out it wasn’t worth the time, he shifted his efforts toward the OAuth flow of the application. He noticed path traversal was possible in the redirect_uri parameter on the flow of an app, allowing him to redirect to arbitrary paths on the app. From here, he used this to redirect to a page with the HTMLi present.

In attempts to read the the OAuth tokens, attempts were made to use img tags which worked, but the default referer policy in place meant only the origin was leaked.

Taking this a step further, it’s possible to change the referer policy on a page via the HTML meta tag. You can set a referer policy within the meta tag, which JR0ch used, to set it to unsafe-url where it’ll send the whole URL in the referer header.

Now, the weird part is that DOM Purify did strip this meta tag out. The payload never appeared in the DOM. Chrome, however, applied the referer policy set in the meta tag even though it was stripped.

Using this gadget and weird browser behaviour, JR0ch used the original idea of an img tag to then leak the token back out once the referer policy was overwritten.

JR0ch and the triage team investigated this and it's a Chrome issue that still hasn’t been patched, so keep this one on your list!

Unexpected OAuth Bugs

Although JR0ch17 focused on server-side bugs for a long period when hunting, he slowly developed a deep understanding of the OAuth flow and the amount of room there is for error in implementations.

If you aren’t familiar with OAuth, it can seem daunting to begin with. Auth0 does a great job of covering different use cases for different OAuth flows here, and PortSwigger has some really good material and content here.

If you’re more intermit with OAuth, a lot of knowledge can be gained from the OAuth security RFCs and some of Frans's Research - all great resources. JR0ch commonly references these when hunting and the good thing is, they provide a lot of attack vectors for each part of the OAuth flow. Now, we don’t like saying read the RFCs but in this case, READ THE RFCs!

Fortunately, the RFCs we’re referencing aren’t the full RFC docs for OAuth, only the security RFCs, but definitely check them out:

Now, he’s found a few bugs which aren’t your standard. In one instance, using a race condition like vector, a bug was found by continuously hitting a refresh token endpoint in an application WITHOUT a refresh token and just a client ID, which would return a valid access token. How?

Turns out, each time another user used the refresh token endpoint to retrieve a new refresh token (which is pretty common in OAuth) if he hit the endpoint at exactly the same time, the token was returned to both him and the user. Not your standard ATO right?

Arbitrary ATO Via GraphQL

An app offered similar functionality to Slack where you could add users to your workspace, and only grant access to a specific channel.

Another setting was similar, where an admin could add an external user to a tenant and the external user would only have access to the data granted.

The GraphQL endpoint on the app didn’t have proper auth checks, meaning the external user could make GraphQL queries. No data was returned at this point, instead an empty data object.

One GraphQL query supported an ID-related variable. Inputting a single quote to the variable generated a verbose error which disclosed an internal JWT. The scope of the JWT was set to ‘sysadmin’. It also leaked two headers, one related to the ID (used to identify the user’s tenant) and the current user ID which was the admin of the application.

The error disclosed an entire HTTP request happening in the background, leaking headers, CSRF tokens, JWTs and so on.

This was closed as there wasn’t enough impact shown. To prove impact, later on, reading through the docs revealed an endpoint where the admin token could be used which could be used to take over the admin account.

The header found in the error earlier (the tenant ID) could also be switched to other IDs which referenced other tenants. By removing the user ID header, and keeping the tenant ID header along with the admin token disclosed allowed for interaction with arbitrary tenants.

From the prior study on the docs - administrative endpoints existed which returned all users belonging to a tenant, along with their associated IDs.

Combine all of this and you have a chain which allows you to go from an external user from tenant A, take over tenant A (if you want to), and then pivot to other tenants and take them over. How’s that for impact?

SSTI to RCE (Ansible Expressions)

When encountering account signup forms, a good place to include an SSTI payload in the email field when hunting - ie attacker+{{payload}}@gmail.com

In one instance, an account was generated with an SSTI payload in the email in an app which allowed you to spin up WordPress instances. The app also provisioned a PHPmyadmin console to help manage the app.

Investigating the tables in the admin console revealed the user had actually been created with an email of [email protected] - this meant the SSTI payload had actually been evaluated during the process.

Now, this is where it becomes unexpected. The sink turned out to be in an Ansible config file, as the app was using Ansible in the background to bring up the instances for the users.

After refining POCs he used this knowledge to leak Ansible config and Ansible variables. Trying to prove more impact, Ansible supports the lookup command which allows you to execute underlying commands.

You can pass the string ‘pipe’ to the lookup to execute OS commands. There are a few restrictions on the characters here, so the payload was hex-encoded to bypass these restrictions.

Configuration file injection can prove to be incredibly impactful, but requires a deep level of knowledge on what you are injecting into. JR0CH went as far as spinning up Ansible playbooks locally to determine what payloads could be used.

MOAB (Mother Of All Bugs)

If you’ve listened to previous episodes, especially the one with Mayonaise AKA Jon Colston, you might be familiar with a MOAB (mother of all bugs). He classified a MOAB as:

  • No one else knows to look for it

  • It can be found on different hosts, functionality, and endpoints making it ‘ceasefire’ resistant

  • It has a rating of high or critical impact

When approaching a target and digging deep into the application, JR0CH was reading the application docs and found the app supported XML parsing. In some cases, the docs didn’t necessarily mention it explicitly parses XML.

An XXE had already been found on one endpoint, but from reading the docs, there were numerous references to support certain W3 standards. Turns out, these W3 standards were actually referencing lesser-known XML formats which almost ALL endpoints supported.

This resulted in mass XXE across a lot of endpoints on the target, due to support for lesser-known XML standards.

Always keep a lookout for custom parsing and custom formats. More often than not, there’ll be some unthought-about vulns lurking in there.

Equally, if the application has documentation available, as boring as it may be - we really do recommend digging into them!

Oneshot Jenkins RCE

JR0ch17 sometimes blurs the lines with some prior findings. In one instance, a contact form was used as a payload delivery mechanism which contained a link with a payload embedded.

When the user clicked on the link, the payload executed the payload which was actually a one-shot RCE for an internal Jenkins instance, executed from the user's machine.

Very cool bug but some programs might not be too happy with that one!

JR0ch clearly is a veteran in the bug bounty world - this episode was littered with nuggets of advice and bug stories.

Be sure to check out his Twitter and Bugcrowd interviews on YouTube channel for additional tips, tricks and breakdowns!

As always, keep hacking!