#!/usr/bin/perl -T # Viralator 0.9pre2.1 5 December 2001 # Author Info: viralator@loddington.com Copyright 2001 Duncan Hall # changes from 0.8->0.9 Open IT S.r.l. (http://www.openit.it): # - Diaolin (diaolin@diaolin.com) # - Marco Ciampa (ciampix@libero.it) # This script is licenced under the GPL. But if you feel the need to # contribute please send RAM from one of your co-workers machines! # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # There you go you have been warned. Dont hold me responsible for anything # that goes wrong with this script or any damage or security # breach that is caused by this script. I dont know your system and it is # up to you to make sure it is secure and stable. Just dont hold # me responsible for anything. # # Instalation Instructions # See the web site http://viralator.loddington.com for further info and FAQs #How it Works # Step 1. Pick up the url of the file the user wants to download # Step 2. Download the file into its own directory on the server using wget # Step 3. Show status of download in pop up window. # Step 4. Run Virus scanner against this file and have virus scanner cure # or delete any infected files # Step 5. Check to see if the file still exists after scan # Step 6. If no virus found start automated download like tucows does # Step 7. If vius found display bad boy virus found message # Step 8. Have close window button on popup windows that deletes the # downloaded file #Collecting the software you need # 1. Squid proxy server www.squid-cache.org (you probably already have this) # 2. Apache Web Server (you probably have this too) # 3. Apache-suexec (some versions of apache come with this already # installed, mandrake does not) See the FAQ, you may not need this! # 4. Squirm Redirector 1.23 for squid www.senet.com.au/squirm/ # (rpm available) # 5. A antivirus scanner - Any of Inoculate, AntiVir, AVP, RAV, Sophos # Sweep, McAfee Trend # 6. This script from viaralator.loddington.com #Installing # 1. I am assuming that you have already got apache, squid and # apache-suexec installed, please refer to the install guide that comes # with each of those programs # # 2. Install squirm 1.23. If you installed squid 1.23 from rpm it expects # to find sqirm.paterns in /usr/etc - I dont know why! # Just copy it there or symlink it from /etc/squid/ # # 3. Add the following lines to your squirm.paterns file change [your proxy # ip] to the ip or address of the proxy server. # # abortregexi (^http://[your proxy ip].*) #eg (^http://192\.168\.100\.1/.*) # abortregexi (^http://[your proxy fully qualified domain name].*) # regexi (^.*\.zip$) http://[yourproxyip]/cgi-bin/viralator.cgi?url=|\1 # regexi (^.*\.doc$) http://[yourproxyip]/cgi-bin/viralator.cgi?url=|\1 # regexi (^.*\.exe$) http://[yourproxyip]/cgi-bin/viralator.cgi?url=|\1 # # keep adding lines for each type of file you wish to scan # Make sure that apache knows about each mime type of the files you want to # scan or all you will get is a screen full of binary. # if you installed squirm 1.0 remove the ^ from each line. # # 4. Edit squid.conf adding squirm as a redirector by adding these lines # # redirect_program /usr/squid/bin/squirm # redirect_children 10 # # 5. Create a user and group for suexec to use. I called mine viralator. # You can ignore this if you are not using suexec. YOU ONLY NEED # TO USE SUEXEC IF THE USER THAT RUNS APACHE CANNOT RUN THE VIRUS SCANNER # # 6. Add the user and group information to your apache vhosts.conf or # httpd.conf file. This enables suexec. Here is an example from my # vhosts.conf file # # < VirtualHost 192.168.100.1> # ServerAdmin webmaster@loddington.com # DocumentRoot /var/www/html # ServerName proxy.loddington.com # ErrorLog logs/error_log # TransferLog logs/access_log # ScriptAlias /cgi-bin/ /var/www/cgi-bin/ # User viralator # Group viralator # # # 7. Create a directory in a web viewable area (inside htdocs or html # directory) called downloads and set permissions to 755 # # 8. Copy this script to your cgi-bin # # 9. Change the ownership and group of your cgi-bin to viralator # (chown viralataor.viralator -R cgi-bin) You can ignore # this if you are not using suexec # # 10. Make sure that you have correct file permissions for this script. # I used chmod 755 viralator.cgi # # 11. Edit the viralator script to make sure you have the correct settings # for the location of your virus scanner, wget, web root and download # directory. It is all together under housekeeping on line 21. # # 12. Restart squid and apache so your changes take effect. # # 13. Since version 0.8 we no longer need to set the users browser to # exclude the proxy IP address from being cached as we # use the abortregexi command in squirm.patterns # # 14. Try to download a zip file, if it does not work look in the apache # error logs or the squirm.error logs # # 15. Browse to http://viralator.loddington.com and try to download my # test zip file. It contains the eicar test virus # signature. It is not a real virus and is harmless but it will set # off most virus scanners. # # 16. Look in the FAQ for solutions to some common problems and information # on how to make wget use the proxy so downloads are cached # # # ################################################################## # # - disabled resize due of javascript problems (re-run) # - javascript windows size enlaged # - added Internationalization & browser language selection # - simpler configuration (one variable!) for different antivirus # - added check on dynamic sites that clobber REQUEST_URI ENV var # see http://www.powerarchiver.com for example... see below # - added file caching (option mantain - not working yet) # # by Open IT S.r.l. (http://www.openit.it): # - Diaolin (diaolin@diaolin.com) # - Marco Ciampa (ciampix@libero.it) # # TODO: currently only ftp 'wget -c' does a real check on file already # downloaded, http re-get it over and over ... has someone a # more efficient way to do the trick? # ################################################################## # A little housekeeping first $default_lang = "en"; #Possible values "it", "en" or write your own lang! $antivirus="INOCULATE"; #Possible values: INOCULATE, AVP, RAV, ANTIVIR, # MCAFEE, SOPHOS, TREND #################################### #Mod by radical\n"; ################################# # # Fake sites # # on many sites it can be found an ugly problem due to the fake URL passed # from remote sites, the result is a strange \ ............. # If you know anything about this sites and you want to permit your users # downloading anyway, you can put a abortregexi into squirm.patterns like this # this example is a site that has convinced us to write this workaround # www.powerarchiver.com # # abortregexi (^http://www\.powerachiver\.com/.*) # # in this case all your users can download from powerarchiver without virus # scanning :-( # # If anyone has a better solution.................. # ################################## if ($url1=~/\s*\?\s*/ | $url1=~/\s*\\\s*/) { # Need to look at this print "\n"; print "

\n"; print " "; ################################# # # End Fake sites # ################################## } else { # OK now we kow the file the user wants to get ($url1) and the page #($requestpage) they found it on. # We now need to actually do something. # Lets start by returning the user to the page they found the file on and # launching a pop up window! # The pop up windows should have some usefull info in it about whats going on. print "\n"; print "

\n"; print $downloading{$lang}." $url1 \n"; print " "; print $q->h3 ("

\n". "\n". "\n". "".$downloading{$lang}. " \"${filename}\"
"); print "\n\n"; #Step 3 # time to improve security and make people happy. # untaint me baby time. delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; $ENV{PATH} = "$wget"; if ($username =~ /([a-zA-Z0-9\.\-\_\@]*)/g) # only allow a-z and A-Z and . - _ and @ { $username = "$1"; } else { $username = ""; } if ($password =~ /([a-zA-Z0-9\.\-\_\@\!\#\$\%\^\&\*\=\+\{\}\:\<\>\?]*)/g) { # prtty much everything except ; " ` ~ ( ) | and spaces $password = "$1"; # There must be a nicer way of doing this } else { # Feel free to make a suggestion $password = ""; } if ($fileurl =~ /([^\;\"\|\<]*)/g) { # Suggestions anyone? $fileurl = "$1"; # just blocking ; " | and < in the url } else { $fileurl = ""; # } if ($filename =~ /([^\;\"\`\<\{\|]*)/g) { $filename = "$1"; # just blocking ; " ` < { and | in the filename } else { $filename = ""; } # To be removed for the next full release - but useful for debugging # # print "<\!\-\- untainted url $fileurl \-\-\>\n"; # print "<\!\-\- untainted filename $filename \-\-\>\n"; open(TR,"wget -c --http-user=".$username. " --http-pass=".$password. " \"$fileurl\" -O $downloads/\"".$filename. "\" 2>&1|") || die "Error"; open (LOG, ">>$logfile"); print LOG "$date $fileurl by $client $username\n"; close LOG; #Sample output for a 401 error #HTTP request sent, awaiting response... 401 Authorization Required #Authorization failed. #Sample Output for a 404 error #HTTP request sent, awaiting response... 404 Not Found #12:00:53 ERROR 404: Not Found. #Sample Output for a host not found #Host not found. while() { if ($_ =~ "404 Not Found") { print "404 ".$filenotfound{$lang}.""; open (LOG, ">>$logfile"); print LOG "$date $fileurl by $client 404 File Not Found\n"; close LOG; exit; } elsif ($_ =~ "Host not found") { print "".$hostnotfound{$lang}.""; open (LOG, ">>$logfile"); print LOG "$date $fileurl by $client Host Not Found\n"; close LOG; exit; } elsif ($_ =~ "Authorization failed.") { print "401 ".$authrequired{$lang}."

\n"; print $pleaseuserpass{$lang}." $fileurl\n"; print "

\n \n \n \n"; print $wusername{$lang}."
\n"; print $wpassword{$lang}." \n \n
\n"; open (LOG, ">>$logfile"); print LOG "$date $fileurl by $client 401 Authorization Required\n"; close LOG; exit; }else { print; print "
"; } } #Step 4 #Yes more untainting delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; $ENV{PATH} = "$virusscannerpath{$antivirus}"; $| = 1; $q = new CGI; print $q->h3 ("".$wviruscan{$lang}. " $filename
".$takeawhile{$lang}."
"); open(TR1," $virusscanner{$antivirus} $viruscmd{$antivirus} $downloads/\"$filename\" 2>&1|") || die "Error"; while() { if ($_ =~ /$virusfound{$antivirus}{$lang} (\s+\d+)/) { if ( $1 > 0) { print; print "\n

".$virusmsg{$lang}." \n

"; unlink "$downloads/$filename"; print $fileremoved{$lang}." \n

"; open (LOG, ">>$logfile"); print LOG "$date $fileurl by $client Infected file removed\n"; close LOG; }else { print; print "
"; } } } ## #If $filename exists in the download dir then it is clean or has been #cleaned (depending on your scanner options),if not then the virus scanner #has renamed or deleted the file (depending on your scanner options) and #it is infected #Step 5 # We check to see if the filename is greater than 1 character long $filenamesize = length ($filename); if ((-e "$downloads/$filename") && ($filenamesize > "1")) { #Step 6 ##Check $filename for spaces or odd charaxters $filename1 = $filename; #this keeps the original value $filename =~ s/([^a-zA-Z0-9_\+\.\-])/"%". unpack("H*",$1)/ge; #This rewebifies the file name. print $oktodown{$lang}."

\n"; print "\n"; print "

\n"; print "".$oncedown{$lang}."

\n"; print "

\n \n \n \n \n"; }else { #step 7 print "\n"; print "

\n"; print $vfounddl{$lang}."\n"; print "
".$virusmsg{$lang}; } } #step 8 # Close window and delete file on server if ($in{'action'} eq "deletefile") { $filename = $in{'filename'}; &PrintHeader; print ""; print ""; print $wremoving{$lang}."
"; print $filename." ".$wfromsrv{$lang}; if ($filename =~ /([^\;\"\`\<\{\|]*)/g) { $filename = "$1"; # just blocking ; " ` < { and | in the filename } else { $filename = ""; } unlink "$downloads/$filename"; } #step 8.1 # Abort function if ($in{'action'} eq "StopMe") { $filename = $in{'filename'}; &PrintHeader; print ""; print "".$downabort{$lang}."
\n"; print $wremoving{$lang}." $filename ".$wfromsrv{$lang}; if ($filename =~ /([^\;\"\`\<\{\|]*)/g) { $filename = "$1"; # just blocking ; " ` < { and | in the filename } else { $filename = ""; } unlink "$downloads/$filename"; } #step 8.1 bis # Mantain files for future downloads (instead of delete them) # If you want to delete old files use cron + rm + find (date based) if ($in{'action'} eq "mantain") { $filename = $in{'filename'}; &PrintHeader; print ""; print "".$downend{$lang}."
\n"; print $maint{$lang}." $filename ".$forfuture{$lang}; } ######### # # Without the header stuff you cant display a web page # # Why not using perl CGI lib????? Have not got around to it yet feel free to change # ######### sub PrintHeader { print "Content-type: text/html\n\n"; } ################ # # This is a bit of web detangeling a result of what browsers pass back to the # server. # It needs some culling as I grabed it from an old script I had for form # processing # # Why not using perl CGI lib????? Have not got around to it yet feel free to change # ############### sub UnWeb1 { # Split the name-value pairs @pairs = split(/&/, $ENV{'QUERY_STRING'}); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); # Un-Webify plus signs and %-encoding $value =~ tr/+/ /; #de-plusify $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; #unwebfy $value =~ s///g; #de-comment $value =~ s/<([^>]|\n)*>//g; #untag $in{$name} = $value; #assign to % } } ################################################################## # # This little sub try to guess the web client preferred language # ################################################################## sub choose_lang{ $httplang=$ENV{"HTTP_ACCEPT_LANGUAGE"}; (@httplang)=split(/\,\s+/,$httplang); $lang=$default_lang; foreach $slang (@httplang){ if ($slang eq "it"){$lang=$slang; last} if ($slang eq "en"){$lang=$slang; last} } }