Skip to content

[security][M1/M2] .NET LND macaroon: redirect-following HttpClient + cleartext http:// + unimplemented TLS knobs #77

Description

@refined-element

Severity: Medium / defense-in-depth · Source: 2026-08 security audit (M1 + M2), MCP .NET port

Location: dotnet/src/LightningEnable.Mcp/Program.cs (wallet HttpClient registrations, ~:180/:214), dotnet/src/LightningEnable.Mcp/Services/LndWalletService.cs:64 (scheme handling) and :70 (macaroon header).

Two related issues on the credential-bearing LND client:

  1. Redirect-following client (M1). The wallet HttpClients use the framework-default handler (AllowAutoRedirect=true). LndWalletService.cs:70 attaches the LND admin macaroon as the custom header Grpc-Metadata-macaroon, which .NET does not strip on a cross-origin redirect (only the typed Authorization header is stripped). A 302 from a hostile hop in front of the LND REST endpoint replays the full admin macaroon to the redirect target → node takeover, bypassing the MCP confirmation/budget gates. The repo already applies AllowAutoRedirect=false to the L402 + discover_api clients for exactly this reason — just not to the ones carrying node credentials. (Python is unaffected: httpx defaults follow_redirects=False.)

  2. Cleartext http:// + inert TLS knobs (M2). LndWalletService.cs:64 accepts an explicit http:// host with no warning, so the admin macaroon travels in cleartext on every request. The class docblock advertises LND_TLS_CERT_PATH / LND_SKIP_TLS_VERIFY, but neither is ever read in the .NET project — so an operator with LND's default self-signed cert has no working remedy and is nudged toward http://. (Python implements skip_tls_verify, but that disables validation wholesale.)

Precondition: an attacker already in the LND transport path (compromised reverse proxy, DNS hijack, MITM on a plaintext/verify-disabled connection). Hence defense-in-depth rather than remote exposure — but on node-draining credentials.

Fix: builder.Services.ConfigureHttpClientDefaults(b => b.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { AllowAutoRedirect = false })) (keep the SSRF ConnectCallback on the agent-facing clients); implement LND_TLS_CERT_PATH cert-pinning via SslOptions.RemoteCertificateValidationCallback and honour LND_SKIP_TLS_VERIFY only with a loud warning; reject/warn on http:// for any non-loopback host; until implemented, delete the misleading LND_TLS_CERT_PATH/LND_SKIP_TLS_VERIFY lines from the .NET docblock. Add a regression test asserting a wallet-endpoint 302 is surfaced as an error, not followed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority/laterShip when capacity (quality, polish, distribution)source/security-auditSurfaced from comprehensive security audit (item #0)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions