DMA[2005-0131a] - 'Setuid Perl PERLIO_DEBUG root owned file creation' Author: Kevin Finisterre Vendor: http://dev.perl.org/ Product: 'Perl 5.8.x - sperl' References: (CAN-2005-0155) http://www.digitalmunition.com/DMA[2005-0131a].txt http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0155 https://rt.perl.org/rt3/Ticket/Display.html?id=33990 (guest/guest) http://www.mail-archive.com/perl5-changes@perl.org/msg10733.html http://www.mail-archive.com/perl5-changes@perl.org/msg10734.html http://www.mail-archive.com/perl5-changes@perl.org/msg10736.html http://www.mail-archive.com/perl5-changes@perl.org/msg10737.html Description: Perl is a stable, cross platform programming language. It is used for mission critical projects in the public and private sectors and is widely used to program web applications of all needs. In the July 18, 2002 highlights for Perl 5.8.0 there was a 'New IO Implementation' added called PerlIO. The new PerlIO implementation was described as both a portable stdio implementation (at the source code level) and a flexible new framework for richer I/O behaviours. As an attacker I would definately say that PerlIO has some rich behavior. Two vulnerabilities were located in the PerlIO package that can allow an attacker to take root on a machine that makes use of setuid perl aka sperl. The first vulnerability will be outlined in this document, and details on the second vulnerability will be explained in DMA[2005-0131b]. Perl provides debug access to PerlIO via an environment variable known as PERLIO_DEBUG. The perl documentaion tells us that if PERLIO_DEBUG is set to the name of a file or device then certain operations of PerlIO sub-system will be logged to that file in append mode. If the file does not exist then it will be created. This vulnerability really does not present itself unless perl is installed with setuid support on the machine in question. If setuid is enabled an attacker has the opportunity to arbitrarily create root owned files. With a little help from umask() files can be created with root ownership AND world writable permissions. This behavior can also be exploited via setuid helper or wrapper scripts that call perl scripts (see the Debian game mooix). kfinisterre@jdam:~$ ls -al /usr/bin/sperl5.8.4 -rwsr-xr-x 1 root root 63808 2004-12-11 18:32 /usr/bin/sperl5.8.4 kfinisterre@jdam:~$ export PERLIO_DEBUG=/tmp/aaa kfinisterre@jdam:~$ umask 001 kfinisterre@jdam:~$ /usr/bin/sperl5.8.4 sperl needs fd script You should not call sperl directly; do you need to change a #! line from sperl to perl? kfinisterre@jdam:~$ ls -al /tmp/aaa -rw-rw-rw- 1 root kfinisterre 1403 2005-01-30 02:34 /tmp/aaa At this point the game is pretty much over. Since the file is world writable the attacker can add any content he or she desires to the file that was created. Charles Stevenson suggested that a file could be written to /etc/crond.d/xxxx which would allow an attacker to control the machine with the next run of cron. I considered a few alternatives like writing to roots crontab, making an sshd root authorized_key file, as well as a few others. In my example exploit I took a more immediate and risky route by writing to /etc/ld.so.preload and providing a trojan.so that always returns 0 for getuid(). kfinisterre@jdam:~$ cc -o ex_perl ex_perl.c kfinisterre@jdam:~$ ls -al /etc/ld.so.preload ls: /etc/ld.so.preload: No such file or directory kfinisterre@jdam:~$ ./ex_perl sperl needs fd script You should not call sperl directly; do you need to change a #! line from sperl to perl? kfinisterre@jdam:~$ su - jdam:~# id uid=0(root) gid=0(root) groups=0(root) jdam:~# rm /etc/ld.so.preload The following patch for this bug was provided by Mandrake care of the vendor-sec list. This patch also fixes the bug that is described in DMA[2005-0131b]. Index: perlio.c =================================================================== --- perlio.c (revision 4342) +++ perlio.c (revision 4346) @@ -454,7 +454,7 @@ va_list ap; dSYS; va_start(ap, fmt); - if (!dbg) { + if (!dbg && !PL_tainting && PL_uid == PL_euid && PL_gid == PL_egid) { char *s = PerlEnv_getenv("PERLIO_DEBUG"); if (s && *s) dbg = PerlLIO_open3(s, O_WRONLY | O_CREAT | O_APPEND, 0666); @@ -471,7 +471,7 @@ s = CopFILE(PL_curcop); if (!s) s = "(none)"; - sprintf(buffer, "%s:%" IVdf " ", s, (IV) CopLINE(PL_curcop)); + sprintf(buffer, "%.40s:%" IVdf " ", s, (IV) CopLINE(PL_curcop)); len = strlen(buffer); vsprintf(buffer+len, fmt, ap); PerlLIO_write(dbg, buffer, strlen(buffer)); an alternate patch was also provided. This patch adds some indication of patching to 'perl -V' --- perlio.c.orig Fri Sep 10 08:06:52 2004 +++ perlio.c Tue Feb 1 22:17:45 2005 @@ -471,7 +471,7 @@ PerlIO_debug(const char *fmt, ...) s = CopFILE(PL_curcop); if (!s) s = "(none)"; - sprintf(buffer, "%s:%" IVdf " ", s, (IV) CopLINE(PL_curcop)); + sprintf(buffer, "%.40s:%" IVdf " ", s, (IV) CopLINE(PL_curcop)); len = strlen(buffer); vsprintf(buffer+len, fmt, ap); PerlLIO_write(dbg, buffer, strlen(buffer)); --- patchlevel.h.orig Sat Nov 27 17:18:15 2004 +++ patchlevel.h Tue Feb 1 22:19:59 2005 @@ -123,3 +123,4 @@ hunk. + ,"SUIDPERLIO1 - fix PERLIO_DEBUG buffer overflow (CAN-2005-0156)" ,NULL }; This bug has been successfully exploited on: Debian 3.1 Ubuntu 4.10 Redhat 8.0 This is basic timeline associated with this bug. 01/30/2005 09:29 AM - Mail to larry wall, perlbug, vendor-sec et all 01/31/2005 04:25 AM - Rafael Garcia-Suarez disabed PERLIO_DEBUG in sperl 01/31/2005 08:31 AM - perl #33990] [RESOLVED] 01/31/2005 11:15 AM - perl-5.8.6-bug33990.patch passed on from Mandrake cvs 02/02/2005 05:20 PM - Alternate patch provided nick@ccl4.org -KF