Nettacker/nettacker/core/module.py

201 lines
8.1 KiB
Python

import copy
import importlib
import json
import os
import time
from threading import Thread
from nettacker import logger
from nettacker.config import Config
from nettacker.core.messages import messages as _
from nettacker.core.template import TemplateLoader
from nettacker.core.utils.common import expand_module_steps, wait_for_threads_to_finish
from nettacker.database.db import find_events
log = logger.get_logger()
class Module:
def __init__(
self,
module_name,
options,
target,
scan_id,
process_number,
thread_number,
total_number_threads,
):
self.module_name = module_name
self.process_number = process_number
self.module_thread_number = thread_number
self.total_module_thread_number = total_number_threads
self.module_inputs = options.__dict__
self.module_inputs["target"] = target
if options.modules_extra_args:
for module_extra_args in self.module_inputs["modules_extra_args"]:
self.module_inputs[module_extra_args] = self.module_inputs["modules_extra_args"][
module_extra_args
]
self.target = target
self.scan_id = scan_id
self.skip_service_discovery = options.skip_service_discovery
self.discovered_services = None
self.ignored_core_modules = [
"subdomain_scan",
"icmp_scan",
"port_scan",
"ssl_weak_version_vuln",
"ssl_weak_cipher_vuln",
"ssl_certificate_weak_signature_vuln",
"ssl_self_signed_certificate_vuln",
"ssl_expired_certificate_vuln",
"ssl_expiring_certificate_scan",
]
contents = TemplateLoader("port_scan", {"target": ""}).load()
self.service_discovery_signatures = list(
set(
contents["payloads"][0]["steps"][0]["response"]["conditions"]
.get("service", set(contents["payloads"][0]["steps"][0]["response"]["conditions"]))
.keys()
)
)
self.libraries = [
module_protocol.split(".py")[0]
for module_protocol in os.listdir(Config.path.module_protocols_dir)
if module_protocol.endswith(".py")
and module_protocol not in {"__init__.py", "base.py"}
]
def load(self):
self.module_content = TemplateLoader(self.module_name, self.module_inputs).load()
if not self.skip_service_discovery and self.module_name not in self.ignored_core_modules:
services = {}
for service in find_events(self.target, "port_scan", self.scan_id):
service_event = json.loads(service.json_event)
port = service_event["port"]
protocols = service_event["response"]["conditions_results"].keys()
for protocol in protocols:
if protocol and protocol in self.libraries:
if protocol in services:
services[protocol].append(port)
else:
services[protocol] = [port]
self.discovered_services = copy.deepcopy(services)
index_payload = 0
for payload in copy.deepcopy(self.module_content["payloads"]):
if (
payload["library"] not in self.discovered_services
and payload["library"] in self.service_discovery_signatures
):
del self.module_content["payloads"][index_payload]
index_payload -= 1
else:
index_step = 0
for step in copy.deepcopy(
self.module_content["payloads"][index_payload]["steps"]
):
step = TemplateLoader.parse(
step, {"port": self.discovered_services[payload["library"]]}
)
self.module_content["payloads"][index_payload]["steps"][index_step] = step
index_step += 1
index_payload += 1
def generate_loops(self):
if self.module_inputs["excluded_ports"]:
excluded_port_set = set(self.module_inputs["excluded_ports"])
if self.module_content and "ports" in self.module_content["payloads"][0]["steps"][0]:
all_ports = self.module_content["payloads"][0]["steps"][0]["ports"]
all_ports[:] = [port for port in all_ports if port not in excluded_port_set]
self.module_content["payloads"] = expand_module_steps(self.module_content["payloads"])
def sort_loops(self):
steps = []
for index in range(len(self.module_content["payloads"])):
for step in copy.deepcopy(self.module_content["payloads"][index]["steps"]):
if "dependent_on_temp_event" not in step[0]["response"]:
steps.append(step)
for step in copy.deepcopy(self.module_content["payloads"][index]["steps"]):
if (
"dependent_on_temp_event" in step[0]["response"]
and "save_to_temp_events_only" in step[0]["response"]
):
steps.append(step)
for step in copy.deepcopy(self.module_content["payloads"][index]["steps"]):
if (
"dependent_on_temp_event" in step[0]["response"]
and "save_to_temp_events_only" not in step[0]["response"]
):
steps.append(step)
self.module_content["payloads"][index]["steps"] = steps
def start(self):
active_threads = []
# counting total number of requests
total_number_of_requests = 0
for payload in self.module_content["payloads"]:
if payload["library"] not in self.libraries:
log.warn(_("library_not_supported").format(payload["library"]))
return None
for step in payload["steps"]:
total_number_of_requests += len(step)
request_number_counter = 0
for payload in self.module_content["payloads"]:
library = payload["library"]
engine = getattr(
importlib.import_module(f"nettacker.core.lib.{library.lower()}"),
f"{library.capitalize()}Engine",
)()
for step in payload["steps"]:
for sub_step in step:
thread = Thread(
target=engine.run,
args=(
sub_step,
self.module_name,
self.target,
self.scan_id,
self.module_inputs,
self.process_number,
self.module_thread_number,
self.total_module_thread_number,
request_number_counter,
total_number_of_requests,
),
)
thread.name = f"{self.target} -> {self.module_name} -> {sub_step}"
request_number_counter += 1
log.verbose_event_info(
_("sending_module_request").format(
self.process_number,
self.module_name,
self.target,
self.module_thread_number,
self.total_module_thread_number,
request_number_counter,
total_number_of_requests,
)
)
thread.start()
time.sleep(self.module_inputs["time_sleep_between_requests"])
active_threads.append(thread)
wait_for_threads_to_finish(
active_threads,
maximum=self.module_inputs["thread_per_host"],
terminable=True,
)
wait_for_threads_to_finish(active_threads, maximum=None, terminable=True)