CVE-2026-8926: password leak with netrc and user in URL
What the report claims
curl/libcurl can combine a username supplied in the URL with a password from a .netrc entry whose explicit login does not match that username, causing the wrong password to be sent.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2026-8926 password leak with netrc and user in URL
Shown for comparison after analysis. The pipeline did not see this label.
ProveIt's finding
The available evidence does not settle the report. A maintainer should investigate.
Why this finding?
- The provided source code (lib/urldata.h) does not contain the .netrc credential resolution logic that would confirm or contradict the claims.
- The deterministic checks only verify that CURLOPT_NETRC exists as a symbol, not its implementation.
- The report includes a detailed PoC and references a specific commit (e9b9bbac22c26cf67316fa8e6c6b9e831af31949) where the issue was allegedly introduced, suggesting the reporter has examined the actual implementation.
- Without the relevant source files (likely lib/netrc.c or similar), the claims cannot be verified against the code.
What still needs checking
- The .netrc credential resolution source code (likely in lib/netrc.c or similar) from revision 81f950dd90b5
- The commit e9b9bbac22c26cf67316fa8e6c6b9e831af31949 changes to understand what was introduced
How this analysis ran 5 claims · 0 citations verified
Checked against curl master on 2026-05-14 (81f950dd90b5). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.
Review a draft reply to the reporter
Edit and verify before sending.
Thank you for the detailed report and PoC. The provided code snippets do not include the .netrc credential resolution logic (likely in lib/netrc.c), so I cannot verify the claims against the implementation. Could you point to the specific file and function where .netrc entries are matched against URL usernames? The deterministic checks confirm CURLOPT_NETRC exists, but the core logic is not in the provided source.
Inspect all claims and source code 5 claims · 1 code excerpts
Report claims
curl can send a password from a .netrc entry whose explicit login does not match the URL username
Read the reasoning
The claim describes a flaw in .netrc credential resolution logic, but the provided source code (lib/urldata.h lines 74-168) only contains type definitions and struct declarations. The actual .netrc parsing and credential selection code is not included in the provided snippets. The deterministic checks only confirm CURLOPT_NETRC exists as a symbol, not its implementation.
in the function that resolves .netrc credentials for a given host and username
Read the reasoning
The claim references a function that resolves .netrc credentials, but no such function appears in the provided code. The provided file (lib/urldata.h) does not contain .netrc resolution logic.
when a URL contains a username and .netrc is enabled with CURLOPT_NETRC
Read the reasoning
The trigger condition involves CURLOPT_NETRC being enabled, which the deterministic checks confirm exists as a symbol. However, the code path from CURLOPT_NETRC to the credential resolution logic is not shown in the provided source, so the trigger cannot be verified against the implementation.
the wrong password is sent in the Authorization header, leaking credentials intended for a different login
Read the reasoning
The impact claim depends on the credential resolution behavior which is not present in the provided code. Without the .netrc lookup implementation, it cannot be determined whether the wrong password would be sent.
use a .netrc entry with login alice and a URL with username bob, then observe the Authorization header containing bob:secret
Read the reasoning
The reproduction steps describe a test case, but the code that would execute this test case (the .netrc parsing and Authorization header generation) is not in the provided source snippets.
What the report says about other projects
not addressed When a username is provided in the URL, curl should look for the password for that specific user, or fall back to a generic machine entry without a login.
Read the original report (excerpt)
Source code
| 74 | typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ |
| 75 | int sockindex, /* socketindex */ |
| 76 | const uint8_t *buf, /* data to write */ |
| 77 | size_t len, /* amount to send */ |
| 78 | bool eos, /* last chunk */ |
| 79 | size_t *pnwritten); /* how much sent */ |
| 80 | |
| 81 | /* On error return, the value of `pnread` has no meaning */ |
| 82 | typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ |
| 83 | int sockindex, /* socketindex */ |
| 84 | char *buf, /* store data here */ |
| 85 | size_t len, /* max amount to read */ |
| 86 | size_t *pnread); /* how much received */ |
| 87 | |
| 88 | #include "mime.h" |
| 89 | #include "protocol.h" |
| 90 | #include "ftp.h" |
| 91 | #include "http.h" |
| 92 | #include "smb.h" |
| 93 | #include "mqtt.h" |
| 94 | #include "ftplistparser.h" |
| 95 | #include "multihandle.h" |
| 96 | #include "cf-socket.h" |
| 97 | |
| 98 | #ifdef HAVE_GSSAPI |
| 99 | # ifdef HAVE_GSSGNU |
| 100 | # include <gss.h> |
| 101 | # elif defined(HAVE_GSSAPI_H) |
| 102 | # include <gssapi.h> |
| 103 | # else /* MIT Kerberos */ |
| 104 | # include <gssapi/gssapi.h> |
| 105 | # include <gssapi/gssapi_krb5.h> /* for GSS_C_CHANNEL_BOUND_FLAG in 1.19+ */ |
| 106 | # endif |
| 107 | #endif |
| 108 | |
| 109 | #ifdef USE_LIBSSH2 |
| 110 | #include <libssh2.h> |
| 111 | #include <libssh2_sftp.h> |
| 112 | #endif /* USE_LIBSSH2 */ |
| 113 | |
| 114 | #define READBUFFER_SIZE CURL_MAX_WRITE_SIZE |
| 115 | #define READBUFFER_MAX CURL_MAX_READ_SIZE |
| 116 | #define READBUFFER_MIN 1024 |
| 117 | |
| 118 | /* The default upload buffer size, should not be smaller than |
| 119 | CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in |
| 120 | a write callback. |
| 121 | |
| 122 | The size was 16KB for many years but was bumped to 64KB because it makes |
| 123 | libcurl able to do significantly faster uploads in some circumstances. Even |
| 124 | larger buffers can help further, but this is deemed a fair memory/speed |
| 125 | compromise. */ |
| 126 | #define UPLOADBUFFER_DEFAULT 65536 |
| 127 | #define UPLOADBUFFER_MAX (2 * 1024 * 1024) |
| 128 | #define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE |
| 129 | |
| 130 | #define CURLEASY_MAGIC_NUMBER 0xc0dedbadU |
| 131 | #ifdef DEBUGBUILD |
| 132 | /* On a debug build, we want to fail hard on easy handles that |
| 133 | * are not NULL, but no longer have the MAGIC touch. This gives |
| 134 | * us early warning on things only discovered by valgrind otherwise. */ |
| 135 | #define GOOD_EASY_HANDLE(x) \ |
| 136 | (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ |
| 137 | (DEBUGASSERT(!(x)), FALSE)) |
| 138 | #else |
| 139 | #define GOOD_EASY_HANDLE(x) \ |
| 140 | ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) |
| 141 | #endif |
| 142 | |
| 143 | struct ssl_primary_config { |
| 144 | char *CApath; /* certificate directory (does not work on Windows) */ |
| 145 | char *CAfile; /* certificate to verify peer against */ |
| 146 | char *issuercert; /* optional issuer certificate filename */ |
| 147 | char *clientcert; |
| 148 | char *cipher_list; /* list of ciphers to use */ |
| 149 | char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ |
| 150 | char *signature_algorithms; /* list of signature algorithms to use */ |
| 151 | char *pinned_key; |
| 152 | char *CRLfile; /* CRL to check certificate revocation */ |
| 153 | struct curl_blob *cert_blob; |
| 154 | struct curl_blob *ca_info_blob; |
| 155 | struct curl_blob *issuercert_blob; |
| 156 | #ifdef USE_TLS_SRP |
| 157 | char *username; /* TLS username (for, e.g., SRP) */ |
| 158 | char *password; /* TLS password (for, e.g., SRP) */ |
| 159 | #endif |
| 160 | char *curves; /* list of curves to use */ |
| 161 | uint32_t version_max; /* max supported version the client wants to use */ |
| 162 | uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ |
| 163 | uint8_t version; /* what version the client wants to use */ |
| 164 | BIT(verifypeer); /* set TRUE if this is desired */ |
| 165 | BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */ |
| 166 | BIT(verifystatus); /* set TRUE if certificate status must be checked */ |
| 167 | BIT(cache_session); /* cache session or not */ |
| 168 | }; |