AuthFail python script

14 10 2011

I wanted to improve this script that I had originally written in bash and I took the opportunity to learn python (three days).

Please, comment! I need criticism… more precisely about the thread management, I understand that’s bad programming.

In fact the portscan takes about 4 minutes with 50 hosts… but work!

Usage of the script:

usage:
 python authfail.py FILE [FILE]

 Parse an auth.log FILE and return the ascending stats list with
 geolocalization about ip addresses that cause a sshd's auth failure.

 If you specify the [FILE] it checks if IP addresses which attacks
 came from and have sshd's port 22 open and save the results on it.

The auth.log file path is:

/var/log/auth.log

however may change…

This software is under GPLv3 license.

Here the source code:

"""
    Copyright (C) 2011  Simone Aonzo

    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 3 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, see <http://www.gnu.org/licenses/>.
"""

import threading, Queue
import urllib2
import socket
import socket
import sys
import re

# Keyword in auth.log for invalid login
authFail = "Failed password for invalid user"

# Regex of ip address (tnx evilsocket for compile suggest!)
ipRegEx = re.compile(r"\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b")

# Website that provide api
apiHostIp = "http://api.hostip.info/get_html.php?ip="

# Dictionary and List initializer
dictBase = {}
listAtt = []

# sshd port
SSHPORT = 22

#scan flag
SCAN = False

# Usage
help = """

	AuthFail 1.0 - by Six110

http://sixthevicious.wordpress.com/

========================================
	usage:
            python authfail.py FILE [FILE]

	Parse an auth.log FILE and return the ascending stats list with
	geolocalization about ip addresses that cause a sshd's auth failure.

	If you specify the [FILE] it checks if IP addresses which attacks
	came from and have sshd's port 22 open and save the results on it.

	This software is released under GPLv3 license.
"""

# Check if input string match the regexp
def ipFormatChk (ipStr):
   if ipRegEx.match(ipStr):
      return True
   else:
      return False

# Convert a list of tuple into the ip address string
def fromRegexToString (line):
	return (" ".join( ipRegEx.findall(line)[0] )).replace(" ",".")

# Class with data of the attacker
class Attacker(object):
	def __init__(self, ip, hits, country, city, isopenssh=None):
		self.ip = ip
		self.hits = hits
		self.country = country
		self.city = city
		self.isopenssh = isopenssh

# Class that implement portscanning with threading
class Scanner(threading.Thread):
    def __init__(self, inq, outq):
        threading.Thread.__init__(self)
        self.setDaemon(1)
        # queues for Attackers
        self.inq = inq
        self.outq = outq

    def run(self):
        while True:
            # Retrive attacker from queue
            a = self.inq.get()
            host, port = a.ip, SSHPORT
            sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            try:
                sd.connect((host, port))
            except socket.error:
				a.isopenssh = "False"
				self.outq.put(a)
            else:
				a.isopenssh = "True"
				self.outq.put(a)
				sd.close()

#======================================================================#

alen = len(sys.argv)

if (alen < 2) or (alen > 3):
	print help
	quit()

if (alen == 3):
	try:
		fileOUT = open (sys.argv[2], 'w')
		SCAN = True
	except:
		fileOUT = None
		print "Error! I can't write this file -> " + sys.argv[2]
else:
	fileOUT = None

try:
	fileIN = open(sys.argv[1], "r")
except:
	fileIN = None
	print "Error! I can't read this file -> " + sys.argv[1]
	sys.exit(1)

line = fileIN.readline()

# Read each line of the file
# if found an ip it search the dictionary for the entry
# if it's present, increment the counter
# otherwise add the ip and initialize the counter
while line:
	if (line.find(authFail) != -1):
		ipFound = fromRegexToString (line)
		if ( ipFound in dictBase):
			dictBase[ipFound] = dictBase.get(ipFound)+1
		else :
			dictBase[ipFound] = 1
	line = fileIN.readline()

# Close the input files
fileIN.close()

items = 0
# Create the classes and add them to the list counting the entries
for ip, hits in dictBase.iteritems():
	resp = urllib2.urlopen( apiHostIp+ip ).read()
	country = resp [ resp.find('Country: ')+9 : resp.find('\n') ]
	city = resp [ resp.find('City: ')+6 : resp.find('\n',resp.find('City: ')) ]
	listAtt.append( Attacker(ip, hits, country, city) )
	items += 1

# Free the dictionary
dictBase.clear()

# Start ssh scanner
if (SCAN):
	toscan = Queue.Queue()
	scanned = Queue.Queue()
	scanners = [Scanner(toscan, scanned) for i in range(items)]

	for scanner in scanners:
		scanner.start()
	for a in listAtt:
		toscan.put(a)
	listAtt.count
	for c in range(0,items):
		try:
			scanned.get(16)
		except Queue.Empty:
			c = items

# Redefines the sort function with the total order relation
# for confront two Attacker classes
listAtt.sort (lambda x, y: cmp(y.hits, x.hits) )

for a in listAtt:
	if ( fileOUT != None and SCAN == True and a.isopenssh == "True"):
		fileOUT.write(a.ip+"\n")
	print "\nIp: "+ a.ip + "\nHits: %d"% a.hits + "\nPort %s open: "%SSHPORT + str(a.isopenssh) + "\nCountry: %s"%a.country + "\nCity: %s\n"%a.city

# Close the output file
if (fileOUT != None):
	fileOUT.close()

Azioni

Informazione

Lascia un Commento

Fill in your details below or click an icon to log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Log Out / Modifica )

Foto Twitter

You are commenting using your Twitter account. Log Out / Modifica )

Foto di Facebook

You are commenting using your Facebook account. Log Out / Modifica )

Connecting to %s




Follow

Get every new post delivered to your Inbox.

Join 401 other followers