mirror of
https://github.com/aljazceru/recon-pipeline.git
synced 2025-12-20 07:44:26 +01:00
added outdir option; install/scan colors match
This commit is contained in:
6
Pipfile.lock
generated
6
Pipfile.lock
generated
@@ -81,10 +81,10 @@
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||
],
|
||||
"version": "==1.13.0"
|
||||
"version": "==1.14.0"
|
||||
},
|
||||
"tornado": {
|
||||
"hashes": [
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# Automated Reconnaissance Pipeline
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
There are an [accompanying set of blog posts](https://epi052.gitlab.io/notes-to-self/blog/2019-09-01-how-to-build-an-automated-recon-pipeline-with-python-and-luigi/) detailing the development process and underpinnings of the pipeline. Feel free to check them out if you're so inclined, but they're in no way required reading to use the tool.
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ class ReconShell(cmd2.Cmd):
|
||||
if self.sentry:
|
||||
|
||||
# only set once the Luigi Execution Summary is seen
|
||||
self.async_alert(cmd2.utils.align_center(style(output.strip(), fg="bright_blue")))
|
||||
self.async_alert(style(output.strip(), fg="bright_blue"))
|
||||
elif output.startswith("INFO: Informed") and output.strip().endswith("PENDING"):
|
||||
# luigi Task has been queued for execution
|
||||
|
||||
@@ -140,7 +140,7 @@ class ReconShell(cmd2.Cmd):
|
||||
Possible scans include
|
||||
AmassScan CORScannerScan GobusterScan SearchsploitScan
|
||||
ThreadedNmapScan WebanalyzeScan AquatoneScan FullScan
|
||||
MasscanScan SubjackScan TKOSubsScan
|
||||
MasscanScan SubjackScan TKOSubsScan HTBScan
|
||||
"""
|
||||
self.async_alert(
|
||||
style(
|
||||
@@ -158,7 +158,7 @@ class ReconShell(cmd2.Cmd):
|
||||
# luigi --module recon.web.webanalyze WebanalyzeScan --target-file tesla --top-ports 1000 --interface eth0
|
||||
command = ["luigi", "--module", scans.get(args.scantype)[0]]
|
||||
command.extend(args.__statement__.arg_list)
|
||||
|
||||
self.async_alert(" ".join(command))
|
||||
if args.verbose:
|
||||
# verbose is not a luigi option, need to remove it
|
||||
command.pop(command.index("--verbose"))
|
||||
@@ -227,13 +227,13 @@ class ReconShell(cmd2.Cmd):
|
||||
# used to determine whether the tool installed correctly or not
|
||||
retvals = list()
|
||||
|
||||
self.async_alert(style(f"[*] Installing {args.tool}...", fg="blue", bold=True))
|
||||
self.async_alert(style(f"[*] Installing {args.tool}...", fg="bright_yellow"))
|
||||
|
||||
for command in tools.get(args.tool).get("commands"):
|
||||
# run all commands required to install the tool
|
||||
|
||||
# print each command being run
|
||||
self.async_alert(style(f"[-] {command}", fg="cyan"))
|
||||
self.async_alert(style(f"[=] {command}", fg="cyan"))
|
||||
|
||||
if tools.get(args.tool).get("shell"):
|
||||
|
||||
@@ -260,7 +260,7 @@ class ReconShell(cmd2.Cmd):
|
||||
if all(x == 0 for x in retvals):
|
||||
# all return values in retvals are 0, i.e. all exec'd successfully; tool has been installed
|
||||
|
||||
self.async_alert(style(f"[+] {args.tool} installed!", fg="bright_green", bold=True))
|
||||
self.async_alert(style(f"[+] {args.tool} installed!", fg="bright_green"))
|
||||
|
||||
tools[args.tool]["installed"] = True
|
||||
else:
|
||||
|
||||
@@ -159,6 +159,11 @@ scan_parser.add_argument(
|
||||
scan_parser.add_argument(
|
||||
"--exempt-list", completer_method=cmd2.Cmd.path_complete, help="list of blacklisted ips/domains"
|
||||
)
|
||||
scan_parser.add_argument(
|
||||
"--results-dir",
|
||||
completer_method=cmd2.Cmd.path_complete,
|
||||
help="directory in which to save scan results",
|
||||
)
|
||||
scan_parser.add_argument(
|
||||
"--wordlist", completer_method=cmd2.Cmd.path_complete, help="path to wordlist used by gobuster"
|
||||
)
|
||||
|
||||
@@ -21,6 +21,7 @@ class AmassScan(ExternalProgramTask):
|
||||
Args:
|
||||
exempt_list: Path to a file providing blacklisted subdomains, one per line.
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
exempt_list = luigi.Parameter(default="")
|
||||
@@ -33,7 +34,8 @@ class AmassScan(ExternalProgramTask):
|
||||
Returns:
|
||||
luigi.ExternalTask - TargetList
|
||||
"""
|
||||
return TargetList(self.target_file)
|
||||
args = {"target_file": self.target_file, "results_dir": self.results_dir}
|
||||
return TargetList(**args)
|
||||
|
||||
def output(self):
|
||||
""" Returns the target output for this task.
|
||||
@@ -43,7 +45,7 @@ class AmassScan(ExternalProgramTask):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"amass.{self.target_file}.json")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/amass.{self.target_file}.json")
|
||||
|
||||
def program_args(self):
|
||||
""" Defines the options/arguments sent to amass after processing.
|
||||
@@ -51,6 +53,7 @@ class AmassScan(ExternalProgramTask):
|
||||
Returns:
|
||||
list: list of options/arguments, beginning with the name of the executable to run
|
||||
"""
|
||||
print(f"debug-epi: amass {self.results_dir}")
|
||||
if not self.input().path.endswith("domains"):
|
||||
return f"touch {self.output().path}".split()
|
||||
|
||||
@@ -65,7 +68,7 @@ class AmassScan(ExternalProgramTask):
|
||||
"-df",
|
||||
self.input().path,
|
||||
"-json",
|
||||
f"amass.{self.target_file}.json",
|
||||
self.output().path,
|
||||
]
|
||||
|
||||
if self.exempt_list:
|
||||
@@ -82,6 +85,7 @@ class ParseAmassOutput(luigi.Task):
|
||||
Args:
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
exempt_list: Path to a file providing blacklisted subdomains, one per line. *--* Optional for upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
def requires(self):
|
||||
@@ -94,7 +98,11 @@ class ParseAmassOutput(luigi.Task):
|
||||
luigi.ExternalTask - TargetList
|
||||
"""
|
||||
|
||||
args = {"target_file": self.target_file, "exempt_list": self.exempt_list}
|
||||
args = {
|
||||
"target_file": self.target_file,
|
||||
"exempt_list": self.exempt_list,
|
||||
"results_dir": self.results_dir,
|
||||
}
|
||||
return AmassScan(**args)
|
||||
|
||||
def output(self):
|
||||
@@ -109,9 +117,11 @@ class ParseAmassOutput(luigi.Task):
|
||||
dict(str: luigi.local_target.LocalTarget)
|
||||
"""
|
||||
return {
|
||||
"target-ips": luigi.LocalTarget(f"{self.target_file}.ips"),
|
||||
"target-ip6s": luigi.LocalTarget(f"{self.target_file}.ip6s"),
|
||||
"target-subdomains": luigi.LocalTarget(f"{self.target_file}.subdomains"),
|
||||
"target-ips": luigi.LocalTarget(f"{self.results_dir}/{self.target_file}.ips"),
|
||||
"target-ip6s": luigi.LocalTarget(f"{self.results_dir}/{self.target_file}.ip6s"),
|
||||
"target-subdomains": luigi.LocalTarget(
|
||||
f"{self.results_dir}/{self.target_file}.subdomains"
|
||||
),
|
||||
}
|
||||
|
||||
def run(self):
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -32,6 +32,7 @@ class MasscanScan(luigi.Task):
|
||||
ports: specifies the port(s) to be scanned
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
exempt_list: Path to a file providing blacklisted subdomains, one per line. *--* Optional for upstream Task
|
||||
results_dir: specifies the directory on disk to which all Task results are written *--* Optional for upstream Task
|
||||
"""
|
||||
|
||||
rate = luigi.Parameter(default=defaults.get("masscan-rate", ""))
|
||||
@@ -39,10 +40,6 @@ class MasscanScan(luigi.Task):
|
||||
top_ports = luigi.IntParameter(default=0) # IntParameter -> top_ports expected as int
|
||||
ports = luigi.Parameter(default="")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MasscanScan, self).__init__(*args, **kwargs)
|
||||
self.masscan_output = f"masscan.{self.target_file}.json"
|
||||
|
||||
def output(self):
|
||||
""" Returns the target output for this task.
|
||||
|
||||
@@ -51,7 +48,7 @@ class MasscanScan(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(self.masscan_output)
|
||||
return luigi.LocalTarget(f"{self.results_dir}/masscan.{self.target_file}.json")
|
||||
|
||||
def run(self):
|
||||
""" Defines the options/arguments sent to masscan after processing.
|
||||
@@ -59,6 +56,7 @@ class MasscanScan(luigi.Task):
|
||||
Returns:
|
||||
list: list of options/arguments, beginning with the name of the executable to run
|
||||
"""
|
||||
print(f"debug-epi: masscan {self.results_dir}")
|
||||
if self.ports and self.top_ports:
|
||||
# can't have both
|
||||
logging.error("Only --ports or --top-ports is permitted, not both.")
|
||||
@@ -82,10 +80,14 @@ class MasscanScan(luigi.Task):
|
||||
self.ports = f"{top_tcp_ports_str},U:{top_udp_ports_str}"
|
||||
self.top_ports = 0
|
||||
|
||||
target_list = yield TargetList(target_file=self.target_file)
|
||||
target_list = yield TargetList(target_file=self.target_file, results_dir=self.results_dir)
|
||||
|
||||
if target_list.path.endswith("domains"):
|
||||
yield ParseAmassOutput(target_file=self.target_file, exempt_list=self.exempt_list)
|
||||
yield ParseAmassOutput(
|
||||
target_file=self.target_file,
|
||||
exempt_list=self.exempt_list,
|
||||
results_dir=self.results_dir,
|
||||
)
|
||||
|
||||
command = [
|
||||
"masscan",
|
||||
@@ -97,7 +99,7 @@ class MasscanScan(luigi.Task):
|
||||
"-e",
|
||||
self.interface,
|
||||
"-oJ",
|
||||
self.masscan_output,
|
||||
self.output().path,
|
||||
"--ports",
|
||||
self.ports,
|
||||
"-iL",
|
||||
@@ -117,6 +119,7 @@ class ParseMasscanOutput(luigi.Task):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
def requires(self):
|
||||
@@ -128,6 +131,7 @@ class ParseMasscanOutput(luigi.Task):
|
||||
luigi.Task - Masscan
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -144,7 +148,7 @@ class ParseMasscanOutput(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"masscan.{self.target_file}.parsed.pickle")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/masscan.{self.target_file}.parsed.pickle")
|
||||
|
||||
def run(self):
|
||||
""" Reads masscan JSON results and creates a pickled dictionary of pertinent information for processing. """
|
||||
|
||||
@@ -30,6 +30,7 @@ class ThreadedNmapScan(luigi.Task):
|
||||
top_ports: Scan top N most popular ports *--* Required by upstream Task
|
||||
ports: specifies the port(s) to be scanned *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||
@@ -44,6 +45,7 @@ class ThreadedNmapScan(luigi.Task):
|
||||
luigi.Task - ParseMasscanOutput
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -64,7 +66,7 @@ class ThreadedNmapScan(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"nmap-{self.target_file}-results")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/nmap-{self.target_file}-results")
|
||||
|
||||
def run(self):
|
||||
""" Parses pickled target info dictionary and runs targeted nmap scans against only open ports. """
|
||||
@@ -78,7 +80,8 @@ class ThreadedNmapScan(luigi.Task):
|
||||
nmap_command = [ # placeholders will be overwritten with appropriate info in loop below
|
||||
"nmap",
|
||||
"--open",
|
||||
"PLACEHOLDER-IDX-2" "-n",
|
||||
"PLACEHOLDER-IDX-2",
|
||||
"-n",
|
||||
"-sC",
|
||||
"-T",
|
||||
"4",
|
||||
@@ -140,6 +143,7 @@ class SearchsploitScan(luigi.Task):
|
||||
top_ports: Scan top N most popular ports *--* Required by upstream Task
|
||||
ports: specifies the port(s) to be scanned *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifies the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
def requires(self):
|
||||
@@ -159,6 +163,7 @@ class SearchsploitScan(luigi.Task):
|
||||
"top_ports": self.top_ports,
|
||||
"interface": self.interface,
|
||||
"target_file": self.target_file,
|
||||
"results_dir": self.results_dir,
|
||||
}
|
||||
return ThreadedNmapScan(**args)
|
||||
|
||||
@@ -173,7 +178,7 @@ class SearchsploitScan(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"searchsploit-{self.target_file}-results")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/searchsploit-{self.target_file}-results")
|
||||
|
||||
def run(self):
|
||||
""" Grabs the xml files created by ThreadedNmap and runs searchsploit --nmap on each one, saving the output. """
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
import shutil
|
||||
import logging
|
||||
import ipaddress
|
||||
from pathlib import Path
|
||||
|
||||
import luigi
|
||||
|
||||
from recon.config import defaults
|
||||
|
||||
|
||||
class TargetList(luigi.ExternalTask):
|
||||
""" External task. `TARGET_FILE` is generated manually by the user from target's scope. """
|
||||
""" External task. `TARGET_FILE` is generated manually by the user from target's scope.
|
||||
|
||||
Args:
|
||||
results_dir: specifies the directory on disk to which all Task results are written
|
||||
"""
|
||||
|
||||
target_file = luigi.Parameter()
|
||||
results_dir = luigi.Parameter(default=defaults.get("results-dir", ""))
|
||||
|
||||
def output(self):
|
||||
""" Returns the target output for this task. target_file.ips || target_file.domains
|
||||
@@ -24,8 +32,9 @@ class TargetList(luigi.ExternalTask):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
print(f"debug-epi: targets {self.results_dir}")
|
||||
try:
|
||||
with open(self.target_file) as f:
|
||||
with open(str(self.target_file)) as f:
|
||||
first_line = f.readline()
|
||||
ipaddress.ip_interface(first_line.strip()) # is it a valid ip/network?
|
||||
except OSError as e:
|
||||
@@ -39,5 +48,10 @@ class TargetList(luigi.ExternalTask):
|
||||
# no exception thrown; ip address found
|
||||
with_suffix = f"{self.target_file}.ips"
|
||||
|
||||
shutil.copy(self.target_file, with_suffix) # copy file with new extension
|
||||
Path(str(self.results_dir)).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
with_suffix = f"{self.results_dir}/{with_suffix}"
|
||||
|
||||
# copy file with new extension
|
||||
shutil.copy(str(self.target_file), with_suffix)
|
||||
return luigi.LocalTarget(with_suffix)
|
||||
|
||||
@@ -29,6 +29,7 @@ class AquatoneScan(luigi.Task):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||
@@ -44,6 +45,7 @@ class AquatoneScan(luigi.Task):
|
||||
luigi.Task - GatherWebTargets
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -61,7 +63,7 @@ class AquatoneScan(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"aquatone-{self.target_file}-results")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/aquatone-{self.target_file}-results")
|
||||
|
||||
def run(self):
|
||||
""" Defines the options/arguments sent to aquatone after processing.
|
||||
|
||||
@@ -32,6 +32,7 @@ class CORScannerScan(ExternalProgramTask):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||
@@ -46,6 +47,7 @@ class CORScannerScan(ExternalProgramTask):
|
||||
luigi.Task - GatherWebTargets
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -63,7 +65,7 @@ class CORScannerScan(ExternalProgramTask):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"corscanner.{self.target_file}.json")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/corscanner.{self.target_file}.json")
|
||||
|
||||
def program_args(self):
|
||||
""" Defines the options/arguments sent to tko-subs after processing.
|
||||
|
||||
@@ -41,6 +41,7 @@ class GobusterScan(luigi.Task):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
proxy = luigi.Parameter(default=defaults.get("proxy", ""))
|
||||
@@ -59,6 +60,7 @@ class GobusterScan(luigi.Task):
|
||||
luigi.Task - GatherWebTargets
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -79,7 +81,7 @@ class GobusterScan(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"gobuster-{self.target_file}-results")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/gobuster-{self.target_file}-results")
|
||||
|
||||
def run(self):
|
||||
""" Defines the options/arguments sent to gobuster after processing.
|
||||
|
||||
@@ -25,6 +25,7 @@ class TKOSubsScan(ExternalProgramTask):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
def requires(self):
|
||||
@@ -37,6 +38,7 @@ class TKOSubsScan(ExternalProgramTask):
|
||||
luigi.Task - GatherWebTargets
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -54,7 +56,7 @@ class TKOSubsScan(ExternalProgramTask):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"tkosubs.{self.target_file}.csv")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/tkosubs.{self.target_file}.csv")
|
||||
|
||||
def program_args(self):
|
||||
""" Defines the options/arguments sent to tko-subs after processing.
|
||||
@@ -93,6 +95,7 @@ class SubjackScan(ExternalProgramTask):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||
@@ -107,6 +110,7 @@ class SubjackScan(ExternalProgramTask):
|
||||
luigi.Task - GatherWebTargets
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -124,7 +128,7 @@ class SubjackScan(ExternalProgramTask):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"subjack.{self.target_file}.txt")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/subjack.{self.target_file}.txt")
|
||||
|
||||
def program_args(self):
|
||||
""" Defines the options/arguments sent to subjack after processing.
|
||||
|
||||
@@ -3,9 +3,9 @@ import pickle
|
||||
import luigi
|
||||
from luigi.util import inherits
|
||||
|
||||
from recon.config import web_ports
|
||||
from recon.amass import ParseAmassOutput
|
||||
from recon.masscan import ParseMasscanOutput
|
||||
from recon.config import web_ports
|
||||
|
||||
|
||||
@inherits(ParseMasscanOutput, ParseAmassOutput)
|
||||
@@ -19,6 +19,7 @@ class GatherWebTargets(luigi.Task):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
def requires(self):
|
||||
@@ -31,6 +32,7 @@ class GatherWebTargets(luigi.Task):
|
||||
dict(str: ParseMasscanOutput, str: ParseAmassOutput)
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -40,7 +42,9 @@ class GatherWebTargets(luigi.Task):
|
||||
return {
|
||||
"masscan-output": ParseMasscanOutput(**args),
|
||||
"amass-output": ParseAmassOutput(
|
||||
exempt_list=self.exempt_list, target_file=self.target_file
|
||||
exempt_list=self.exempt_list,
|
||||
target_file=self.target_file,
|
||||
results_dir=self.results_dir,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -52,7 +56,7 @@ class GatherWebTargets(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"webtargets.{self.target_file}.txt")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/webtargets.{self.target_file}.txt")
|
||||
|
||||
def run(self):
|
||||
""" Gather all potential web targets into a single file to pass farther down the pipeline. """
|
||||
|
||||
@@ -39,6 +39,7 @@ class WebanalyzeScan(luigi.Task):
|
||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream Task
|
||||
rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task
|
||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
||||
results_dir: specifes the directory on disk to which all Task results are written *--* Required by upstream Task
|
||||
"""
|
||||
|
||||
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||
@@ -53,6 +54,7 @@ class WebanalyzeScan(luigi.Task):
|
||||
luigi.Task - GatherWebTargets
|
||||
"""
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -72,7 +74,7 @@ class WebanalyzeScan(luigi.Task):
|
||||
Returns:
|
||||
luigi.local_target.LocalTarget
|
||||
"""
|
||||
return luigi.LocalTarget(f"webanalyze-{self.target_file}-results")
|
||||
return luigi.LocalTarget(f"{self.results_dir}/webanalyze-{self.target_file}-results")
|
||||
|
||||
def _wrapped_subprocess(self, cmd):
|
||||
with open(f"webanalyze.{cmd[2].replace('//', '_').replace(':', '')}.txt", "wb") as f:
|
||||
|
||||
@@ -24,6 +24,7 @@ class FullScan(luigi.WrapperTask):
|
||||
def requires(self):
|
||||
""" FullScan is a wrapper, as such it requires any Tasks that it wraps. """
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
@@ -39,9 +40,11 @@ class FullScan(luigi.WrapperTask):
|
||||
|
||||
yield GobusterScan(**args)
|
||||
|
||||
# remove options that are gobuster specific; if left dictionary unpacking to other scans throws an exception
|
||||
for gobuster_opt in ("proxy", "wordlist", "extensions", "recursive"):
|
||||
del args[gobuster_opt]
|
||||
|
||||
# add aquatone scan specific option
|
||||
args.update({"scan_timeout": self.scan_timeout})
|
||||
|
||||
yield AquatoneScan(**args)
|
||||
@@ -56,3 +59,41 @@ class FullScan(luigi.WrapperTask):
|
||||
del args["threads"]
|
||||
|
||||
yield TKOSubsScan(**args)
|
||||
|
||||
|
||||
@inherits(SearchsploitScan, AquatoneScan, GobusterScan, WebanalyzeScan)
|
||||
class HTBScan(luigi.WrapperTask):
|
||||
""" Wraps multiple scan types in order to run tasks on the same hierarchical level at the same time. """
|
||||
|
||||
def requires(self):
|
||||
""" HTBScan is a wrapper, as such it requires any Tasks that it wraps. """
|
||||
args = {
|
||||
"results_dir": self.results_dir,
|
||||
"rate": self.rate,
|
||||
"target_file": self.target_file,
|
||||
"top_ports": self.top_ports,
|
||||
"interface": self.interface,
|
||||
"ports": self.ports,
|
||||
"exempt_list": self.exempt_list,
|
||||
"threads": self.threads,
|
||||
"proxy": self.proxy,
|
||||
"wordlist": self.wordlist,
|
||||
"extensions": self.extensions,
|
||||
"recursive": self.recursive,
|
||||
}
|
||||
|
||||
yield GobusterScan(**args)
|
||||
|
||||
# remove options that are gobuster specific; if left dictionary unpacking to other scans throws an exception
|
||||
for gobuster_opt in ("proxy", "wordlist", "extensions", "recursive"):
|
||||
del args[gobuster_opt]
|
||||
|
||||
# add aquatone scan specific option
|
||||
args.update({"scan_timeout": self.scan_timeout})
|
||||
|
||||
yield AquatoneScan(**args)
|
||||
|
||||
del args["scan_timeout"]
|
||||
|
||||
yield SearchsploitScan(**args)
|
||||
yield WebanalyzeScan(**args)
|
||||
|
||||
Reference in New Issue
Block a user