├── .gitignore ├── README.md └── scanDetector.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PortScanDetector 2 | Linux port scan detector. Will add a rule to iptables or firewalld to log TCP packets and ban them if they hit more than 10 ports. 3 | 4 | ## Requires 5 | - [Python 2.7](https://www.python.org/download/releases/2.7/) 6 | 7 | ## Useage 8 | Make sure to set isIpTables to True or False depending on your system. Currently only supports iptables and firewalld. 9 | 10 | `$ sudo python scanDetector.py` 11 | -------------------------------------------------------------------------------- /scanDetector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import re 4 | import os 5 | import subprocess 6 | 7 | # Change this to True if you are using iptables 8 | isIptables = False 9 | 10 | 11 | def iptables(): 12 | if '-A INPUT -p tcp -j LOG --log-prefix " INPUT TCP "' not in subprocess.check_output(["iptables", "-S"]): 13 | print "Rule not there" 14 | # rule = '-A INPUT -p tcp -j LOG --log-prefix " INPUT TCP " ' 15 | subprocess.call( 16 | ['iptables', '-A', 'INPUT', '-p', 'tcp', '-j', 'LOG', '--log-prefix', ' INPUT TCP ', '--log-level', '4']) 17 | 18 | 19 | def firewalld(): 20 | if 'rule family="ipv4" source NOT address="0.0.0.0" log prefix="INPUT TCP " accept' not in subprocess.check_output( 21 | ["firewall-cmd", "--list-all-zones"]): 22 | print "Rule not there" 23 | # rule = '-A INPUT -p tcp -j LOG --log-prefix " INPUT TCP " ' 24 | os.system( 25 | 'firewall-cmd --add-rich-rule=\'rule family="ipv4" source address=0.0.0.0 invert="true" log prefix="INPUT TCP " accept\'') 26 | 27 | 28 | def isInFirewall(ip): 29 | if isIptables: 30 | if ip in subprocess.check_output(["iptables", "-S"]): 31 | return True 32 | else: 33 | if ip in subprocess.check_output(["firewall-cmd", "--list-all-zones"]): 34 | return True 35 | return False 36 | 37 | 38 | def banIp(ip): 39 | print "BANNING:", ip 40 | if isIptables: 41 | subprocess.call(['iptables', '-A', 'INPUT', '-s', ip, '-j', 'DROP']) 42 | else: 43 | os.system('firewall-cmd --add-rich-rule=\'rule family="ipv4" source address="' + ip + '" drop\'') 44 | 45 | 46 | if isIptables: 47 | iptables() 48 | else: 49 | firewalld() 50 | 51 | try: 52 | log = open("/var/log/kern.log", "r") 53 | except: 54 | log = open("/var/log/messages", "r") 55 | 56 | ips = {} 57 | for line in log: 58 | if "INPUT TCP" in line: 59 | match = re.match(r'^(\w* ?\d* \d*:\d*:\d*) .* INPUT TCP .* SRC=(\S*) .* DPT=(\d*) .*$', line) 60 | if match is None: 61 | continue 62 | timestamp = match.group(1) 63 | ip = match.group(2) 64 | port = match.group(3) 65 | found = False 66 | for key, value in ips.iteritems(): 67 | if ip == key: 68 | if port not in ips[key]: 69 | ips[key].append(port) 70 | found = True 71 | 72 | if found == False: 73 | ips[ip] = [port] 74 | 75 | for key, value in ips.iteritems(): 76 | #TODO: Added check for 10 ports 77 | if not isInFirewall(key): 78 | banIp(key) 79 | --------------------------------------------------------------------------------