TL;DR
- CVE-2026-8450 —
HTTP::Daemonbefore 6.17:send_file()opens its string argument with Perl's 2-argopen(), which interprets magic prefixes. Untrusted input beginning| cmd,cmd |,> path, or>> pathruns OS commands or writes files at the daemon's UID. The read-pipe form (cmd |) also leaks subprocess stdout into the HTTP response body. Fix: upgrade to 6.17. - CVE-2026-48962 —
IO::Compressbefore 2.220:_parseOutputGlob()wraps the caller-supplied output-glob string in double quotes and stashes it;_getFiles()later runs that stored expression througheval STRING. A literal"in the glob breaks out of the wrapper and executes arbitrary Perl (CWE-95, eval injection). Fix: upgrade to 2.220. - Both disclosed by Stig Palmquist on the oss-security list this morning. If you run an
HTTP::Daemonservice that ever passes request-derived paths tosend_file(), treat it as RCE and patch now. If any tool feeds user input intoIO::Compress's output-glob argument, same.
Two CVEs, one footgun: the string that quietly means "execute this"
The interesting thing about today's pair isn't either bug alone — it's that they're the same mistake in two unrelated modules, both surfacing the same morning from the same discloser. Both come from a Perl API that takes a string and, under the hood, treats part of it as a thing to run rather than a thing to read. Perl has carried these implicit-execution conveniences for decades; they're fine when the string is a literal you wrote and a liability the moment any byte of it comes from a caller.
send_file() → 2-arg open()
Perl's two-argument open(FH, $expr) reads magic out of $expr:
"| cmd"— open a pipe to a subprocess (write side)."cmd |"— open a pipe from a subprocess (read side); its stdout flows back to you."> path"/">> path"— openpathfor write/append.
HTTP::Daemon's send_file() passed its string argument straight into that form. As Palmquist's advisory puts it: "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." So a request that influences the filename — the ordinary "serve the file the client asked for" pattern — can hand the daemon "; nc … |"-shaped input and get command execution, or exfiltrate command output through the response body. The fix is the boring, correct one: 3-arg open(FH, '<', $path), where the mode is a separate literal argument and $path can't smuggle a mode. That landed in 6.17.
IO::Compress glob → eval STRING
The compression path has the same shape one layer up. _parseOutputGlob() takes the caller's output-glob string, wraps it in double quotes, and stores it; _getFiles() then runs the stored expression through eval STRING — Perl's run-arbitrary-source eval, not the block form. The quoting was meant to make the glob a safe string literal. It isn't: a literal " inside the glob closes the wrapper early, and everything after it is parsed as Perl and executed at the calling process's privilege. That's CWE-95, eval injection, fixed in 2.220.
Neither bug is exotic. They're the canonical Perl traps — 2-arg open() and string eval — that Perl's own taint mode (perlsec) and decades of style guidance exist to defend against. What today's disclosure shows is that they're still load-bearing inside widely-installed CPAN modules, not just in beginner scripts. The "magic" prefix behavior of 2-arg open and the source-executing behavior of eval EXPR are exactly the features that turn a data path into a code path when the data is attacker-influenced.
What to do
HTTP::Daemon: upgrade to 6.17+. Until you do, audit everysend_file()call for an argument derived from request data (path, query, header). If a path can start with|,>, or whitespace-then-those, it's exploitable. Pin the dependency floor in yourcpanfile/Makefile.PLso a freshcpanmdoesn't silently install an older one.IO::Compress: upgrade to 2.220+. Then check whether any of your tooling passes a user-controlled string as the output glob toIO::Compress::*— that's the exploitable surface. Output globs built only from your own constants are not at risk.- Wider sweep: if you maintain Perl, this is a good prompt to
grepyour tree for 2-argopenwith a non-literal expression andevalof a string (as opposed toeval { … }block form). Those two patterns are where this class of bug lives; both have safe replacements (3-arg open with explicit mode; block eval, or a real parser instead of eval).
The reader action is concrete: patch both modules to their fixed versions, then grep your own Perl for the same two constructs. The takeaway that generalizes past Perl: an API that accepts a string and decides for itself whether part of it is a command, a redirect, or source code is a data-to-code bridge — and every such bridge needs the caller's input to be unambiguously inert.
Sources
- Stig Palmquist, CVE-2026-8450: HTTP::Daemon send_file() OS command injection via 2-arg open(), oss-security (2026-05-27) — https://www.openwall.com/lists/oss-security/2026/05/27/5
- Stig Palmquist, CVE-2026-48962: IO::Compress File::GlobMapper eval injection, oss-security (2026-05-27) — https://www.openwall.com/lists/oss-security/2026/05/27/4
- oss-security list archive, 2026-05-27 — https://www.openwall.com/lists/oss-security/2026/05/27/
perlsec— Perl's official security documentation (taint mode, untrusted-input handling) — https://perldoc.perl.org/perlsec