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": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd",
|
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||||
"sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66"
|
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
|
||||||
],
|
],
|
||||||
"version": "==1.13.0"
|
"version": "==1.14.0"
|
||||||
},
|
},
|
||||||
"tornado": {
|
"tornado": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
# Automated Reconnaissance Pipeline
|
# 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.
|
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:
|
if self.sentry:
|
||||||
|
|
||||||
# only set once the Luigi Execution Summary is seen
|
# 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"):
|
elif output.startswith("INFO: Informed") and output.strip().endswith("PENDING"):
|
||||||
# luigi Task has been queued for execution
|
# luigi Task has been queued for execution
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ class ReconShell(cmd2.Cmd):
|
|||||||
Possible scans include
|
Possible scans include
|
||||||
AmassScan CORScannerScan GobusterScan SearchsploitScan
|
AmassScan CORScannerScan GobusterScan SearchsploitScan
|
||||||
ThreadedNmapScan WebanalyzeScan AquatoneScan FullScan
|
ThreadedNmapScan WebanalyzeScan AquatoneScan FullScan
|
||||||
MasscanScan SubjackScan TKOSubsScan
|
MasscanScan SubjackScan TKOSubsScan HTBScan
|
||||||
"""
|
"""
|
||||||
self.async_alert(
|
self.async_alert(
|
||||||
style(
|
style(
|
||||||
@@ -158,7 +158,7 @@ class ReconShell(cmd2.Cmd):
|
|||||||
# luigi --module recon.web.webanalyze WebanalyzeScan --target-file tesla --top-ports 1000 --interface eth0
|
# luigi --module recon.web.webanalyze WebanalyzeScan --target-file tesla --top-ports 1000 --interface eth0
|
||||||
command = ["luigi", "--module", scans.get(args.scantype)[0]]
|
command = ["luigi", "--module", scans.get(args.scantype)[0]]
|
||||||
command.extend(args.__statement__.arg_list)
|
command.extend(args.__statement__.arg_list)
|
||||||
|
self.async_alert(" ".join(command))
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
# verbose is not a luigi option, need to remove it
|
# verbose is not a luigi option, need to remove it
|
||||||
command.pop(command.index("--verbose"))
|
command.pop(command.index("--verbose"))
|
||||||
@@ -227,13 +227,13 @@ class ReconShell(cmd2.Cmd):
|
|||||||
# used to determine whether the tool installed correctly or not
|
# used to determine whether the tool installed correctly or not
|
||||||
retvals = list()
|
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"):
|
for command in tools.get(args.tool).get("commands"):
|
||||||
# run all commands required to install the tool
|
# run all commands required to install the tool
|
||||||
|
|
||||||
# print each command being run
|
# 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"):
|
if tools.get(args.tool).get("shell"):
|
||||||
|
|
||||||
@@ -260,7 +260,7 @@ class ReconShell(cmd2.Cmd):
|
|||||||
if all(x == 0 for x in retvals):
|
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
|
# 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
|
tools[args.tool]["installed"] = True
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ scan_parser.add_argument(
|
|||||||
scan_parser.add_argument(
|
scan_parser.add_argument(
|
||||||
"--exempt-list", completer_method=cmd2.Cmd.path_complete, help="list of blacklisted ips/domains"
|
"--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(
|
scan_parser.add_argument(
|
||||||
"--wordlist", completer_method=cmd2.Cmd.path_complete, help="path to wordlist used by gobuster"
|
"--wordlist", completer_method=cmd2.Cmd.path_complete, help="path to wordlist used by gobuster"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class AmassScan(ExternalProgramTask):
|
|||||||
Args:
|
Args:
|
||||||
exempt_list: Path to a file providing blacklisted subdomains, one per line.
|
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
|
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="")
|
exempt_list = luigi.Parameter(default="")
|
||||||
@@ -33,7 +34,8 @@ class AmassScan(ExternalProgramTask):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.ExternalTask - TargetList
|
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):
|
def output(self):
|
||||||
""" Returns the target output for this task.
|
""" Returns the target output for this task.
|
||||||
@@ -43,7 +45,7 @@ class AmassScan(ExternalProgramTask):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def program_args(self):
|
||||||
""" Defines the options/arguments sent to amass after processing.
|
""" Defines the options/arguments sent to amass after processing.
|
||||||
@@ -51,6 +53,7 @@ class AmassScan(ExternalProgramTask):
|
|||||||
Returns:
|
Returns:
|
||||||
list: list of options/arguments, beginning with the name of the executable to run
|
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"):
|
if not self.input().path.endswith("domains"):
|
||||||
return f"touch {self.output().path}".split()
|
return f"touch {self.output().path}".split()
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@ class AmassScan(ExternalProgramTask):
|
|||||||
"-df",
|
"-df",
|
||||||
self.input().path,
|
self.input().path,
|
||||||
"-json",
|
"-json",
|
||||||
f"amass.{self.target_file}.json",
|
self.output().path,
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.exempt_list:
|
if self.exempt_list:
|
||||||
@@ -82,6 +85,7 @@ class ParseAmassOutput(luigi.Task):
|
|||||||
Args:
|
Args:
|
||||||
target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task
|
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
|
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):
|
def requires(self):
|
||||||
@@ -94,7 +98,11 @@ class ParseAmassOutput(luigi.Task):
|
|||||||
luigi.ExternalTask - TargetList
|
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)
|
return AmassScan(**args)
|
||||||
|
|
||||||
def output(self):
|
def output(self):
|
||||||
@@ -109,9 +117,11 @@ class ParseAmassOutput(luigi.Task):
|
|||||||
dict(str: luigi.local_target.LocalTarget)
|
dict(str: luigi.local_target.LocalTarget)
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
"target-ips": luigi.LocalTarget(f"{self.target_file}.ips"),
|
"target-ips": luigi.LocalTarget(f"{self.results_dir}/{self.target_file}.ips"),
|
||||||
"target-ip6s": luigi.LocalTarget(f"{self.target_file}.ip6s"),
|
"target-ip6s": luigi.LocalTarget(f"{self.results_dir}/{self.target_file}.ip6s"),
|
||||||
"target-subdomains": luigi.LocalTarget(f"{self.target_file}.subdomains"),
|
"target-subdomains": luigi.LocalTarget(
|
||||||
|
f"{self.results_dir}/{self.target_file}.subdomains"
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
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
|
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
|
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
|
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", ""))
|
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
|
top_ports = luigi.IntParameter(default=0) # IntParameter -> top_ports expected as int
|
||||||
ports = luigi.Parameter(default="")
|
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):
|
def output(self):
|
||||||
""" Returns the target output for this task.
|
""" Returns the target output for this task.
|
||||||
|
|
||||||
@@ -51,7 +48,7 @@ class MasscanScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Defines the options/arguments sent to masscan after processing.
|
""" Defines the options/arguments sent to masscan after processing.
|
||||||
@@ -59,6 +56,7 @@ class MasscanScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
list: list of options/arguments, beginning with the name of the executable to run
|
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:
|
if self.ports and self.top_ports:
|
||||||
# can't have both
|
# can't have both
|
||||||
logging.error("Only --ports or --top-ports is permitted, not 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.ports = f"{top_tcp_ports_str},U:{top_udp_ports_str}"
|
||||||
self.top_ports = 0
|
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"):
|
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 = [
|
command = [
|
||||||
"masscan",
|
"masscan",
|
||||||
@@ -97,7 +99,7 @@ class MasscanScan(luigi.Task):
|
|||||||
"-e",
|
"-e",
|
||||||
self.interface,
|
self.interface,
|
||||||
"-oJ",
|
"-oJ",
|
||||||
self.masscan_output,
|
self.output().path,
|
||||||
"--ports",
|
"--ports",
|
||||||
self.ports,
|
self.ports,
|
||||||
"-iL",
|
"-iL",
|
||||||
@@ -117,6 +119,7 @@ class ParseMasscanOutput(luigi.Task):
|
|||||||
interface: use the named raw network interface, such as "eth0" *--* Required by upstream 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
|
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
|
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):
|
def requires(self):
|
||||||
@@ -128,6 +131,7 @@ class ParseMasscanOutput(luigi.Task):
|
|||||||
luigi.Task - Masscan
|
luigi.Task - Masscan
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -144,7 +148,7 @@ class ParseMasscanOutput(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Reads masscan JSON results and creates a pickled dictionary of pertinent information for processing. """
|
""" 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
|
top_ports: Scan top N most popular ports *--* Required by upstream Task
|
||||||
ports: specifies the port(s) to be scanned *--* 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
|
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", ""))
|
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||||
@@ -44,6 +45,7 @@ class ThreadedNmapScan(luigi.Task):
|
|||||||
luigi.Task - ParseMasscanOutput
|
luigi.Task - ParseMasscanOutput
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -64,7 +66,7 @@ class ThreadedNmapScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Parses pickled target info dictionary and runs targeted nmap scans against only open ports. """
|
""" 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_command = [ # placeholders will be overwritten with appropriate info in loop below
|
||||||
"nmap",
|
"nmap",
|
||||||
"--open",
|
"--open",
|
||||||
"PLACEHOLDER-IDX-2" "-n",
|
"PLACEHOLDER-IDX-2",
|
||||||
|
"-n",
|
||||||
"-sC",
|
"-sC",
|
||||||
"-T",
|
"-T",
|
||||||
"4",
|
"4",
|
||||||
@@ -140,6 +143,7 @@ class SearchsploitScan(luigi.Task):
|
|||||||
top_ports: Scan top N most popular ports *--* Required by upstream 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
|
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
|
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):
|
def requires(self):
|
||||||
@@ -159,6 +163,7 @@ class SearchsploitScan(luigi.Task):
|
|||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
"interface": self.interface,
|
"interface": self.interface,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
|
"results_dir": self.results_dir,
|
||||||
}
|
}
|
||||||
return ThreadedNmapScan(**args)
|
return ThreadedNmapScan(**args)
|
||||||
|
|
||||||
@@ -173,7 +178,7 @@ class SearchsploitScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Grabs the xml files created by ThreadedNmap and runs searchsploit --nmap on each one, saving the output. """
|
""" Grabs the xml files created by ThreadedNmap and runs searchsploit --nmap on each one, saving the output. """
|
||||||
|
|||||||
@@ -1,14 +1,22 @@
|
|||||||
import shutil
|
import shutil
|
||||||
import logging
|
import logging
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import luigi
|
import luigi
|
||||||
|
|
||||||
|
from recon.config import defaults
|
||||||
|
|
||||||
|
|
||||||
class TargetList(luigi.ExternalTask):
|
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()
|
target_file = luigi.Parameter()
|
||||||
|
results_dir = luigi.Parameter(default=defaults.get("results-dir", ""))
|
||||||
|
|
||||||
def output(self):
|
def output(self):
|
||||||
""" Returns the target output for this task. target_file.ips || target_file.domains
|
""" Returns the target output for this task. target_file.ips || target_file.domains
|
||||||
@@ -24,8 +32,9 @@ class TargetList(luigi.ExternalTask):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
luigi.local_target.LocalTarget
|
||||||
"""
|
"""
|
||||||
|
print(f"debug-epi: targets {self.results_dir}")
|
||||||
try:
|
try:
|
||||||
with open(self.target_file) as f:
|
with open(str(self.target_file)) as f:
|
||||||
first_line = f.readline()
|
first_line = f.readline()
|
||||||
ipaddress.ip_interface(first_line.strip()) # is it a valid ip/network?
|
ipaddress.ip_interface(first_line.strip()) # is it a valid ip/network?
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
@@ -39,5 +48,10 @@ class TargetList(luigi.ExternalTask):
|
|||||||
# no exception thrown; ip address found
|
# no exception thrown; ip address found
|
||||||
with_suffix = f"{self.target_file}.ips"
|
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)
|
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
|
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
|
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
|
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", ""))
|
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||||
@@ -44,6 +45,7 @@ class AquatoneScan(luigi.Task):
|
|||||||
luigi.Task - GatherWebTargets
|
luigi.Task - GatherWebTargets
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -61,7 +63,7 @@ class AquatoneScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Defines the options/arguments sent to aquatone after processing.
|
""" 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
|
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
|
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
|
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", ""))
|
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||||
@@ -46,6 +47,7 @@ class CORScannerScan(ExternalProgramTask):
|
|||||||
luigi.Task - GatherWebTargets
|
luigi.Task - GatherWebTargets
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -63,7 +65,7 @@ class CORScannerScan(ExternalProgramTask):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def program_args(self):
|
||||||
""" Defines the options/arguments sent to tko-subs after processing.
|
""" 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
|
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
|
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
|
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", ""))
|
proxy = luigi.Parameter(default=defaults.get("proxy", ""))
|
||||||
@@ -59,6 +60,7 @@ class GobusterScan(luigi.Task):
|
|||||||
luigi.Task - GatherWebTargets
|
luigi.Task - GatherWebTargets
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -79,7 +81,7 @@ class GobusterScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Defines the options/arguments sent to gobuster after processing.
|
""" 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
|
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
|
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
|
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):
|
def requires(self):
|
||||||
@@ -37,6 +38,7 @@ class TKOSubsScan(ExternalProgramTask):
|
|||||||
luigi.Task - GatherWebTargets
|
luigi.Task - GatherWebTargets
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -54,7 +56,7 @@ class TKOSubsScan(ExternalProgramTask):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def program_args(self):
|
||||||
""" Defines the options/arguments sent to tko-subs after processing.
|
""" 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
|
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
|
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
|
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", ""))
|
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||||
@@ -107,6 +110,7 @@ class SubjackScan(ExternalProgramTask):
|
|||||||
luigi.Task - GatherWebTargets
|
luigi.Task - GatherWebTargets
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -124,7 +128,7 @@ class SubjackScan(ExternalProgramTask):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def program_args(self):
|
||||||
""" Defines the options/arguments sent to subjack after processing.
|
""" Defines the options/arguments sent to subjack after processing.
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import pickle
|
|||||||
import luigi
|
import luigi
|
||||||
from luigi.util import inherits
|
from luigi.util import inherits
|
||||||
|
|
||||||
from recon.config import web_ports
|
|
||||||
from recon.amass import ParseAmassOutput
|
from recon.amass import ParseAmassOutput
|
||||||
from recon.masscan import ParseMasscanOutput
|
from recon.masscan import ParseMasscanOutput
|
||||||
|
from recon.config import web_ports
|
||||||
|
|
||||||
|
|
||||||
@inherits(ParseMasscanOutput, ParseAmassOutput)
|
@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
|
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
|
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
|
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):
|
def requires(self):
|
||||||
@@ -31,6 +32,7 @@ class GatherWebTargets(luigi.Task):
|
|||||||
dict(str: ParseMasscanOutput, str: ParseAmassOutput)
|
dict(str: ParseMasscanOutput, str: ParseAmassOutput)
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -40,7 +42,9 @@ class GatherWebTargets(luigi.Task):
|
|||||||
return {
|
return {
|
||||||
"masscan-output": ParseMasscanOutput(**args),
|
"masscan-output": ParseMasscanOutput(**args),
|
||||||
"amass-output": ParseAmassOutput(
|
"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:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def run(self):
|
||||||
""" Gather all potential web targets into a single file to pass farther down the pipeline. """
|
""" 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
|
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
|
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
|
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", ""))
|
threads = luigi.Parameter(default=defaults.get("threads", ""))
|
||||||
@@ -53,6 +54,7 @@ class WebanalyzeScan(luigi.Task):
|
|||||||
luigi.Task - GatherWebTargets
|
luigi.Task - GatherWebTargets
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -72,7 +74,7 @@ class WebanalyzeScan(luigi.Task):
|
|||||||
Returns:
|
Returns:
|
||||||
luigi.local_target.LocalTarget
|
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):
|
def _wrapped_subprocess(self, cmd):
|
||||||
with open(f"webanalyze.{cmd[2].replace('//', '_').replace(':', '')}.txt", "wb") as f:
|
with open(f"webanalyze.{cmd[2].replace('//', '_').replace(':', '')}.txt", "wb") as f:
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class FullScan(luigi.WrapperTask):
|
|||||||
def requires(self):
|
def requires(self):
|
||||||
""" FullScan is a wrapper, as such it requires any Tasks that it wraps. """
|
""" FullScan is a wrapper, as such it requires any Tasks that it wraps. """
|
||||||
args = {
|
args = {
|
||||||
|
"results_dir": self.results_dir,
|
||||||
"rate": self.rate,
|
"rate": self.rate,
|
||||||
"target_file": self.target_file,
|
"target_file": self.target_file,
|
||||||
"top_ports": self.top_ports,
|
"top_ports": self.top_ports,
|
||||||
@@ -39,9 +40,11 @@ class FullScan(luigi.WrapperTask):
|
|||||||
|
|
||||||
yield GobusterScan(**args)
|
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"):
|
for gobuster_opt in ("proxy", "wordlist", "extensions", "recursive"):
|
||||||
del args[gobuster_opt]
|
del args[gobuster_opt]
|
||||||
|
|
||||||
|
# add aquatone scan specific option
|
||||||
args.update({"scan_timeout": self.scan_timeout})
|
args.update({"scan_timeout": self.scan_timeout})
|
||||||
|
|
||||||
yield AquatoneScan(**args)
|
yield AquatoneScan(**args)
|
||||||
@@ -56,3 +59,41 @@ class FullScan(luigi.WrapperTask):
|
|||||||
del args["threads"]
|
del args["threads"]
|
||||||
|
|
||||||
yield TKOSubsScan(**args)
|
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