curl · Report #3970650
Stacked --proto modifiers leave denied protocol enabled
How it was found
Summary
proto2num() (line 431–447) consumes only one leading modifier character via a switch statement. The documentation says entries may be "prefixed by zero or more modifiers." Before commit 86ac471d, a loop consumed all modifiers. The replacement switch reads only the first character.
--proto '+-ftp' reads + (action=allow), then passes -ftp to proto_token() (line 466). -ftp is not a recognized protocol name → warning emitted, FTP stays in the default set. --silent suppresses the warning entirely.
The same parser handles --proto-redir, so redirect protocol restrictions are equally bypassable.
Reproduction
python3 poc_proto_bypass.py <sha>
# Denied correctly:
curl --proto '-file' file:///etc/hostname # exit 1 (protocol denied)
# Bypass:
curl --proto '+-file' file:///etc/hostname # exit 0 — reads the file!
curl --silent --proto '+-file' file:///etc/hostname # no warning shown
Root cause
// src/tool_paramhlp.c:431-447
switch(*str++) { // ← consumes ONE character only
case '=': action = set; break;
case '-': action = deny; break;
case '+': action = allow; break;
default: str--; plen++; break;
}
// str now points to "-ftp", which proto_token() doesn't recognize
Impact
A trusted wrapper or script that uses the documented stacked-modifier syntax (e.g. --proto '+-ftp' intending to deny FTP after first allowing all) silently leaves the denied protocol enabled. An untrusted party who controls the URL or a redirect target can then reach a protocol the wrapper explicitly tried to block. --proto-redir is equally affected, so a server-controlled 3xx redirect can escalate to file://, gopher://, or ftp:// reads despite the wrapper's restrictions. --silent suppresses the only visible indicator (the "unrecognized protocol" warning). Direct libcurl API callers are not affected — the bug is in the CLI argument parser only.