diff --git a/recon/nmap.py b/recon/nmap.py index 59790cc..51a4fb1 100644 --- a/recon/nmap.py +++ b/recon/nmap.py @@ -123,3 +123,72 @@ class ThreadedNmap(luigi.Task): with concurrent.futures.ThreadPoolExecutor(max_workers=self.threads) as executor: executor.map(subprocess.run, commands) + + +@inherits(ThreadedNmap) +class Searchsploit(luigi.Task): + """ Run searchcploit against each nmap*.xml file in the TARGET-nmap-results directory and write results to disk. + + searchsploit commands are structured like the example below. + + searchsploit --nmap htb-targets-nmap-results/nmap.10.10.10.155-tcp.xml + + The corresponding luigi command is shown below. + + PYTHONPATH=$(pwd) luigi --local-scheduler --module recon.nmap Searchsploit --target-file htb-targets --top-ports 5000 + + Args: + threads: number of threads for parallel nmap command execution *--* Required by upstream Task + rate: desired rate for transmitting packets (packets per second) *--* Required by upstream Task + interface: use the named raw network interface, such as "eth0" *--* 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 + target_file: specifies the file on disk containing a list of ips or domains *--* Required by upstream Task + """ + + def requires(self): + """ Searchsploit depends on ThreadedNmap to run. + + TargetList expects target_file as a parameter. + Masscan expects rate, target_file, interface, and either ports or top_ports as parameters. + ThreadedNmap expects threads + + Returns: + luigi.Task - ThreadedNmap + """ + args = { + "rate": self.rate, + "ports": self.ports, + "threads": self.threads, + "top_ports": self.top_ports, + "interface": self.interface, + "target_file": self.target_file, + } + return ThreadedNmap(**args) + + def output(self): + """ Returns the target output for this task. + + Naming convention for the output folder is TARGET_FILE-searchsploit-results. + + The output folder will be populated with all of the output files generated by + any searchsploit commands run. + + Returns: + luigi.local_target.LocalTarget + """ + return luigi.LocalTarget(f"{self.target_file}-searchsploit-results") + + def run(self): + """ Grabs the xml files created by ThreadedNmap and runs searchsploit --nmap on each one, saving the output. """ + for entry in Path(self.input().path).glob("nmap*.xml"): + proc = subprocess.run(["searchsploit", "--nmap", str(entry)], stderr=subprocess.PIPE) + if proc.stderr: + Path(self.output().path).mkdir(parents=True, exist_ok=True) + + # change wall-searchsploit-results/nmap.10.10.10.157-tcp to 10.10.10.157 + target = entry.stem.replace("nmap.", "").replace("-tcp", "").replace("-udp", "") + + Path( + f"{self.output().path}/searchsploit.{target}-{entry.stem[-3:]}.txt" + ).write_bytes(proc.stderr)