2024-04-15 22:28:20 -06:00
|
|
|
import subprocess
|
2024-04-17 21:46:53 -06:00
|
|
|
import requests
|
2024-04-15 22:28:20 -06:00
|
|
|
import random
|
2024-04-17 21:46:53 -06:00
|
|
|
import importlib
|
2024-04-15 22:28:20 -06:00
|
|
|
import socket
|
|
|
|
|
import struct
|
2024-04-17 21:46:53 -06:00
|
|
|
import re
|
2024-04-19 23:11:46 -06:00
|
|
|
import zlib
|
|
|
|
|
import base64
|
|
|
|
|
import shutil
|
|
|
|
|
import resource
|
2024-04-15 22:28:20 -06:00
|
|
|
from threading import Thread
|
|
|
|
|
|
2024-04-18 13:51:45 -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
|
2024-04-17 21:46:53 -06:00
|
|
|
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 = []
|
|
|
|
|
|
|
|
|
|
downIps = 0
|
|
|
|
|
upIps = 0
|
|
|
|
|
globalSettings = {}
|
2024-04-17 21:46:53 -06:00
|
|
|
|
|
|
|
|
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-17 21:46:53 -06:00
|
|
|
|
2024-04-15 22:28:20 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def start(settings):
|
2024-04-17 21:46:53 -06:00
|
|
|
global tasks
|
2024-04-19 23:11:46 -06:00
|
|
|
global globalSettings
|
|
|
|
|
globalSettings = settings
|
2024-04-17 21:46:53 -06:00
|
|
|
|
|
|
|
|
if processStarted(): return
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
|
2024-04-17 21:46:53 -06:00
|
|
|
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
|
|
|
|
|
|
|
|
for i in range(0,settings['numJobs']):
|
2024-04-19 23:11:46 -06:00
|
|
|
c = ScanTask(i+1)
|
2024-04-18 13:51:45 -06:00
|
|
|
t = Thread(target = c.run, args=(
|
|
|
|
|
settings['maxPingTimeout'],
|
|
|
|
|
settings['maxNmapTimeout'],
|
|
|
|
|
settings['nmapGroupSize'],
|
|
|
|
|
portString,))
|
2024-04-15 22:28:20 -06:00
|
|
|
t.start()
|
2024-04-17 21:46:53 -06:00
|
|
|
tasks.append(c)
|
2024-04-15 22:28:20 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def stop():
|
2024-04-19 23:11:46 -06:00
|
|
|
if not processStarted(): return
|
2024-04-17 21:46:53 -06:00
|
|
|
global tasks
|
|
|
|
|
for task in tasks:
|
|
|
|
|
task.stop()
|
|
|
|
|
tasks = []
|
2024-04-15 22:28:20 -06:00
|
|
|
print("\n\nstopped Scanner!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def processStarted():
|
2024-04-17 21:46:53 -06:00
|
|
|
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-17 21:46:53 -06:00
|
|
|
|
2024-04-18 13:51:45 -06:00
|
|
|
def parseNmapResult(result: object, host: str):
|
2024-04-17 21:46:53 -06:00
|
|
|
|
2024-04-19 23:11:46 -06:00
|
|
|
# dict_keys(['hostnames', 'addresses', 'vendor', 'status', 'tcp', 'portused', 'osmatch'])
|
2024-04-17 21:46:53 -06:00
|
|
|
|
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-17 21:46:53 -06:00
|
|
|
|
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
|
2024-04-18 13:51:45 -06:00
|
|
|
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')
|
|
|
|
|
|
|
|
|
|
resultstr += f',{compressedData}'
|
2024-04-18 13:51:45 -06:00
|
|
|
|
2024-04-20 12:52:21 -06:00
|
|
|
resultstr += "]"
|
|
|
|
|
|
|
|
|
|
resultstr += "]"
|
2024-04-18 13:51:45 -06:00
|
|
|
|
2024-04-20 12:52:21 -06:00
|
|
|
print(resultstr)
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def printIndicator():
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
width = shutil.get_terminal_size((80, 20)).columns
|
|
|
|
|
global globalSettings
|
|
|
|
|
numJobs = int(globalSettings['numJobs'])
|
|
|
|
|
|
|
|
|
|
print("\033[F\033[F\033[F" +
|
|
|
|
|
f"P: {printBar(hostSearchingCount/numJobs,(width-3))}\n" +
|
|
|
|
|
f"N: {printBar(nmapScanningCount/numJobs,(width-3))}\n" +
|
|
|
|
|
f"S: {printBar(furtherScanningCount/numJobs,(width-3))}\n", end="")
|
|
|
|
|
# print(f"1: {hostSearchingCount}, " +
|
|
|
|
|
# f"2: {nmapScanningCount}, " +
|
|
|
|
|
# f"3: {furtherScanningCount}", end="\r")
|
|
|
|
|
|
2024-04-15 22:28:20 -06:00
|
|
|
class ScanTask:
|
2024-04-17 21:46:53 -06:00
|
|
|
def __init__(self, threadid: int):
|
|
|
|
|
self.threadid = threadid
|
2024-04-15 22:28:20 -06:00
|
|
|
self.running = True
|
2024-04-18 13:51:45 -06:00
|
|
|
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 stop(self):
|
2024-04-19 23:11:46 -06:00
|
|
|
self.running = False
|
2024-04-15 22:28:20 -06:00
|
|
|
|
2024-04-18 13:51:45 -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
|
|
|
|
|
global excludeRanges
|
|
|
|
|
|
|
|
|
|
|
2024-04-17 21:46:53 -06:00
|
|
|
while self.running:
|
2024-04-19 23:11:46 -06:00
|
|
|
|
|
|
|
|
self.status = 1
|
|
|
|
|
printIndicator()
|
2024-04-18 13:51:45 -06:00
|
|
|
ipGroup = []
|
2024-04-19 23:11:46 -06:00
|
|
|
|
2024-04-18 13:51:45 -06:00
|
|
|
while len(ipGroup) < nmapGroupSize and self.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-18 13:51:45 -06:00
|
|
|
|
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-18 13:51:45 -06:00
|
|
|
ipGroup.append(address)
|
2024-04-19 23:11:46 -06:00
|
|
|
else:
|
|
|
|
|
addOfflineHost(address)
|
|
|
|
|
downIps += 1
|
2024-04-18 13:51:45 -06:00
|
|
|
# print(f"{self.threadid} {address}: FAIL")
|
|
|
|
|
continue
|
2024-04-19 23:11:46 -06:00
|
|
|
|
|
|
|
|
if not self.running: return
|
|
|
|
|
|
|
|
|
|
self.status = 2
|
|
|
|
|
printIndicator()
|
2024-04-18 13:51:45 -06:00
|
|
|
|
|
|
|
|
self.nm.scan(hosts=' '.join(ipGroup), ports=portString, arguments="-O --send-eth --privileged -sS --reason -sU")
|
|
|
|
|
|
2024-04-19 23:11:46 -06:00
|
|
|
if not self.running: return
|
|
|
|
|
|
|
|
|
|
self.status = 3
|
|
|
|
|
printIndicator()
|
|
|
|
|
|
2024-04-18 13:51:45 -06:00
|
|
|
for address in self.nm.all_hosts():
|
|
|
|
|
parseNmapResult(self.nm[address], address)
|