mirror of
https://github.com/Astatin3/Auto-Shodanner.git
synced 2026-06-09 00:28:00 -06:00
Add port scanners, better scanning, geo location
This commit is contained in:
+111
-24
@@ -1,68 +1,155 @@
|
||||
import subprocess
|
||||
import requests
|
||||
import random
|
||||
import importlib
|
||||
import socket
|
||||
import struct
|
||||
import re
|
||||
from threading import Thread
|
||||
|
||||
import src.utils as utils
|
||||
import libs.scanutils as scanutils
|
||||
|
||||
import libs.scanners.tcpScanner as tcpScanner
|
||||
import libs.scanners.udpScanner as udpScanner
|
||||
|
||||
portScanners = []
|
||||
tasks = []
|
||||
|
||||
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__}")}')
|
||||
|
||||
|
||||
|
||||
def getScanner(port: int, protocol: str):
|
||||
for scanner in portScanners:
|
||||
if str(scanner.__name__) == f'{port}.{protocol}.py':
|
||||
return scanner
|
||||
if protocol == "tcp":
|
||||
return tcpScanner
|
||||
elif protocol == "udp":
|
||||
return udpScanner
|
||||
else:
|
||||
raise Exception("This should not happen...")
|
||||
|
||||
|
||||
threads = []
|
||||
|
||||
def start(settings):
|
||||
global threads
|
||||
global tasks
|
||||
|
||||
if processStarted(): return
|
||||
|
||||
print("\n\nStarted Scanner!")
|
||||
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'])))
|
||||
|
||||
|
||||
for i in range(0,settings['numJobs']):
|
||||
c = ScanTask()
|
||||
t = Thread(target = c.run, args=(settings['maxPingTimeout'],))
|
||||
c = ScanTask(i)
|
||||
t = Thread(target = c.run, args=(settings['maxPingTimeout'],portString,))
|
||||
t.start()
|
||||
|
||||
# def getStdout():
|
||||
# global process
|
||||
# return subprocess.check_output(process).decode()
|
||||
# return "eee" + process.stdout.readline()
|
||||
tasks.append(c)
|
||||
|
||||
|
||||
def stop():
|
||||
global threads
|
||||
for thread in threads:
|
||||
thread.stop()
|
||||
threads = []
|
||||
global tasks
|
||||
for task in tasks:
|
||||
task.stop()
|
||||
tasks = []
|
||||
print("\n\nstopped Scanner!")
|
||||
|
||||
|
||||
def processStarted():
|
||||
global threads
|
||||
return len(threads) != 0;
|
||||
global tasks
|
||||
return len(tasks) != 0;
|
||||
|
||||
|
||||
|
||||
def parseNmapResult(result: str, address: str):
|
||||
return
|
||||
|
||||
|
||||
ports = scanutils.getPorts(result)
|
||||
hostname = scanutils.getHostname(result)
|
||||
resultstr = f'### {address} ({hostname}) {ports}\n'
|
||||
|
||||
# resultstr += f'Location: {scanutils.geolocation(address)}\n'
|
||||
|
||||
for port in ports:
|
||||
if port[1] != 'open':
|
||||
continue
|
||||
# resultstr += str(port) + '\n'
|
||||
|
||||
portInt = int(port[0].split("/")[0])
|
||||
protocol = port[0].split("/")[1]
|
||||
scanner = getScanner(portInt, protocol)
|
||||
|
||||
resultstr += f'[{scanner.__name__}]\n'
|
||||
resultstr += scanner.scan(address, portInt) + "\n"
|
||||
|
||||
# print(resultstr)
|
||||
|
||||
|
||||
class ScanTask:
|
||||
def __init__(self):
|
||||
def __init__(self, threadid: int):
|
||||
self.threadid = threadid
|
||||
self.running = True
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
|
||||
def run(self, maxPingTimeout):
|
||||
while True:
|
||||
def run(self, maxPingTimeout: int, portString: str):
|
||||
while self.running:
|
||||
address = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||
|
||||
pingCommand = f"ping {address} -c 1 -W {maxPingTimeout}"
|
||||
|
||||
try:
|
||||
subprocess.check_output(pingCommand.split(" "))
|
||||
# print(f"{address}: FOUND")
|
||||
# print(f"{self.threadid} {address}: FOUND")
|
||||
except subprocess.CalledProcessError:
|
||||
# print(f"{address}: FAIL")
|
||||
# print(f"{self.threadid} {address}: FAIL")
|
||||
continue
|
||||
|
||||
nmapCommand = f"sudo nmap {address} -O --send-eth --privileged -v -sS"
|
||||
|
||||
|
||||
nmapCommand = f"sudo nmap {address} -O --send-eth --privileged -v -sS --reason -sU -p {portString}"
|
||||
|
||||
|
||||
try:
|
||||
print(subprocess.check_output(nmapCommand.split(" ")).decode())
|
||||
parseNmapResult(subprocess.check_output(nmapCommand.split(" ")).decode(), address)
|
||||
except subprocess.CalledProcessError:
|
||||
continue
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import random
|
||||
import datetime
|
||||
import socket
|
||||
import ssl
|
||||
import OpenSSL
|
||||
|
||||
from faker import Faker
|
||||
|
||||
|
||||
def generate_headers():
|
||||
fake = Faker()
|
||||
return "\r\n".join([
|
||||
f"User-Agent: {fake.user_agent()}",
|
||||
f"Accept: {fake.mime_type()}",
|
||||
f"Accept-Language: {fake.language_code()},{fake.language_code()};q=0.9",
|
||||
f"Accept-Encoding: {fake.mime_type()}, {fake.mime_type()}, {fake.mime_type()}",
|
||||
f"Referer: {fake.url()}",
|
||||
f"Connection: {random.choice(['keep-alive', 'close'])}",
|
||||
f"Cache-Control: {random.choice(['no-cache', 'max-age=0'])}",
|
||||
f"Pragma: {random.choice(['no-cache', ''])}",
|
||||
]) + "\r\n\r\n"
|
||||
|
||||
|
||||
|
||||
def get_ssl_cert_info(cert_data):
|
||||
if not cert_data:
|
||||
return ""
|
||||
|
||||
cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1, cert_data)
|
||||
|
||||
string = "Certificate Information:\n"
|
||||
string += "------------------------\n"
|
||||
|
||||
string += "Subject:\n"
|
||||
subject = cert.get_subject()
|
||||
string += f" Common Name (CN): {subject.CN}\n"
|
||||
string += f" Organization (O): {subject.O}\n"
|
||||
string += f" Organizational Unit (OU): {subject.OU}\n"
|
||||
string += f" Country (C): {subject.C}\n"
|
||||
string += f" State/Province (ST): {subject.ST}\n"
|
||||
string += f" Locality (L): {subject.L}\n"
|
||||
|
||||
string += "\nIssuer:\n"
|
||||
issuer = cert.get_issuer()
|
||||
string += f" Common Name (CN): {issuer.CN}\n"
|
||||
string += f" Organization (O): {issuer.O}\n"
|
||||
string += f" Organizational Unit (OU): {issuer.OU}\n"
|
||||
string += f" Country (C): {issuer.C}\n"
|
||||
string += f" State/Province (ST): {issuer.ST}\n"
|
||||
string += f" Locality (L): {issuer.L}\n"
|
||||
|
||||
string += f"\nVersion: {cert.get_version() + 1}"
|
||||
string += f"Serial Number: {cert.get_serial_number()}"
|
||||
|
||||
not_before = datetime.datetime.strptime(cert.get_notBefore().decode('ascii'), '%Y%m%d%H%M%SZ')
|
||||
not_after = datetime.datetime.strptime(cert.get_notAfter().decode('ascii'), '%Y%m%d%H%M%SZ')
|
||||
string += f"Not Before: {not_before}\n"
|
||||
string += f"Not After: {not_after}\n"
|
||||
|
||||
string += f"Expired: {cert.has_expired()}\n"
|
||||
|
||||
string += "\nExtensions:\n"
|
||||
for i in range(cert.get_extension_count()):
|
||||
ext = cert.get_extension(i)
|
||||
string += f" {ext.get_short_name().decode('utf-8')}: {ext}\n"
|
||||
|
||||
string += "\nSignature Algorithm:\n"
|
||||
string += f" {cert.get_signature_algorithm().decode('utf-8')}\n"
|
||||
|
||||
string += "\nPublic Key:\n"
|
||||
public_key = cert.get_pubkey()
|
||||
string += f" Algorithm: {public_key.type()}\n"
|
||||
string += f" Bits: {public_key.bits()}\n"
|
||||
|
||||
return string
|
||||
|
||||
|
||||
|
||||
context = ssl.create_default_context()
|
||||
# context = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
def scan(address:str, port:str):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
# Wrap the socket with SSL/TLS
|
||||
ssl_sock = context.wrap_socket(sock, server_hostname=address)
|
||||
|
||||
returnVal = ""
|
||||
|
||||
try:
|
||||
# Connect to the server
|
||||
ssl_sock.connect((address, port))
|
||||
|
||||
ssl_sock.sendall(generate_headers().encode())
|
||||
|
||||
cert = ssl_sock.getpeercert(binary_form=True)
|
||||
# Receive the response
|
||||
response = b""
|
||||
while True:
|
||||
|
||||
chunk = ssl_sock.recv(64)
|
||||
if not chunk: break
|
||||
|
||||
response += chunk
|
||||
|
||||
returnVal = f"Response {response.decode()}" + "\n"
|
||||
|
||||
returnVal += "SSL Certificate Information:\n"
|
||||
returnVal += get_ssl_cert_info(cert) + "\n"
|
||||
returnVal += "######### \n"
|
||||
|
||||
except socket.error as e:
|
||||
returnVal = f"<Error> (possible connection reset) {e}"
|
||||
finally:
|
||||
if ssl_sock:
|
||||
ssl_sock.close()
|
||||
return returnVal
|
||||
@@ -0,0 +1,48 @@
|
||||
import random
|
||||
import socket
|
||||
from faker import Faker
|
||||
|
||||
def generate_headers():
|
||||
fake = Faker()
|
||||
return [
|
||||
f"User-Agent: {fake.user_agent()}",
|
||||
f"Accept: {fake.mime_type()}",
|
||||
f"Accept-Language: {fake.language_code()},{fake.language_code()};q=0.9",
|
||||
f"Accept-Encoding: {fake.mime_type()}, {fake.mime_type()}, {fake.mime_type()}",
|
||||
f"Referer: {fake.url()}",
|
||||
f"Connection: {random.choice(['keep-alive', 'close'])}",
|
||||
f"Cache-Control: {random.choice(['no-cache', 'max-age=0'])}",
|
||||
f"Pragma: {random.choice(['no-cache', ''])}",
|
||||
]
|
||||
|
||||
def scan(host:str, port:int):
|
||||
returnVal = ""
|
||||
try:
|
||||
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
client_socket.connect((host, port))
|
||||
headers = generate_headers()
|
||||
request = "GET / HTTP/1.1\r\n"
|
||||
request += "Host: " + host + "\r\n"
|
||||
request += "\r\n".join(headers)
|
||||
request += "\r\n\r\n"
|
||||
|
||||
client_socket.send(request.encode())
|
||||
|
||||
response = b""
|
||||
while True:
|
||||
|
||||
chunk = client_socket.recv(64)
|
||||
|
||||
if not chunk: break
|
||||
|
||||
response += chunk
|
||||
|
||||
returnVal = "Response: " + response.decode()
|
||||
|
||||
except:
|
||||
returnVal = "<Error> (possible connection reset)"
|
||||
finally:
|
||||
if client_socket:
|
||||
client_socket.close()
|
||||
|
||||
return returnVal
|
||||
@@ -0,0 +1,34 @@
|
||||
import socket
|
||||
import time
|
||||
|
||||
def scan(address:str, port:int, timeout:int=5):
|
||||
returnVal = ""
|
||||
|
||||
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
try:
|
||||
client_socket.connect((address, port))
|
||||
client_socket.settimeout(timeout)
|
||||
start_time = time.time()
|
||||
|
||||
outBytes = b''
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
try:
|
||||
|
||||
data = client_socket.recv(64)
|
||||
if not data: break
|
||||
outBytes += data
|
||||
|
||||
except socket.timeout:
|
||||
break
|
||||
|
||||
returnVal = f'({address}:{port}) Recieved: {outBytes.decode()}'
|
||||
|
||||
except socket.error as e:
|
||||
print(f"Error: {e}")
|
||||
returnVal = f'({address}:{port}) Recieved: <error>'
|
||||
finally:
|
||||
client_socket.close()
|
||||
|
||||
return returnVal
|
||||
@@ -0,0 +1,2 @@
|
||||
def scan(address:str, port:int):
|
||||
return f'<{address}:{port}>'
|
||||
+157
-1
@@ -1,4 +1,9 @@
|
||||
import subprocess
|
||||
import requests
|
||||
import src.utils as utils
|
||||
import os
|
||||
import re
|
||||
import geoip2.database
|
||||
|
||||
def countScannedIps():
|
||||
files = utils.listSubdirs("data/")
|
||||
@@ -9,4 +14,155 @@ def countScannedIps():
|
||||
with open('data/'+file) as f:
|
||||
#Count lines in scan files, Masscan has a 2 line header, so hence -2
|
||||
count += sum(1 for _ in f)-2
|
||||
return count
|
||||
return count
|
||||
|
||||
def getMostCommon(protocol: str, n: int):
|
||||
nmap_services_file = "/usr/share/nmap/nmap-services"
|
||||
|
||||
if not os.path.exists(nmap_services_file):
|
||||
print(f"Error: {nmap_services_file} not found.")
|
||||
return []
|
||||
|
||||
ports = []
|
||||
with open(nmap_services_file, "r") as file:
|
||||
for line in file:
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
|
||||
fields = line.split()
|
||||
if len(fields) >= 3:
|
||||
port_info = fields[1]
|
||||
port, proto = port_info.split("/")
|
||||
frequency = float(fields[2])
|
||||
|
||||
if proto.lower() == protocol.lower():
|
||||
ports.append((int(port), frequency))
|
||||
|
||||
most_common_ports = sorted(ports, key=lambda x: x[1], reverse=True)[:n]
|
||||
return [port[0] for port in most_common_ports]
|
||||
|
||||
|
||||
|
||||
|
||||
def portsRelatedTo(protocol: str, search_string: str):
|
||||
nmap_services_file = "/usr/share/nmap/nmap-services"
|
||||
|
||||
if not os.path.exists(nmap_services_file):
|
||||
print(f"Error: {nmap_services_file} not found.")
|
||||
return []
|
||||
|
||||
ports = []
|
||||
with open(nmap_services_file, "r") as file:
|
||||
for line in file:
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
|
||||
fields = line.split("#")
|
||||
port_info = fields[0].strip()
|
||||
description = fields[1].strip() if len(fields) >= 2 else ""
|
||||
|
||||
port_fields = port_info.split()
|
||||
if len(port_fields) >= 2:
|
||||
port, proto = port_fields[1].split("/")
|
||||
service_name = port_fields[0]
|
||||
|
||||
if (
|
||||
proto.lower() == protocol.lower() and
|
||||
(search_string.lower() in description.lower() or search_string.lower() in service_name.lower())
|
||||
):
|
||||
ports.append(int(port))
|
||||
|
||||
return ports
|
||||
|
||||
|
||||
def getPorts(nmapResult: str):
|
||||
lines = nmapResult.split("\n")
|
||||
|
||||
row_pattern = re.compile(r'\d+/(tcp|udp)\s+\S+')
|
||||
|
||||
portInfo = []
|
||||
|
||||
for line in lines:
|
||||
if row_pattern.match(line):
|
||||
columns = line.split(None, 3)
|
||||
if len(columns) == 4:
|
||||
portInfo.append(columns)
|
||||
|
||||
return portInfo
|
||||
|
||||
|
||||
|
||||
def getHostname(nmapResult: str):
|
||||
hostnamePattern = re.compile(r"Nmap scan report for ([\w.-]+)\s+\(([\d.]+)\)")
|
||||
hostnameList = hostnamePattern.findall(nmapResult)
|
||||
return hostnameList[0][0] if len(hostnameList) != 0 else ""
|
||||
|
||||
|
||||
|
||||
# Download geolocation databases
|
||||
utils.makeDir(utils.getRoot("data"))
|
||||
|
||||
CITY_DB_PATH = utils.getRoot("data/GeoLite2-City.mmdb")
|
||||
if not utils.pathExists(CITY_DB_PATH):
|
||||
print("Downloading GeoLite2-City.mmdb database...")
|
||||
open(CITY_DB_PATH, 'wb').write(
|
||||
requests.get("https://github.com/P3TERX/GeoLite.mmdb/releases/download/2024.04.16/GeoLite2-City.mmdb").content
|
||||
)
|
||||
|
||||
COUNTRY_DB_PATH = utils.getRoot("data/GeoLite2-Country.mmdb")
|
||||
if not utils.pathExists(COUNTRY_DB_PATH):
|
||||
print("Downloading GeoLite2-Country.mmdb database...")
|
||||
open(COUNTRY_DB_PATH, 'wb').write(
|
||||
requests.get("https://github.com/P3TERX/GeoLite.mmdb/releases/download/2024.04.16/GeoLite2-Country.mmdb").content
|
||||
)
|
||||
|
||||
ASN_DB_PATH = utils.getRoot("data/GeoLite2-ASN.mmdb")
|
||||
if not utils.pathExists(ASN_DB_PATH):
|
||||
print("Downloading GeoLite2-ASN.mmdb database...")
|
||||
open(ASN_DB_PATH, 'wb').write(
|
||||
requests.get("https://github.com/P3TERX/GeoLite.mmdb/releases/download/2024.04.16/GeoLite2-ASN.mmdb").content
|
||||
)
|
||||
|
||||
|
||||
|
||||
def geolocation(ip_address):
|
||||
try:
|
||||
# Attempt to retrieve city-level information
|
||||
with geoip2.database.Reader(CITY_DB_PATH) as reader:
|
||||
response = reader.city(ip_address)
|
||||
return {
|
||||
'ip': ip_address,
|
||||
'city': response.city.name,
|
||||
'subdivision': response.subdivisions.most_specific.name,
|
||||
'country': response.country.name,
|
||||
'continent': response.continent.name,
|
||||
'latitude': response.location.latitude,
|
||||
'longitude': response.location.longitude,
|
||||
'postal_code': response.postal.code,
|
||||
'time_zone': response.location.time_zone
|
||||
}
|
||||
except geoip2.errors.AddressNotFoundError:
|
||||
try:
|
||||
# Attempt to retrieve country-level information
|
||||
with geoip2.database.Reader(COUNTRY_DB_PATH) as reader:
|
||||
response = reader.country(ip_address)
|
||||
return {
|
||||
'ip': ip_address,
|
||||
'country': response.country.name,
|
||||
'continent': response.continent.name
|
||||
}
|
||||
except geoip2.errors.AddressNotFoundError:
|
||||
try:
|
||||
# Attempt to retrieve ASN information
|
||||
with geoip2.database.Reader(ASN_DB_PATH) as reader:
|
||||
response = reader.asn(ip_address)
|
||||
return {
|
||||
'ip': ip_address,
|
||||
'asn': response.autonomous_system_number,
|
||||
'org': response.autonomous_system_organization
|
||||
}
|
||||
except geoip2.errors.AddressNotFoundError:
|
||||
return {
|
||||
'ip': ip_address,
|
||||
'error': 'No geolocation data found'
|
||||
}
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import subprocess
|
||||
import random
|
||||
import socket
|
||||
import struct
|
||||
from threading import Thread
|
||||
|
||||
maxPingTimeout = 3
|
||||
|
||||
class ScanTask:
|
||||
def __init__(self):
|
||||
self.running = True
|
||||
|
||||
def terminate(self):
|
||||
self.running = False
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
address = socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||
|
||||
pingCommand = f"ping {address} -c 1 -W {maxPingTimeout}"
|
||||
|
||||
try:
|
||||
subprocess.check_output(pingCommand.split(" "))
|
||||
# print(f"{address}: FOUND")
|
||||
except subprocess.CalledProcessError:
|
||||
# print(f"{address}: FAIL")
|
||||
continue
|
||||
|
||||
nmapCommand = f"sudo nmap {address} -O --send-eth --privileged -v -sS"
|
||||
|
||||
try:
|
||||
print(subprocess.check_output(nmapCommand.split(" ")).decode())
|
||||
except subprocess.CalledProcessError:
|
||||
continue
|
||||
|
||||
threads = []
|
||||
|
||||
for i in range(0,500):
|
||||
c = ScanTask()
|
||||
t = Thread(target = c.run)
|
||||
t.start()
|
||||
# threads.push(c)
|
||||
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
Reference in New Issue
Block a user