Stroom trusted the 'signer' field in AWS ALB headers without verification. Attackers can bring their own ALB signatures to log in as anyone, or inject malicious regions into the signer ARN to trigger SSRF against the AWS Metadata Service (IMDS).
A critical authentication bypass and SSRF vulnerability in GCHQ's Stroom data platform allows attackers to spoof AWS Application Load Balancer identities or reach internal AWS metadata services.
Stroom is a data processing and analytics platform open-sourced by GCHQ. Yes, that GCHQ. When you're building software for intelligence agencies, you'd expect the authentication mechanism to be bulletproof. Stroom supports offloading authentication to an AWS Application Load Balancer (ALB). This is a common pattern: let Amazon handle the messy OIDC handshakes and MFA, and just trust the headers passed downstream.
Here is the setup: The ALB authenticates the user, signs a JSON Web Token (JWT), and stuffs it into the x-amzn-oidc-data header. The backend (Stroom) receives this header, validates the signature, and grants access. Sounds secure, right? It relies on a chain of trust. The backend must verify that the token was signed by Amazon.
But there is a nuance here that turned into a gaping hole. Stroom didn't just check if the token was signed by Amazon; it asked the token which Amazon key signed it. And then it trusted the answer. This is the cryptographic equivalent of a bouncer asking, "Is this ID fake?" and believing you when you say "No."
To verify the x-amzn-oidc-data header, the backend needs Amazon's public key. The header contains a claim called signer, which is the ARN (Amazon Resource Name) of the Load Balancer that signed the request. Stroom's logic was effectively:
signer ARN from the incoming header.public-keys.auth.elb.<region>.amazonaws.com.Do you see the problem? The code verified that the signature was valid for some ALB, but it never checked if it was the ALB assigned to the Stroom instance.
This means an attacker can simply spin up their own AWS ALB in their own personal AWS account. They authenticate against their own ALB (which is perfectly valid), get a legitimate x-amzn-oidc-data header signed by Amazon, and send that header to the victim Stroom instance. Stroom sees a valid signature from a real AWS endpoint and rolls out the red carpet. It's an authentication bypass via trusted third-party spoofing.
But wait, it gets worse. Because the code extracted the region string directly from the signer ARN to build the URL, it also introduced a Server-Side Request Forgery (SSRF) vulnerability. If I control the string used to build the URL, I control where the server connects.
Let's look at the smoking gun in StandardJwtContextFactory.java. The vulnerability stems from trusting user input (the header) to determine security context.
The Vulnerable Code:
static String getAwsPublicKeyUri(final JwsParts jwsParts) {
// Extracting the signer directly from the untrusted header
final String signer = headerValues.get("signer");
final String keyId = headerValues.get("kid");
// Taint propagation: simple string split with no validation
final String awsRegion = signer.split(":")[3];
// Sinking the tainted data into a URL
return "https://public-keys.auth.elb." + awsRegion + ".amazonaws.com/" + keyId;
}If I send a signer like arn:aws:elasticloadbalancing:us-east-1:12345:loadbalancer/app/my-lb, it works normally.
The Patch (PR #4320):
The fix introduces two critical checks: an allowlist for the signer ARN and a regex for the region.
// 1. Validate the region matches a strict regex (prevention of SSRF/Path Traversal)
private static final Pattern REGION_PATTERN = Pattern.compile("^[a-z0-9-]+$");
// 2. Validate the signer against a configured allowlist
boolean isSignerAllowed = expectedSignerPrefixes.stream()
.anyMatch(prefix -> signer.startsWith(prefix));
if (!isSignerAllowed) {
throw new RuntimeException("Signer ... does not match any of the values in the expectedSignerPrefixes");
}This forces the admin to explicitly define which ALBs are allowed to sign tokens, closing the loop.
A sophisticated attacker has two distinct paths to victory here. Let's walk through the kill chain.
Scenario A: The Golden Ticket (Auth Bypass)
x-amzn-oidc-data. The attacker captures this header.amazonaws.com domain), verifies the signature, and logs the attacker in as a valid user.Scenario B: The Metadata Heist (SSRF)
signer field, instead of a region like us-east-1, they inject a payload designed to manipulate the URL.../../ sequences or special characters to break out of the domain validation.http://169.254.169.254/latest/meta-data/iam/security-credentials/.Stroom is a data pipeline tool. It ingests, transforms, and stores massive amounts of data. In a typical deployment (especially given its origin at GCHQ), this data is likely high-value intelligence or sensitive corporate logs.
Confidentiality Loss: The Auth Bypass allows an attacker to view all data within Stroom. They become an insider.
Integrity Loss: Stroom pipelines often involve data transformation logic (XSLT, scripting). An attacker with admin access could modify these pipelines to poison data or inject backdoors into downstream systems.
Cloud Compromise: The SSRF vector is the most dangerous for the hosting environment. Accessing the Instance Metadata Service (IMDS) allows the attacker to steal the temporary credentials of the role assigned to the Stroom server. If that role has S3FullAccess or EC2FullAccess, the game is over. The attacker pivots from the application layer to the infrastructure layer.
If you run Stroom, you need to patch immediately. The fixed versions are 7.2.24, 7.3-beta.22, 7.4.4, and 7.5-beta.2.
However, the patch alone isn't enough; you must configure it. The fix introduces a new configuration parameter: stroom.security.authentication.openId.expectedSignerPrefixes.
You must set this value. It takes a list of ARN prefixes.
stroom:
security:
authentication:
openId:
expectedSignerPrefixes:
- "arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-corp-alb/"Network Defense: Ensure your Stroom backend ports (usually 8080/8443) are not public. They should only accept traffic from the Security Group of your ALB. If an attacker cannot reach the backend directly to inject the header, the attack surface is significantly reduced.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
Stroom GCHQ | >= 7.2-beta.53, < 7.2.24 | 7.2.24 |
Stroom GCHQ | 7.3-beta.1 - < 7.3-beta.22 | 7.3-beta.22 |
Stroom GCHQ | 7.4-beta.1 - < 7.4.4 | 7.4.4 |
Stroom GCHQ | 7.5-beta.1 | 7.5-beta.2 |
| Attribute | Detail |
|---|---|
| CWE | CWE-290 (Auth Bypass by Spoofing) |
| CVSS | 9.4 (Critical) |
| Attack Vector | Network |
| Exploit Status | PoC Available |
| Impact | Authentication Bypass / SSRF |
| KEV Status | Not Listed |
Authentication Bypass by Spoofing
Get the latest CVE analysis reports delivered to your inbox.