Files

366 lines
8.6 KiB
Python
Raw Permalink Normal View History

2024-04-15 22:28:20 -06:00
import subprocess
import requests
2024-04-15 22:28:20 -06:00
import random
import importlib
2024-04-15 22:28:20 -06:00
import socket
import struct
import re
2024-04-19 23:11:46 -06:00
import zlib
import base64
import shutil
import resource
2024-04-20 18:15:46 -06:00
from threading import Thread, Timer
2024-04-15 22:28:20 -06:00
import nmap
2024-04-15 22:28:20 -06:00
import src.utils as utils
2024-04-20 12:52:21 -06:00
import src.jsonpack as jsonpack
import libs.scanutils as scanutils
import libs.scanners.tcpScanner as tcpScanner
import libs.scanners.udpScanner as udpScanner
portScanners = []
tasks = []
2024-04-19 23:11:46 -06:00
excludeRanges = []
upIps = 0
2024-04-20 18:15:46 -06:00
downIps = 0
extendedIps = 0
upIpsPS = 0
downIpsPS = 0
extendedIpsPS = 0
countScannedBeforeStart = 0
running = False
2024-04-19 23:11:46 -06:00
globalSettings = {}
2024-04-20 18:15:46 -06:00
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__}")}')
2024-04-19 23:11:46 -06:00
soft_limit, hard_limit = resource.getrlimit(resource.RLIMIT_NOFILE)
resource.setrlimit(resource.RLIMIT_NOFILE, (hard_limit, hard_limit))
2024-04-15 22:28:20 -06:00
2024-04-20 18:15:46 -06:00
def start(settings, onStats):
if processStarted(): return
global tasks
2024-04-19 23:11:46 -06:00
global globalSettings
globalSettings = settings
2024-04-20 18:15:46 -06:00
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!")
2024-04-19 23:11:46 -06:00
print("\n\n\n", end='')
2024-04-15 22:28:20 -06:00
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'])))
2024-04-19 23:11:46 -06:00
global excludeRanges
excludeRanges = scanutils.parseIpList(utils.getRoot("exclude.conf"))
2024-04-15 22:28:20 -06:00
2024-04-20 18:15:46 -06:00
global running
running = True
2024-04-15 22:28:20 -06:00
for i in range(0,settings['numJobs']):
2024-04-19 23:11:46 -06:00
c = ScanTask(i+1)
t = Thread(target = c.run, args=(
settings['maxPingTimeout'],
settings['maxNmapTimeout'],
settings['nmapGroupSize'],
portString,))
2024-04-15 22:28:20 -06:00
t.start()
tasks.append(c)
2024-04-20 18:15:46 -06:00
statsTimer()
2024-04-15 22:28:20 -06:00
def stop():
2024-04-19 23:11:46 -06:00
if not processStarted(): return
global tasks
2024-04-20 18:15:46 -06:00
global running
tasks = []
2024-04-20 18:15:46 -06:00
running = False
2024-04-15 22:28:20 -06:00
print("\n\nstopped Scanner!")
def processStarted():
global tasks
return len(tasks) != 0;
2024-04-19 23:11:46 -06:00
class hostScanDetail:
def __init__(self):
self.address = None
self.hostname = None
# self.
2024-04-20 18:15:46 -06:00
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)
2024-04-19 23:11:46 -06:00
def parseNmapResult(result: object, host: str):
2024-04-19 23:11:46 -06:00
# dict_keys(['hostnames', 'addresses', 'vendor', 'status', 'tcp', 'portused', 'osmatch'])
2024-04-20 12:52:21 -06:00
# 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')
2024-04-20 12:52:21 -06:00
resultstr = f'{host},1,{result.hostname()}'
2024-04-19 23:11:46 -06:00
osInfo = []
if 'osmatch' in result:
for os in result['osmatch']:
2024-04-20 12:52:21 -06:00
osInfo.append([int(os["accuracy"]), os["name"]])
2024-04-19 23:11:46 -06:00
2024-04-20 12:52:21 -06:00
resultstr += f',{osInfo}'
2024-04-19 23:11:46 -06:00
2024-04-20 12:52:21 -06:00
resultstr += ',['
first = True
for protocol in result.all_protocols():
for portInt in result[protocol].keys():
port = result[protocol][portInt]
2024-04-20 12:52:21 -06:00
if not first:
resultstr += ','
first = False
resultstr += f'[{portInt},{protocol},{port["state"]},{port["reason"]}'
2024-04-19 23:11:46 -06:00
if port['state'] == 'open':
data = scan(host, portInt, protocol)
compressedData = base64.b64encode(zlib.compress(data.encode())).decode('ASCII')
2024-04-20 18:15:46 -06:00
2024-04-19 23:11:46 -06:00
resultstr += f',{compressedData}'
2024-04-20 12:52:21 -06:00
resultstr += "]"
resultstr += "]"
2024-04-20 18:15:46 -06:00
if len(result.all_protocols()) > 0:
global extendedIps
global extendedIpsPS
extendedIps += 1
extendedIpsPS += 1
# print(resultstr)
2024-04-20 12:52:21 -06:00
write(host, resultstr)
2024-04-19 23:11:46 -06:00
def addOfflineHost(host:str):
2024-04-20 12:52:21 -06:00
write(host, f'{host},0')
2024-04-19 23:11:46 -06:00
# print(string, end='')
2024-04-20 12:52:21 -06:00
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")
2024-04-19 23:11:46 -06:00
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()
2024-04-15 22:28:20 -06:00
2024-04-19 23:11:46 -06:00
def printBar(percentage: float, cols: int):
return ("#" * round(percentage*cols)) + ("-" * round((1-percentage) * cols))
2024-04-15 22:28:20 -06:00
class ScanTask:
def __init__(self, threadid: int):
self.threadid = threadid
self.nm = nmap.PortScanner()
2024-04-19 23:11:46 -06:00
self.pingsock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
self.status = None
2024-04-15 22:28:20 -06:00
def run(self, maxPingTimeout: int, maxNmapTimeout: int, nmapGroupSize: int, portString: str):
2024-04-19 23:11:46 -06:00
global upIps
global downIps
2024-04-20 18:15:46 -06:00
global extendedIps
global upIpsPS
global downIpsPS
global extendedIpsPS
2024-04-19 23:11:46 -06:00
global excludeRanges
2024-04-20 18:15:46 -06:00
global running
2024-04-19 23:11:46 -06:00
2024-04-20 18:15:46 -06:00
while running:
2024-04-19 23:11:46 -06:00
self.status = 1
ipGroup = []
2024-04-19 23:11:46 -06:00
2024-04-20 18:15:46 -06:00
while len(ipGroup) < nmapGroupSize and running:
address = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
2024-04-19 23:11:46 -06:00
if scanutils.ipInArray(address, excludeRanges):
# print(f"Tried {address}")
continue
2024-04-19 23:11:46 -06:00
if scanutils.ping(address, maxPingTimeout, self.pingsock):
# print(f"{self.threadid} {address}: FOUND {len(ipGroup)+1}/{nmapGroupSize}")
upIps += 1
2024-04-20 18:15:46 -06:00
upIpsPS += 1
ipGroup.append(address)
2024-04-19 23:11:46 -06:00
else:
addOfflineHost(address)
downIps += 1
2024-04-20 18:15:46 -06:00
downIpsPS += 1
# print(f"{self.threadid} {address}: FAIL")
continue
2024-04-19 23:11:46 -06:00
2024-04-20 18:15:46 -06:00
if not running: return
2024-04-19 23:11:46 -06:00
self.status = 2
self.nm.scan(hosts=' '.join(ipGroup), ports=portString, arguments="-O --send-eth --privileged -sS --reason -sU")
2024-04-20 18:15:46 -06:00
if not running: return
2024-04-19 23:11:46 -06:00
self.status = 3
for address in self.nm.all_hosts():
parseNmapResult(self.nm[address], address)