Resolves arbitrary code execution via attacker-controlled output glob (CVE-2026-48962)

Resolves: RHEL-180421
This commit is contained in:
Michal Josef Špaček 2026-06-12 12:27:23 +02:00
parent e572066c0f
commit c9d28261ce
2 changed files with 167 additions and 1 deletions

View File

@ -0,0 +1,160 @@
From f2db247bf90d4cc7ee2710be384946081f3b4610 Mon Sep 17 00:00:00 2001
From: pmqs <pmqs@cpan.org>
Date: Sat, 16 May 2026 17:48:34 +0100
Subject: [PATCH] remove use of eval in globmapper. #73
---
lib/File/GlobMapper.pm | 52 ++++++++++++++++++++++++++++++++++--------
t/globmapper.t | 52 +++++++++++++++++++++++++++++++++++++++++-
2 files changed, 94 insertions(+), 10 deletions(-)
diff -u -r IO-Compress-2.096.orig/lib/File/GlobMapper.pm IO-Compress-2.096/lib/File/GlobMapper.pm
--- IO-Compress-2.096.orig/lib/File/GlobMapper.pm 2026-06-12 12:18:53.933423914 +0200
+++ IO-Compress-2.096/lib/File/GlobMapper.pm 2026-06-12 12:22:04.236128335 +0200
@@ -29,6 +29,11 @@
$VERSION = '1.001';
@EXPORT_OK = qw( globmap );
+our $BEGIN_DELIM = "\xFF";
+our $END_DELIM = "\xFE";
+our $BACKSLASH_ESC = "\xFD";
+our $HASH_ESC = "\xFC";
+our $STAR_ESC = "\xFB";
our ($noPreBS, $metachars, $matchMetaRE, %mapping, %wildCount);
$noPreBS = '(?<!\\\)' ; # no preceding backslash
@@ -310,14 +315,23 @@
}
my $noPreBS = '(?<!\\\)' ; # no preceding backslash
- #warn "noPreBS = '$noPreBS'\n";
+ my $noPreESC = '(?<![${BEGIN_DELIM}])' ; # no preceding backslash
- #$string =~ s/${noPreBS}\$(\d)/\${$1}/g;
- $string =~ s/${noPreBS}#(\d)/\${$1}/g;
- $string =~ s#${noPreBS}\*#\${inFile}#g;
- $string = '"' . $string . '"';
+ # escape any use of the delimiter symbols
+ # $string =~ s/(${BEGIN_DELIM}|${END_DELIM}|${BACKSLASH_ESC})/$1$1/g;
+
+ # escape \# and \*
+ $string =~ s/\\#/${HASH_ESC}/g;
+ $string =~ s/\\\*/${STAR_ESC}/g;
+
+ # Transform "#3" to BEGIN_DELIM 3 END_DELIM
+ $string =~ s/${noPreESC}#(\d)/${BEGIN_DELIM}${1}${END_DELIM}/g;
+
+ $string =~ s#\*#${BEGIN_DELIM}${END_DELIM}#g;
+
+ # print "INPUT '$self->{InputPattern}'\n";
+ # print "OUTPUT '$self->{OutputGlob}' => '$string'\n";
- #print "OUTPUT '$self->{OutputGlob}' => '$string'\n";
$self->{OutputPattern} = $string ;
return 1 ;
@@ -335,11 +349,31 @@
next if $inFiles{$inFile} ++ ;
my $outFile = $inFile ;
+ my @matches ;
+
+ my $noPreESC = '(?<![${BEGIN_DELIM}])' ; # no preceding backslash
- if ( $inFile =~ m/$self->{InputPattern}/ )
+ if (@matches = ($inFile =~ m/$self->{InputPattern}/ ))
{
- no warnings 'uninitialized';
- eval "\$outFile = $self->{OutputPattern};" ;
+ $outFile = $self->{OutputPattern};
+ my $ix = 1;
+
+ # get the filename glob
+ $outFile =~ s/${noPreESC}${BEGIN_DELIM}${END_DELIM}/$inFile/g;
+
+ # now each of the #1, #2,...
+ for my $pattern (@matches)
+ {
+ $outFile =~ s/${noPreESC}${BEGIN_DELIM}${ix}${END_DELIM}/$pattern/g;
+
+ ++ $ix;
+ }
+
+ # unescape
+ $outFile =~ s/${BEGIN_DELIM}${BEGIN_DELIM}/${BEGIN_DELIM}/g;
+ $outFile =~ s/${END_DELIM}${END_DELIM}/${END_DELIM}/g;
+ $outFile =~ s/${HASH_ESC}/#/g;
+ $outFile =~ s/${STAR_ESC}/*/g;
if (defined $outInMapping{$outFile})
{
diff -u -r IO-Compress-2.096.orig/t/globmapper.t IO-Compress-2.096/t/globmapper.t
--- IO-Compress-2.096.orig/t/globmapper.t 2026-06-12 12:18:53.938987087 +0200
+++ IO-Compress-2.096/t/globmapper.t 2026-06-12 12:22:04.238128347 +0200
@@ -24,7 +24,7 @@
$extra = 1
if eval { require Test::NoWarnings ; import Test::NoWarnings; 1 };
- plan tests => 68 + $extra ;
+ plan tests => 76 + $extra ;
use_ok('File::GlobMapper') ;
}
@@ -290,6 +290,56 @@
], " got mapping";
}
+{
+ title "check escaping";
+
+ my $tmpDir ;#= 'td';
+ my $lex = LexDir->new( $tmpDir );
+
+ my $BEGIN_DELIM = "\xFF";
+ my $END_DELIM = "\xFE";
+
+ #mkdir $tmpDir, 0777 ;
+
+ touch map { "$tmpDir/$_.tmp" } qw( abc1 abc2 abc3 ) ;
+
+ my $map = File::GlobMapper::globmap("$tmpDir/*b*.tmp", "$tmpDir/X-${BEGIN_DELIM}#2-#1${END_DELIM}-X");
+ ok $map, " got map"
+ or diag $File::GlobMapper::Error ;
+
+ is @{ $map }, 3, " returned 3 maps";
+ is_deeply $map,
+ [ [map { "$tmpDir/$_" } ("abc1.tmp", "X-${BEGIN_DELIM}c1-a${END_DELIM}-X")],
+ [map { "$tmpDir/$_" } ("abc2.tmp", "X-${BEGIN_DELIM}c2-a${END_DELIM}-X")],
+ [map { "$tmpDir/$_" } ("abc3.tmp", "X-${BEGIN_DELIM}c3-a${END_DELIM}-X")],
+ ], " got mapping";
+}
+
+{
+ title "check backslash escaping";
+
+ my $tmpDir ;#= 'td';
+ my $lex = LexDir->new( $tmpDir );
+
+ my $BEGIN_DELIM = "\xFF";
+ my $END_DELIM = "\xFE";
+
+ #mkdir $tmpDir, 0777 ;
+
+ touch map { "$tmpDir/$_.tmp" } qw( abc1 abc2 abc3 ) ;
+
+ my $map = File::GlobMapper::globmap("$tmpDir/*b*.tmp", $tmpDir . '/X-#2-\\#1\\*-X');
+ ok $map, " got map"
+ or diag $File::GlobMapper::Error ;
+
+ is @{ $map }, 3, " returned 3 maps";
+ is_deeply $map,
+ [ [map { "$tmpDir/$_" } ("abc1.tmp", "X-c1-#1*-X")],
+ [map { "$tmpDir/$_" } ("abc2.tmp", "X-c2-#1*-X")],
+ [map { "$tmpDir/$_" } ("abc3.tmp", "X-c3-#1*-X")],
+ ], " got mapping";
+}
+
# TODO
# test each of the wildcard metacharacters can be mapped to the output filename
#
Pouze v IO-Compress-2.096/t: globmapper.t.orig

View File

@ -11,11 +11,12 @@
Name: perl-IO-Compress
Version: 2.096
Release: 1%{?dist}
Release: 2%{?dist}
Summary: Read and write compressed data
License: GPL+ or Artistic
URL: https://metacpan.org/release/IO-Compress
Source0: https://cpan.metacpan.org/modules/by-module/IO/IO-Compress-%{version}.tar.gz
Patch0: IO-Compress-2.096-remove_use_of_eval_in_globmapper.patch
BuildArch: noarch
# Module Build
BuildRequires: coreutils
@ -93,6 +94,7 @@ included with the IO-Compress distribution:
%prep
%setup -q -n IO-Compress-%{version}
%patch -P0 -p1
# Remove spurious exec permissions
chmod -c -x lib/IO/Uncompress/{Adapter/Identity,RawInflate}.pm
@ -144,6 +146,10 @@ make test COMPRESS_ZLIB_RUN_%{?with_long_tests:ALL}%{!?with_long_tests:MOST}=1
%{_mandir}/man3/IO::Uncompress::*.3*
%changelog
* Fri Jun 12 2026 Michal Josef Špaček <mspacek@redhat.com> - 2.096-2
- Resolves arbitrary code execution via attacker-controlled output glob (CVE-2026-48962)
Resolves: RHEL-180421
* Sat Aug 1 2020 Paul Howarth <paul@city-fan.org> - 2.096-1
- 2.096 bump