CVE-2026-8450
Description
HTTP::Daemon versions before 6.17 for Perl allow OS command injection via send_file().
send_file() opens its string argument with Perl's 2-arg open(). The 2-arg form interprets magic prefixes: '| cmd' and 'cmd |' open a pipe to a subprocess, '> path' and '>> path' open the path for write or append.
Untrusted input passed to send_file() can run OS commands at the daemon process UID. The read-pipe form ('cmd |') also leaks subprocess stdout into the HTTP response body. The write-mode forms can create or truncate files at attacker chosen paths.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
HTTP::Daemon before 6.17 allows OS command injection or arbitrary file write when send_file() is called with attacker-influenced input due to use of Perl's 2-arg open().
Vulnerability
The send_file() method in HTTP::Daemon::ClientConn before version 6.17 uses Perl's 2-argument open(FILE, $file) call, which interprets shell-magic prefixes in the path argument [1][2]. When an attacker-controlled string is passed to send_file(), the characters |, >, >>, +<, &, or leading whitespace can trigger pipe commands or file redirections instead of a normal file open. The vulnerability affects all versions prior to 6.17 [3]. The fix switches to 3-arg open(my $fh, '<', $file), ensuring the path is treated as a literal filename.
Exploitation
An attacker must be able to influence the string argument passed to send_file(), for example by providing a filename via a query parameter to a download endpoint [1][2]. No authentication or special network position is required if the endpoint is publicly accessible. By crafting input such as | command (write pipe), command | (read pipe), > /path/to/write (truncate-and-write), or >> /path/to/append, the attacker achieves command execution or arbitrary file operations at the daemon's UID [2].
Impact
Successful exploitation yields OS command injection as the daemon process user, enabling full system compromise within that user's scope [1][2]. The read-pipe form (command |) also leaks the subprocess's stdout into the HTTP response body, exfiltrating output. Write-mode forms (>, >>) allow creating or overwriting arbitrary files, potentially leading to privilege escalation or denial of service.
Mitigation
Upgrade to HTTP::Daemon version 6.17 (released 2026-05-19) or later [3]. The fix replaces the dangerous 2-arg open with a 3-arg open that does not interpret shell-magic. No workaround is available for users who cannot upgrade; administrators must ensure no attacker-influenced input reaches send_file() by applying strict input validation and sanitisation [2].
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2(expand)+ 1 more
- (no CPE)
- (no CPE)range: <6.17
Patches
2945d35141d94Fix CVE-2026-8450: send_file() honoured 2-arg open() shell-magic
2 files changed · +42 −5
Changes+12 −0 modified@@ -1,6 +1,18 @@ Revision history for HTTP-Daemon {{$NEXT}} + - Fix CVE-2026-8450 (affects 6.15 and earlier): 2-arg open() in + send_file() enabled RCE / arbitrary file write / response-body + exfiltration when a string argument was derived from attacker- + influenced input. send_file() now uses 3-arg open() with an + explicit '<' read mode, so the path is always treated as a literal + filename and 2-arg open() shell-magic shapes ('| cmd', 'cmd |', + '> path', etc.) are no longer interpreted. send_file() now also + returns '0E0' (true zero) on a successful zero-byte transfer so + callers can distinguish empty file from open failure (undef). See + https://www.cve.org/CVERecord?id=CVE-2026-8450 for the advisory. + Reported and patched by Stig Palmquist (stigtsp). (Stig Palmquist, + Olaf Alders) - Bump LWP::UserAgent to 6.37 in TestSuggests (GH#65) (Olaf Alders) 6.15 2023-02-22 22:02:46Z
lib/HTTP/Daemon.pm+30 −5 modified@@ -598,11 +598,10 @@ sub send_dir { sub send_file { my ($self, $file) = @_; my $opened = 0; - local (*FILE); if (!ref($file)) { - open(FILE, $file) || return undef; - binmode(FILE); - $file = \*FILE; + open(my $fh, '<', $file) || return undef; + binmode($fh) || do { close($fh); return undef }; + $file = $fh; $opened++; } my $cnt = 0; @@ -614,7 +613,11 @@ sub send_file { print $self $buf; } close($file) if $opened; - $cnt; + + # Return a "true zero" for empty-but-successful copies so callers + # using `send_file or die` can distinguish open failure (undef) + # from a successful zero-byte transfer. + $cnt || '0E0'; } sub daemon { @@ -896,6 +899,28 @@ Copy the file to the client. The file can be a string (which will be interpreted as a filename) or a reference to an C<IO::Handle> or glob. +Returns the number of bytes copied on success, or C<undef> if the +filename form failed to open. An empty file returns the string +C<'0E0'> (zero numerically, true in boolean context) so that callers +using C<< send_file or die >> can distinguish open failure from a +successful zero-byte transfer. + +The filename form uses Perl's 3-argument C<open> with an explicit C<< +< >> mode, so the path is no longer interpreted as a 2-argument +C<open> shell-magic shape such as C<< | cmd >>, C<< cmd | >>, or +C<< > path >>. See +L<CVE-2026-8450|https://www.cve.org/CVERecord?id=CVE-2026-8450> for +the prior 2-argument C<open> behaviour this replaces. + +Note that this fix only neutralises 2-argument C<open> shell-magic. +Callers remain responsible for validating attacker-influenced paths: +C<send_file> will still happily open symlinks, character/block devices +(e.g. C</dev/zero>, C</dev/stdin>), named pipes (which may block the +worker), and files outside an intended document root. If C<$filename> +can be derived from request input, validate it (canonicalise, reject +C<..> segments, require C<-f _> and a vetted prefix) before passing it +in. + =item $c->daemon Return a reference to the corresponding C<HTTP::Daemon> object.
7aa2827eefeet/send-file-magic-open: drop redundant pipe spellings
1 file changed · +6 −6
t/send-file-magic-open.t+6 −6 modified@@ -45,13 +45,13 @@ my $writer = qq{$^X -e 'open my \$f, q{>}, \$ENV{HTTPD_MAGIC_MARKER} or die; close \$f'}; my @magic_shapes = ( - {name => 'write-pipe', shape => sub {"| $writer"}}, - {name => 'read-pipe', shape => sub {"$writer |"}}, - # 2-arg open() strips leading whitespace before checking for magic - # prefixes, so " | cmd" (note the leading space) is also a pipe. - {name => 'leading-ws-pipe', shape => sub {" | $writer"}}, - {name => 'write-redirect', shape => sub { my ($m) = @_; "> $m" }}, + # One pipe shape stands for the whole command-execution family + # (| cmd, cmd |, ...). The leading-space spelling is the one kept: + # 2-arg open() strips leading whitespace before testing for a magic + # prefix, so " | cmd" also exercises that quirk. + {name => 'pipe', shape => sub {" | $writer"}}, + {name => 'write-redirect', shape => sub { my ($m) = @_; "> $m" }}, ); for my $case (@magic_shapes) {
Vulnerability mechanics
Root cause
"send_file() used Perl's two-argument open(), which interprets shell-magic prefixes in the filename argument as pipe commands or file redirections instead of literal filenames."
Attack vector
An attacker supplies a string to `send_file()` that begins with a shell-magic prefix such as `| cmd`, `cmd |`, `> path`, or `>> path` [ref_id=1]. Because the function used Perl's two-argument `open()`, these prefixes are interpreted as pipe-to-command, pipe-from-command, or write/append redirections rather than literal filenames [patch_id=2623896]. The read-pipe form (`cmd |`) also leaks the subprocess's stdout into the HTTP response body. Any HTTP::Daemon-based application that passes attacker-influenced bytes to `send_file()` — for example, a download endpoint that derives the filename from a query parameter — grants OS command execution and/or arbitrary file write at the daemon's process UID [ref_id=1].
Affected code
The vulnerability is in `HTTP::Daemon::ClientConn::send_file()` in `lib/HTTP/Daemon.pm` [patch_id=2623896]. The function used the two-argument form `open(FILE, $file)`, which interprets shell-magic prefixes in the string argument [ref_id=1]. The patch replaces this with the three-argument form `open(my $fh, '
What the fix does
The patch switches from two-argument `open(FILE, $file)` to three-argument `open(my $fh, '
Preconditions
- inputThe application must pass attacker-influenced input as the string argument to send_file(), e.g. deriving a filename from a query parameter.
- networkNo authentication or special network position is required beyond the ability to send HTTP requests to the daemon.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.