✓ProveIt
Examples / Report #3735184 Saved analysis

CVE-2026-8926: password leak with netrc and user in URL

curl · 2026-05-147.9s analysis · $0.010 in model calls

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

Needs human review

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.

1 · Grounding
grounded
no model · 2 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2020 tokens · $0.0002
3 · Evidence
1 code snippets
real curl source at 81f950dd90b5 · 2 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
7347 tokens · 5.2s · $0.0101
5 · Receipts
0 of 0 citations verified
every citation must point at code the judge was shown
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

C1flaw not verifiable

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.

C2location not verifiable

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.

C3trigger not verifiable

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.

C4impact not verifiable

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.

C5reproduction not verifiable

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)

Full report on HackerOne ↗

Hey all, Before the following report, I just want to point to a comment from our scanner, where it seems it was already picked up but it was dismissed and the PR was closed later by another commit - https://github.com/curl/curl/pull/20932#issuecomment-4072903895 Nonetheless, it appears this issue exists for a while, actually since `e9b9bbac22c26cf67316fa8e6c6b9e831af31949` (2024-11-15) ## Summary: 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. For example, if .netrc contains credentials for alice, and the requested URL contains username bob, curl sends an HTTP Basic Authorization header for bob:alice_password. This contradicts the documented behavior that, when a username is provided in the URL, curl/libcurl looks for the password for that specific user, or falls back to a generic machine entry without a login. ## Steps To Reproduce: Given this .netrc: `machine 127.0.0.1 login alice password secret` and this request: `curl --netrc-optional http://bob@127.0.0.1:18080/` curl sends: `Authorization: Basic Ym9iOnNlY3JldA==` which decodes to: `bob:secret` The password secret belongs to login alice, but it is sent with URL username bob. PoC ``` #!/usr/bin/env bash set -euo pipefail tmp="$(mktemp -d)" port=18080 mkdir -p "$tmp/home" cat >"$tmp/home/.netrc" <<'EOF' machine 127.0.0.1 login alice password secret EOF chmod 0600 "$tmp/home/.netrc" cat >"$tmp/server.py" <<'PY' import base64 import http.server import socketserver class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): auth = self.headers.get("Authorization", "") print("Authorization:", auth, flush=True) if auth.startswith("Basic "): decoded = base64.b64decode(auth.split(" ", 1)[1]).decode() print("Decoded:", decoded, flush=True) self.send_response(200) self.end_headers() self.wfile.write(b"ok\n") def log_message(self, fmt, *args): pass with socketserver.TCPServer(("127.0.0.1", 18080), Handler) as httpd: httpd.handle_request() PY python3 "$tmp/server.py" & server_pid=$! sleep 0.3 HOME="$tmp/home" curl \ --silent \ --show-error \ --netrc-optional \ "http://bob@127.0.0.1:$port/" \ >/dev/null wait "$server_pid" ``` Expected Result curl should not use alice’s password …

Source code

lib/urldata.hlines 74–168 · definition of CURLcode
74typedef 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 */
82typedef 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
143struct 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};