CVE-2025-15649 Overview
CVE-2025-15649 affects IO::Uncompress::Unzip versions before 2.215 for Perl. The module propagates an uncaught exception when parsing a zip header that contains a malformed DOS date field. The internal _dosToUnixTime() function decodes the local-file-header last-modification date and calls Time::Local::timelocal() without an eval guard. A header whose date decodes to an out-of-range month, day, or hour causes timelocal() to die. The exception propagates out of IO::Uncompress::Unzip->new($file), where callers expect undef plus $UnzipError instead of a fatal error. This breaks the documented error-handling contract and can crash Perl applications that process untrusted zip archives [CWE-248].
Critical Impact
A crafted zip archive can terminate any Perl process that parses it with IO::Uncompress::Unzip, producing a denial-of-service condition in archive-processing services.
Affected Products
- IO::Uncompress::Unzip Perl module versions before 2.215
- IO-Compress distribution on CPAN prior to release 2.215
- Perl applications and services that pass untrusted zip files to IO::Uncompress::Unzip->new()
Discovery Timeline
- 2026-05-27 - CVE-2025-15649 published to NVD
- 2026-05-27 - OpenWall OSS-Security advisory issued
- 2026-05-27 - Last updated in NVD database
Technical Details for CVE-2025-15649
Vulnerability Analysis
The issue is an uncaught exception in IO::Uncompress::Unzip, classified under [CWE-248] (Uncaught Exception). When the module parses a zip local-file-header, it extracts the DOS-format last-modified timestamp and converts it to Unix time. The conversion routine _dosToUnixTime() directly invokes Time::Local::timelocal() on values decoded from attacker-controlled bytes. timelocal() throws a fatal exception when month, day, or hour fields fall outside valid ranges. Without an eval block around the call, the exception escapes the module entirely.
Root Cause
The root cause is missing exception handling around Time::Local::timelocal(). The DOS date field is a packed 32-bit value where year, month, day, hour, minute, and second bits are extracted via shifts and masks. Malformed archives can encode a month of 0 or 15, a day of 0, or an hour greater than 23. Time::Local validates these values and dies on failure. _dosToUnixTime() assumed all decoded values would be valid and omitted defensive handling.
Attack Vector
An attacker supplies a zip file with a crafted local-file-header date field to any Perl program or service that calls IO::Uncompress::Unzip->new() on untrusted input. The constructor dies instead of returning undef, terminating the calling Perl process unless the caller wrapped the constructor in its own eval. Common targets include mail filters, web upload handlers, backup processors, and CI/CD pipelines that inspect zip archives.
# Patch excerpt from lib/IO/Uncompress/Unzip.pm (fix in 2.215)
# from Archive::Zip & info-zip
sub _dosToUnixTime
{
+ # Returns zero when $dt is already zero or it doesn't expand to a value that Time::Local::timelocal()
+ # can handle.
+
my $dt = shift;
+ # warn "_dosToUnixTime dt=[$dt]\n";
+
+ # some zip files don't populate the datetime field at all
+ return 0 if ! $dt;
my $year = ( ( $dt >> 25 ) & 0x7f ) + 80;
my $mon = ( ( $dt >> 21 ) & 0x0f ) - 1;
Source: GitHub Commit Patch
The patch adds an early return 0 for zero datetime values and wraps the subsequent timelocal() invocation to handle out-of-range fields gracefully.
Detection Methods for CVE-2025-15649
Indicators of Compromise
- Perl process crashes or die messages referencing Time::Local::timelocal or _dosToUnixTime in application logs
- Unhandled exception stack traces originating in IO/Uncompress/Unzip.pm during archive ingestion
- Repeated zip uploads from the same source followed by service restarts or worker failures
Detection Strategies
- Inventory installed IO-Compress versions across servers using cpan -D IO::Compress or perl -MIO::Compress -e 'print $IO::Compress::VERSION' and flag any version below 2.215
- Search application and supervisor logs for fatal Perl exceptions raised during zip parsing operations
- Add unit tests that pass a zip file with an invalid DOS date to IO::Uncompress::Unzip->new() and verify the call returns undef with $UnzipError set
Monitoring Recommendations
- Alert on abnormal termination of Perl-based archive workers, mail scanners, or CI runners that consume user-supplied zip files
- Monitor inbound zip uploads for malformed headers using a zip integrity tool before handing them to Perl processors
- Track CPAN dependency updates so production deployments stay aligned with the fixed IO-Compress release
How to Mitigate CVE-2025-15649
Immediate Actions Required
- Upgrade IO-Compress to version 2.215 or later on every system that runs Perl code parsing zip archives
- Audit application code for direct calls to IO::Uncompress::Unzip->new() and confirm callers do not rely on undef return semantics without exception handling
- Restrict or sandbox the processing of zip files received from untrusted sources until patching is complete
Patch Information
The fix is committed in the IO-Compress repository as commit fd28c1d2374eee9811f6d0c5bddc0957abdf1da8 and released in version 2.215 on CPAN. See the GitHub Commit Patch, the MetaCPAN Release Changes, the GitHub Issue Discussion, and the OpenWall OSS-Security Mailing for full advisory context.
Workarounds
- Wrap every IO::Uncompress::Unzip->new() call in eval { ... } and treat exceptions as parse failures until the upgrade is deployed
- Reject zip archives whose local-file-header date fields fall outside valid DOS ranges during pre-validation
- Run zip-processing workers under a supervisor that restarts on crash to limit denial-of-service impact
# Upgrade IO-Compress to the patched release
cpan IO::Compress
# or, with cpanm
cpanm IO::Compress@2.215
# Verify the installed version
perl -MIO::Compress -e 'print "$IO::Compress::VERSION\n"'
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


