CVE-2015-3408 (published 2015-05-19T18:59Z)

Module::Signature before 0.74 allows remote attackers to execute arbitrary shell commands via a crafted SIGNATURE file which is not properly handled when generating checksums from a signed manifest.

Severity: HIGH
CVSS v2 base score: 10.0 (AV:N/AC:L/Au:N/C:C/I:C/A:C)

Weakness information:
cwe_id: CWE-77
cwe_name: Improper Neutralization of Special Elements used in a Command ('Command Injection')
description: The product constructs all or part of a command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended command when it is sent to a downstream component.
extended_description: 
            Many protocols and products have their own custom command language. While OS or shell command strings are frequently discovered and targeted, developers may not realize that these other command languages might also be vulnerable to attacks.
         
         

Fix information:
repo_url: https://github.com/audreyt/module-signature
commit_details: repo_url: https://github.com/audreyt/module-signature
author: Audrey Tang
author_date: 2015-04-05 16:05:20+08:00
msg: * Fix issues reported by John Lightsey
repository: repo_url: https://github.com/audreyt/module-signature
repo_name: audreyt/module-signature
description: Module signature file manipulation
repo_language: Perl
owner: audreyt
file_changes: filename: Makefile.PL
change_type: ModificationType.MODIFY
diff: @@ -10,6 +10,7 @@ readme_from     'lib/Module/Signature.pm';
 repository      'http://github.com/audreyt/module-signature';
 install_script  'script/cpansign';
 build_requires  'Test::More', 0, 'IPC::Run', 0;
+requires        'File::Temp';
 
 # On Win32 (excluding cygwin) we know that IO::Socket::INET,
 # which is needed for keyserver stuff, doesn't work. In fact

programming_language: Perl
filename: README
change_type: ModificationType.MODIFY
diff: @@ -248,7 +248,7 @@ SEE ALSO
     Dist::Zilla::Plugin::Signature
 
 AUTHORS
-    唐鳳 <cpan@audreyt.org>
+    Audrey Tang <cpan@audreyt.org>
 
 CC0 1.0 Universal
     To the extent possible under law, 唐鳳 has waived all copyright and

programming_language: Perl
filename: Signature.pm
change_type: ModificationType.MODIFY
diff: @@ -1,5 +1,5 @@
 package Module::Signature;
-$Module::Signature::VERSION = '0.73_01';
+$Module::Signature::VERSION = '0.74';
 
 use 5.005;
 use strict;
@@ -58,6 +58,8 @@ sub _cipher_map {
     my @lines = split /\015?\012/, $sigtext;
     my %map;
     for my $line (@lines) {
+        last if $line eq '-----BEGIN PGP SIGNATURE-----';
+        next if $line =~ /^---/ .. $line eq '';
         my($cipher,$digest,$file) = split " ", $line, 3;
         return unless defined $file;
         $map{$file} = [$cipher, $digest];
@@ -66,7 +68,7 @@ sub _cipher_map {
 }
 
 sub verify {
-    my %args = ( skip => 1, @_ );
+    my %args = ( @_ );
     my $rv;
 
     (-r $SIGNATURE) or do {
@@ -179,6 +181,11 @@ sub _fullcheck {
         ($mani, $file) = ExtUtils::Manifest::fullcheck();
     }
     else {
+        my $_maniskip = &ExtUtils::Manifest::maniskip;
+        local *ExtUtils::Manifest::maniskip = sub { sub {
+            return unless $skip;
+            return $_maniskip->(@_);
+        } };
         ($mani, $file) = ExtUtils::Manifest::fullcheck();
     }
 
@@ -238,6 +245,11 @@ sub _verify_gpg {
 
     my $keyserver = _keyserver($version);
 
+    require File::Temp;
+    my $fh = File::Temp->new();
+    print $fh $sigtext;
+    close $fh;
+
     my $gpg = _which_gpg();
     my @quiet = $Verbose ? () : qw(-q --logger-fd=1);
     my @cmd = (
@@ -246,7 +258,7 @@ sub _verify_gpg {
             ($AutoKeyRetrieve and $version ge '1.0.7')
                 ? '--keyserver-options=auto-key-retrieve'
                 : ()
-        ) : ()), $SIGNATURE
+        ) : ()), $fh->filename
     );
 
     my $output = '';
@@ -258,6 +270,7 @@ sub _verify_gpg {
         my $cmd = join ' ', @cmd;
         $output = `$cmd`;
     }
+    unlink $fh->filename;
 
     if( $? ) {
         print STDERR $output;
@@ -286,7 +299,7 @@ sub _verify_crypt_openpgp {
     my $pgp = Crypt::OpenPGP->new(
         ($KeyServer) ? ( KeyServer => $KeyServer, AutoKeyRetrieve => $AutoKeyRetrieve ) : (),
     );
-    my $rv = $pgp->handle( Filename => $SIGNATURE )
+    my $rv = $pgp->handle( Data => $sigtext )
         or die $pgp->errstr;
 
     return SIGNATURE_BAD if (!$rv->{Validity} and $AutoKeyRetrieve);
@@ -309,32 +322,35 @@ sub _read_sigfile {
     my $well_formed;
 
     local *D;
-    open D, $sigfile or die "Could not open $sigfile: $!";
+    open D, "< $sigfile" or die "Could not open $sigfile: $!";
 
     if ($] >= 5.006 and <D> =~ /\r/) {
         close D;
-        open D, $sigfile or die "Could not open $sigfile: $!";
+        open D, '<', $sigfile or die "Could not open $sigfile: $!";
         binmode D, ':crlf';
     } else {
         close D;
-        open D, $sigfile or die "Could not open $sigfile: $!";
+        open D, "< $sigfile" or die "Could not open $sigfile: $!";
     }
 
+    my $begin = "-----BEGIN PGP SIGNED MESSAGE-----\n";
+    my $end = "-----END PGP SIGNATURE-----\n";
     while (<D>) {
-        next if (1 .. /^-----BEGIN PGP SIGNED MESSAGE-----/);
-        last if /^-----BEGIN PGP SIGNATURE/;
-
+        next if (1 .. ($_ eq $begin));
         $signature .= $_;
+        return "$begin$signature" if $_ eq $end;
     }
 
-    return ((split(/\n+/, $signature, 2))[1]);
+    return;
 }
 
 sub _compare {
     my ($str1, $str2, $ok) = @_;
 
     # normalize all linebreaks
+    $str1 =~ s/^-----BEGIN PGP SIGNED MESSAGE-----\n(?:.+\n)*\n//;
     $str1 =~ s/[^\S ]+/\n/g; $str2 =~ s/[^\S ]+/\n/g;
+    $str1 =~ s/-----BEGIN PGP SIGNATURE-----\n(?:.+\n)*$//;
 
     return $ok if $str1 eq $str2;
 
@@ -345,7 +361,7 @@ sub _compare {
     }
     else {
         local (*D, *S);
-        open S, $SIGNATURE or die "Could not open $SIGNATURE: $!";
+        open S, "< $SIGNATURE" or die "Could not open $SIGNATURE: $!";
         open D, "| diff -u $SIGNATURE -" or (warn "Could not call diff: $!", return SIGNATURE_MISMATCH);
         while (<S>) {
             print D $_ if (1 .. /^-----BEGIN PGP SIGNED MESSAGE-----/);
@@ -412,9 +428,9 @@ sub _sign_gpg {
         die "Cannot find $sigfile.tmp, signing aborted.\n";
     };
 
-    open D, "$sigfile.tmp" or die "Cannot open $sigfile.tmp: $!";
+    open D, "< $sigfile.tmp" or die "Cannot open $sigfile.tmp: $!";
 
-    open S, ">$sigfile" or do {
+    open S, "> $sigfile" or do {
         unlink "$sigfile.tmp";
         die "Could not write to $sigfile: $!";
     };
@@ -597,7 +613,7 @@ sub _mkdigest_files {
         }
         else {
             local *F;
-            open F, $file or die "Cannot open $file for reading: $!";
+            open F, "< $file" or die "Cannot open $file for reading: $!";
             if (-B $file) {
                 binmode(F);
                 $obj->addfile(*F);
@@ -949,7 +965,7 @@ L<Dist::Zilla::Plugin::Signature>
 
 =head1 AUTHORS
 
-唐鳳 E<lt>cpan@audreyt.orgE<gt>
+Audrey Tang E<lt>cpan@audreyt.orgE<gt>
 
 =head1 CC0 1.0 Universal
 

programming_language: Perl