Files
Auto-Shodanner/libs/scanner.py
T
2024-04-20 18:15:46 -06:00

366 lines
8.6 KiB
Python

import subprocess
import requests
import random
import importlib
import socket
import struct
import re
import zlib
import base64
import shutil
import resource
from threading import Thread, Timer
import nmap
import src.utils as utils
import src.jsonpack as jsonpack
import libs.scanutils as scanutils
import libs.scanners.tcpScanner as tcpScanner
import libs.scanners.udpScanner as udpScanner
portScanners = []
tasks = []
excludeRanges = []
upIps = 0
downIps = 0
extendedIps = 0
upIpsPS = 0
downIpsPS = 0
extendedIpsPS = 0
countScannedBeforeStart = 0
running = False
globalSettings = {}
onStatsFunc = None
for script in utils.listSubdirs(utils.getRoot("libs/scanners/")):
if not script.endswith(".py"): continue
if script == "tcpScanner.py": continue
if script == "udpScanner.py": continue
spec = importlib.util.spec_from_file_location(script, utils.getRoot(f'libs/scanners/{script}'))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
portScanners.append(module)
print(f'Imported: {utils.getRoot(f"libs/scanners/{module.__name__}")}')
soft_limit, hard_limit = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (hard_limit, hard_limit))
def start(settings, onStats):
if processStarted(): return
global tasks
global globalSettings
globalSettings = settings
global onStatsFunc
onStatsFunc = onStats
global upIps
global downIps
global extendedIps
upIps = 0
downIps = 0
extendedIps = 0
global upIpsPS
global downIpsPS
global extendedIpsPS
upIpsPS = 0
downIpsPS = 0
extendedIpsPS = 0
global countScannedBeforeStart
countScannedBeforeStart = scanutils.countScannedIps()
print("\n\nStarted Scanner!")
print("\n\n\n", end='')
utils.makeDir("data/scans")
portString = ""
match settings['udpSettings']['mode']:
case -1:
pass
case 1:
portString += "U:" + ",".join(map(str, settings['udpSettings']['ports']))
case 2:
portString += "U:" + ",".join(map(str, scanutils.getMostCommon('udp', settings['udpSettings']['topCount'])))
case 3:
portString += "U:" + ",".join(map(str, scanutils.portsRelatedTo('udp', settings['udpSettings']['relatedString'])))
if settings['tcpSettings']['mode'] != -1 and settings['udpSettings']['mode'] != -1:
portString += ","
match settings['tcpSettings']['mode']:
case -1:
pass
case 1:
portString += "T:" + ",".join(map(str, settings['tcpSettings']['ports']))
case 2:
portString += "T:" + ",".join(map(str, scanutils.getMostCommon('tcp', settings['tcpSettings']['topCount'])))
case 3:
portString += "T:" + ",".join(map(str, scanutils.portsRelatedTo('tcp', settings['tcpSettings']['relatedString'])))
global excludeRanges
excludeRanges = scanutils.parseIpList(utils.getRoot("exclude.conf"))
global running
running = True
for i in range(0,settings['numJobs']):
c = ScanTask(i+1)
t = Thread(target = c.run, args=(
settings['maxPingTimeout'],
settings['maxNmapTimeout'],
settings['nmapGroupSize'],
portString,))
t.start()
tasks.append(c)
statsTimer()
def stop():
if not processStarted(): return
global tasks
global running
tasks = []
running = False
print("\n\nstopped Scanner!")
def processStarted():
global tasks
return len(tasks) != 0;
class hostScanDetail:
def __init__(self):
self.address = None
self.hostname = None
# self.
def statsTimer():
if running:
Timer(1.0, statsTimer).start()
global upIps
global downIps
global extendedIps
global upIpsPS
global downIpsPS
global extendedIpsPS
global numJobs
global countScannedBeforeStart
hostSearchingCount = 0
nmapScanningCount = 0
furtherScanningCount = 0
for task in tasks:
match task.status:
case 1:
hostSearchingCount += 1
case 2:
nmapScanningCount += 1
case 3:
furtherScanningCount += 1
stats = {
"upIps": upIps,
"downIps": downIps,
"extendedIps": extendedIps,
"upIpsPS": upIpsPS,
"downIpsPS": downIpsPS,
"extendedIpsPS": extendedIpsPS,
"hostSearchingCount": hostSearchingCount,
"nmapScanningCount": nmapScanningCount,
"furtherScanningCount": furtherScanningCount,
"countScannedBeforeStart": countScannedBeforeStart,
"numJobs": int(globalSettings['numJobs'])
}
upIpsPS = 0
downIpsPS = 0
extendedIpsPS = 0
onStatsFunc(stats)
def parseNmapResult(result: object, host: str):
# dict_keys(['hostnames', 'addresses', 'vendor', 'status', 'tcp', 'portused', 'osmatch'])
# resultstr = '### Start Host Info ###\n'
# location = scanutils.geolocation(host)
# location.pop('ip', None) # The geolocation string does not have to include the IP again
# compressedLocationData = base64.b64encode(
# zlib.compress(
# jsonpack.pack(location).encode())
# ).decode('ASCII')
resultstr = f'{host},1,{result.hostname()}'
osInfo = []
if 'osmatch' in result:
for os in result['osmatch']:
osInfo.append([int(os["accuracy"]), os["name"]])
resultstr += f',{osInfo}'
resultstr += ',['
first = True
for protocol in result.all_protocols():
for portInt in result[protocol].keys():
port = result[protocol][portInt]
if not first:
resultstr += ','
first = False
resultstr += f'[{portInt},{protocol},{port["state"]},{port["reason"]}'
if port['state'] == 'open':
data = scan(host, portInt, protocol)
compressedData = base64.b64encode(zlib.compress(data.encode())).decode('ASCII')
resultstr += f',{compressedData}'
resultstr += "]"
resultstr += "]"
if len(result.all_protocols()) > 0:
global extendedIps
global extendedIpsPS
extendedIps += 1
extendedIpsPS += 1
# print(resultstr)
write(host, resultstr)
def addOfflineHost(host:str):
write(host, f'{host},0')
# print(string, end='')
def write(host:str, data:str):
split = host.split(".")
utils.makeDir(utils.getRoot(f"data/scans/{split[0]}/"))
with open(utils.getRoot(f"data/scans/{split[0]}/{split[1]}.txt"), "a") as file:
file.write(data + "\n")
def scan(host:str, port: int, protocol: str):
error = False
results = ""
for scanner in portScanners:
if str(scanner.__name__) == f'{protocol}{port}.py':
scanResults, error = scanner.scan(host, port)
results += f'[{scanner.__name__}, {host}:{port}] {scanResults}'
if not error:
return results
else:
results += " Trying default scanner... "
if protocol == "tcp":
scanResults, error = tcpScanner.scan(host, port)
results += f'[{tcpScanner.__name__}, {host}:{port}] {scanResults}'
elif protocol == "udp":
scanResults, error = udpScanner.scan(host, port)
results += f'[{udpScanner.__name__}, {host}:{port}] {scanResults}'
else:
raise Exception("This should not happen...")
return results
# def saveData()
def printBar(percentage: float, cols: int):
return ("#" * round(percentage*cols)) + ("-" * round((1-percentage) * cols))
class ScanTask:
def __init__(self, threadid: int):
self.threadid = threadid
self.nm = nmap.PortScanner()
self.pingsock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
self.status = None
def run(self, maxPingTimeout: int, maxNmapTimeout: int, nmapGroupSize: int, portString: str):
global upIps
global downIps
global extendedIps
global upIpsPS
global downIpsPS
global extendedIpsPS
global excludeRanges
global running
while running:
self.status = 1
ipGroup = []
while len(ipGroup) < nmapGroupSize and running:
address = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
if scanutils.ipInArray(address, excludeRanges):
# print(f"Tried {address}")
continue
if scanutils.ping(address, maxPingTimeout, self.pingsock):
# print(f"{self.threadid} {address}: FOUND {len(ipGroup)+1}/{nmapGroupSize}")
upIps += 1
upIpsPS += 1
ipGroup.append(address)
else:
addOfflineHost(address)
downIps += 1
downIpsPS += 1
# print(f"{self.threadid} {address}: FAIL")
continue
if not running: return
self.status = 2
self.nm.scan(hosts=' '.join(ipGroup), ports=portString, arguments="-O --send-eth --privileged -sS --reason -sU")
if not running: return
self.status = 3
for address in self.nm.all_hosts():
parseNmapResult(self.nm[address], address)