def __init__( self, victim: ChainImage, normal: ChainImage, exploit: ChainImage, wait_times, warmup_time=60, recording_time=300, exploit_start_time=0, storage_services: List[CollectorStorageService] = None ): """ initialize all time sequences needed for the recording process as well es for statistically relevant execution """ self.general_meta = ScenarioMeta(exploit_start_time, warmup_time, recording_time) self.logger = log.get_logger("control_script", ScenarioEnvironment().logging_queue) self.logging_thread = Thread(target=log.print_logs) self.logging_thread.start() self.storage_services = storage_services if storage_services else [] self.victim = ScenarioVictim(victim) self.normal = ScenarioNormal(normal, wait_times) self.exploit = ScenarioAttacker(exploit) Collector().set_meta( name=self.general_meta.name, image=victim.name, recording_time=self.general_meta.recording_time, is_exploit=self.general_meta.is_exploit)
def optimize_attack_time(image: ChainImage): env = ScenarioEnvironment() logger = log.get_logger("postprocessing", env.logging_queue) scap = os.path.join(env.out_dir, f'{env.recording_name}.scap') pcap = os.path.join(env.out_dir, f'{env.recording_name}.pcap') ip = Collector().attacker_ip logger.info(f"Starting postprocessing for attacker {ip}") matcher = PostprocessingMatcher(pcap, scap, ip) for command in image.commands: optimized_time, source = matcher.get_exact_attack_time( command.after_packet) Collector().set_exploit_time(command.name, optimized_time, source) logger.info(f"Finished postprocessing")
def start_container(self, check_if_available, init=None): self.container = run_image(self.image.name, self.env.network, self.env.victim_hostname, self.port_mapping) self.logger.debug("Waiting for container to be available") self.container.reload() Collector().add_container(self.env.victim_hostname, "victim", get_ip_address(self.container)) wait_until(check_if_available, 60, 1, container=self.container) self.logger.info("Container available on port(s) %s" % self.container.ports) if init is not None: init(self.container, self.logger) yield self.container self.container.stop()
def __call__(self, with_exploit=False): self.logger.info('Simulating Scenario: {}'.format(self)) with self.victim.start_container(self.wait_for_availability, self.init_victim) as container: Collector().set_container_ready() self._warmup() self._recording() self._teardown() self._postprocessing() log.stop() self.logging_thread.join()
def _warmup(self): self.logger.info('Warming up Scenario: {}'.format(self.general_meta.name)) sleep(self.general_meta.warmup_time) Collector().set_warmup_end() if self.is_exploit: exploit_time = time() + self.general_meta.exploit_time self.exploit_thread = Thread( target=self.exploit.execute_exploit_at_time, args=(exploit_time,)) self.exploit_thread.start() self.logger.info('Start Normal Behaviours for Scenario: {}'.format(self.general_meta.name)) wts = self.normal.start_simulation() self.logger.debug("Simulating with %s" % wts)
def execute_exploit_at_time(self, execution_time): while time.time() < execution_time: time.sleep(0.5) for command in self.image.commands: self.logger.info('Executing the exploit step %s now at %s' % (command.name, time.time())) Collector().set_exploit_time(command.name) cmd = format_command(command.command) if self.to_stdin: socket = self.container.attach_socket(params={ 'stdin': 1, 'stream': 1 }) socket._writing = True try: socket.write(cmd.encode() + b"\n") except: pass else: _, out = self.container.exec_run(cmd) for line in out.decode("utf-8").split("\n")[:-1]: self.logger.info(line)
def start_container(self): self.container = run_image(self.image.name, self.network, self.container_name) Collector().add_container(self.container_name, "attacker", get_ip_address(self.container))
Collector().set_exploit_time(command.name, optimized_time, source) logger.info(f"Finished postprocessing") if __name__ == '__main__': portscan = ExecCommand("sh /app/nmap.sh ${victim}", name="port-scan") bruteforce = ExecCommand("sh /app/hydra.sh ${victim}", name="brute-force", after_packet=TCPPacketPartsMatcher( ["GET /", "User-Agent: curl"], forbidden_parts=["Authorization"])) privilege_escalation = ExecCommand( "python3 /app/attacker.py ${victim}:5984", name="privilege-escalation", after_packet=TCPPacketPartsMatcher( ["GET /_all_dbs", "User-Agent: curl", "Authorization"])) remote_code = ExecCommand("python3 /app/reverse-shell.py ${victim}:5984", name="remote-code", after_packet=TCPPacketPartsMatcher( ["GET /_users/_all_docs", "Authorization"])) exploit_full = ChainImage( "exploit_couchdb", commands=[portscan, bruteforce, privilege_escalation, remote_code]) ScenarioEnvironment().out_dir = "../../couchdb_example/runs" ScenarioEnvironment().recording_name = "young_franklin_9218" Collector().add_container("x", "attacker", "192.168.224.3") optimize_attack_time(exploit_full)
def _postprocessing(self): if self.is_exploit: postprocessing.optimize_attack_time(self.exploit.image) Collector().write(self.storage_services)
def start_containers(self): for k in self.containers.keys(): args = format_command(self.image.init_args) self.containers[k] = run_image(self.image.name, network=self.network, name=k, command=args) self.logger[k] = log.get_logger(f"[NORMAL] {k}", self.queue) Collector().add_container(k, "normal", get_ip_address(self.containers[k]))