Bypassing Strict SSH Server Verification via Connection Pool Reuse in libcurl

Disclosed: 2026-03-31 21:48:48 By whitehat411 To curl
Unknown
Vulnerability Details
## Summary There is a logic flaw in how libcurl manages its connection pool for SSH protocols (SFTP/SCP). When evaluating an existing connection for reuse, `ssh_config_matches()` (in `lib/url.c`) fails to compare server identity verification policies. By ignoring `CURLOPT_SSH_KNOWNHOSTS`, `CURLOPT_SSH_HOST_PUBLIC_KEY_MD5`, and `CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256` during the matching process, libcurl allows a handle with strict security requirements to reuse an existing, unverified, or MITM'd connection established by a previous relaxed handle. ## Technical Details ### The Flaw In libcurl, connection reuse is governed by protocol-specific matching functions. While the TLS backend strictly enforces that security configurations (like `verifypeer`) must match between handles for a connection to be reused, the SSH backend lacks these checks. In `lib/url.c`, the `ssh_config_matches` function only compares client-side RSA keys: ```c static bool ssh_config_matches(struct connectdata *one, struct connectdata *two) { struct ssh_conn *sshc1, *sshc2; sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN); sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN); return sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) && Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub); } ``` It entirely omits the server verification policy options. If `Handle A` connects without host key verification and `Handle B` connects with strict verification, libcurl sees them as a "perfect match" because their client keys (often both NULL) are identical. ### Attack Scenario 1. **Handle A (Relaxed):** A routine connects to `sftp://example.com`. It does not enforce a `known_hosts` file. An attacker performs a Man-in-the-Middle (MITM) attack; the connection is established and added to the pool. 2. **Handle B (Strict):** A separate routine handles sensitive data and connects to the same host. It explicitly sets `CURLOPT_SSH_KNOWNHOSTS` to a trusted file to prevent MITM. 3. **The Bypass:** `ssh_config_matches()` evaluates the connection from Handle A. Since the client credentials match, it reuses the connection. 4. **Impact:** Handle B bypasses the handshake entirely. It communicates over the MITM'd connection, ignoring the strict security policy set by the developer. ## Proof of Concept (PoC) The following C program demonstrates the bypass using a shared connection cache (`CURLSH`). ```c #include <curl/curl.h> #include <stdio.h> int main(void) { CURL *h1, *h2; CURLSH *share; long connects = 0; curl_global_init(CURL_GLOBAL_DEFAULT); share = curl_share_init(); curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); h1 = curl_easy_init(); h2 = curl_easy_init(); if(h1 && h2) { /* Handle 1: Relaxed connection */ curl_easy_setopt(h1, CURLOPT_URL, "sftp://demo:[email protected]/readme.txt"); curl_easy_setopt(h1, CURLOPT_SHARE, share); printf("--- Transfer 1 (Relaxed initial connection) ---\n"); curl_easy_perform(h1); /* Handle 2: Security-critical connection */ curl_easy_setopt(h2, CURLOPT_URL, "sftp://demo:[email protected]/readme.txt"); curl_easy_setopt(h2, CURLOPT_SHARE, share); /* Strict policy: verify against a non-existent file (should fail) */ curl_easy_setopt(h2, CURLOPT_SSH_KNOWNHOSTS, "non_existent_file.txt"); printf("\n--- Transfer 2 (Strict Policy Requested) ---\n"); curl_easy_perform(h2); curl_easy_getinfo(h2, CURLINFO_NUM_CONNECTS, &connects); if(connects == 0) { printf("\nVULNERABLE: Transfer 2 reused the relaxed connection from Transfer 1!\n"); printf("CURLOPT_SSH_KNOWNHOSTS was bypassed.\n"); } else { printf("\nNOT VULNERABLE: Connection reuse denied.\n"); } curl_easy_cleanup(h1); curl_easy_cleanup(h2); } curl_share_cleanup(share); curl_global_cleanup(); return 0; } ``` ## Suggested Mitigation Modify `lib/url.c:ssh_config_matches()` to ensure that connections are only reused if the following security strings in `data->set.str[]` match: * `STRING_SSH_KNOWNHOSTS` * `STRING_SSH_HOST_PUBLIC_KEY_MD5` * `STRING_SSH_HOST_PUBLIC_KEY_SHA256` ## Impact ## Summary: This is a **High Severity** authentication bypass. It breaks the security guarantees of host key pinning and `known_hosts` verification in multi-tenant or shared-pool environments (e.g., PHP-FPM, proxies, or applications using `CURLM`). An attacker can intercept sensitive SFTP/SCP data even when the application specifically requests encrypted and verified sessions.
Actions
View on HackerOne
Report Stats
  • Report ID: 3639277
  • State: Closed
  • Substate: informative
  • Upvotes: 1
Share this report