Show Notes

219 - Rapid Reset, Attacking AWS Cognito, and Confluence Bugs

Rapid Reset is a Denial of Service (DOS) attack that abuses the HTTP/2 RST_STREAM frames functionality. Where HTTP/1.1 is text-based with the more familiar header/body messages, HTTP/2 uses binary multiplexed streams, where frames are sent back and forth consisting of data and flow-control frames. Furthermore, you can open multiple streams on one connection, where multiple streams have requests and responses going simultaneously, as opposed to the serialized request/response in HTTP/1.1. One of the control frames that’s supported is RST_STREAM or reset stream, which can be used to ‘cancel’ a stream and cease processing any further frames. The problem is that an attacker can just continuously bombard the server with these reset stream frames on parallel streams, as the server will be flooded with processing the reset requests. This ultimately starves the CPU, which can bring down the server’s ability to respond to further requests.

A straightforward path traversal issue in NodeJS. Node’s fs functions allow specifying paths in the form of either strings or Uint8Array objects. It seems the support for Uint8Array is for the Buffer class which extends from Uint8Array. Node’s new experimental permission model will check for path traversal in the fs function family. However, while strings and Buffer objects are checked, Uint8Array objects simply aren’t and can bypass the path traversal checks.

A two-part blog series by Rhino Security Labs that talks about common issues seen in deployments using AWS Cognito, and how they automated testing and attacking of said issues. AWS Cognito is a front-end solution by AWS for user auth and authorization, typically used for web and mobile apps. Cognito has this concept of “pools”, where you have a user pool for managing login and registration information (essentially an OpenID Connect Identity Provider), and an identity pool, which generates temporary AWS credentials for a user to access various assets stored on AWS. Due to insecure defaults, it’s easy to have problematic configuration issues that can give an attacker a lot of information, including IAM credentials.

Potential issues

  • Client, user pool, and identity pool IDs are commonly found in HTTP responses, which can be pulled by an attacker to obtain IAM credentials
  • Cognito’s user registration is left open by default, allowing an attacker to use the client and user pool IDs to register even if the application doesn’t export a login or registration portal
  • Custom attributes used for Role-Based Access Control (RBAC) can be configured by the user by default
  • Other attributes such as the email can be case-sensitive, which could allow account takeover if the backend isn’t case-sensitive

Rhino Security Labs used these findings to develop modules for their Pacu AWS exploitation framework called cognito__enum and cognito__attack. The enumerator will attempt to send requests and parse responses to pull user pool and client IDs, and check attributes such as the password policy and Multi-Factor Authentication (MFA) config to alert if MFA isn’t required or if the password policy is weak, and it’ll try to enumerate identity pool IDs to see if access keys and session tokens can be obtained.

The attack module will try to leverage those IAM credentials to perform various actions, such as registering an account, testing for registering with emails with different cases, and assuming roles to escalate privileges.

An interesting vulnerability was found in confluence that allowed for calling semi-arbitrary methods chains on the Action class being executed. This reminds me a bit of deserialization attacks, but in this case you’ve got the Xwork2 framework providing a SafeParametersInterceptor class. This class is responsible for translating parameters from the request URL into Setter calls. So for example a URL parameter like example.test=hello would let to a call of the following method chain on the handling Action class. .getExample().setTest("hello").

Giving an unauthenticated attacker the ability to cause somewhat arbitrary method calls. Similar to a deserialization attack where you’re trying to find the right object to instantiate, in this case you need to find the right type of method to instantiate, perhaps chain of methods to dig your way up through the classes available and to some interesting primitive. In this case this was exploited by setting the following URL parameter: ?bootstrapStatusProvider.applicationConfig.setupComplete=false the resulting method chain ends up calling into the application’s configuration to call setSetupComplete(false) reenabling access to the setup functionality of the application. Which unsurprisingly gives some fairly powerful functionality, allowing an attacker to create a new administrative user.

Log injections are a class of bug that is often overlooked, both because it is difficult to spot during a black box engagement, and because the impact is difficult to determine. IN this case though the impact was easier to spot because the program processing the output logs was part of the same application. Effectively Cosmos SDK would run a watchdog application that would watch the standard output of the main application, certain output log strings could trigger the watchdog application to perform certain actions. Most interesting to the author here was a string that would result in the watchdog downloading and executing a new binary. This sort of functionality just sounds dangerous, so the author set out to prove first that a simple log injection would be all that was needed by introducing a vulnerable log statement.

Once the vulnerability concept was proven it was time to hunt for an actually vulnerable log statement. Unfortunately most log statement would escape the quote characters in the part of the log that attackers could easily control. With the quotes being escaped the watchdog wouldn’t recognize the trigger statement. Ultimately they did find one rather odd logging statement. that would attempt to print a message in the usual logger format, but it would do so merging the whole statement into the description parameter, which is not escaped, and usually would be a fixed string not influenced by an attacker.

Honestly this logging statement feels very odd to me, perhaps a remnant of older code before the logging system added support for the nice printing of key/values but regardless, it works great for an attacker who can inject their a fake upgrade command into the output and get arbitrary code downloaded and run. Its a rather fun bug in my opinion and as the author points out, while it is technically a Web3 bug, it feels very much like a more standard application security bug.