diff --git a/intercepting-communication/firewall-1/.init b/intercepting-communication/firewall-1/.init new file mode 120000 index 00000000..ea4ba499 --- /dev/null +++ b/intercepting-communication/firewall-1/.init @@ -0,0 +1 @@ +../.init \ No newline at end of file diff --git a/intercepting-communication/firewall-1/DESCRIPTION.md b/intercepting-communication/firewall-1/DESCRIPTION.md new file mode 100644 index 00000000..e69de29b diff --git a/intercepting-communication/firewall-1/run b/intercepting-communication/firewall-1/run new file mode 100755 index 00000000..7449cccb --- /dev/null +++ b/intercepting-communication/firewall-1/run @@ -0,0 +1,55 @@ +#!/usr/bin/exec-suid --real -- /usr/bin/python -I + +import multiprocessing +import os +import socket +import socketserver +import time + +import psutil +from dojjail import Host, Network + +flag = open("/flag").read() +parent_process = psutil.Process(os.getppid()) + +class ServerHost(Host): + def entrypoint(self): + last_connected_time = multiprocessing.Value("d", time.time()) + + def watchdog(): + while True: + with last_connected_time.get_lock(): + if time.time() - last_connected_time.value > 2: + print(flag, flush=True) + break + time.sleep(1) + + watchdog_process = multiprocessing.Process(target=watchdog) + watchdog_process.daemon = True + watchdog_process.start() + + class ForkingTCPHandler(socketserver.BaseRequestHandler): + def handle(self): + with last_connected_time.get_lock(): + last_connected_time.value = time.time() + self.request.recv(1024) + + with socketserver.ForkingTCPServer(("0.0.0.0", 31337), ForkingTCPHandler) as server: + server.serve_forever() + +class ClientHost(Host): + def entrypoint(self): + while True: + try: + with socket.create_connection(("10.0.0.1", 31337)) as client_socket: + client_socket.sendall(b"Hello, World!\n") + time.sleep(1) + except (OSError, ConnectionError, TimeoutError): + continue + +user_host = ServerHost("ip-10-0-0-1", privileged_uid=parent_process.uids().effective) +client_host = ClientHost("ip-10-0-0-2") +network = Network(hosts={user_host: "10.0.0.1", client_host: "10.0.0.2"}, subnet="10.0.0.0/24") +network.run() + +user_host.interactive(environ=parent_process.environ()) diff --git a/intercepting-communication/firewall-2/.init b/intercepting-communication/firewall-2/.init new file mode 120000 index 00000000..ea4ba499 --- /dev/null +++ b/intercepting-communication/firewall-2/.init @@ -0,0 +1 @@ +../.init \ No newline at end of file diff --git a/intercepting-communication/firewall-2/DESCRIPTION.md b/intercepting-communication/firewall-2/DESCRIPTION.md new file mode 100644 index 00000000..e69de29b diff --git a/intercepting-communication/firewall-2/run b/intercepting-communication/firewall-2/run new file mode 100755 index 00000000..37c5adbf --- /dev/null +++ b/intercepting-communication/firewall-2/run @@ -0,0 +1,61 @@ +#!/usr/bin/exec-suid --real -- /usr/bin/python -I + +import multiprocessing +import os +import socket +import socketserver +import time + +import psutil +from dojjail import Host, Network + +flag = open("/flag").read() +parent_process = psutil.Process(os.getppid()) + +class ServerHost(Host): + def entrypoint(self): + manager = multiprocessing.Manager() + last_connected_times = manager.dict() + + def watchdog(): + while True: + time.sleep(1) + current_time = time.time() + if current_time - last_connected_times.get("10.0.0.2", current_time) > 2: + continue + if current_time - last_connected_times.get("10.0.0.3", current_time) < 2: + continue + print(flag, flush=True) + break + + watchdog_process = multiprocessing.Process(target=watchdog) + watchdog_process.daemon = True + watchdog_process.start() + + class ForkingTCPHandler(socketserver.BaseRequestHandler): + def handle(self): + client_ip, _ = self.client_address + last_connected_times[client_ip] = time.time() + self.request.recv(1024) + + with socketserver.ForkingTCPServer(("0.0.0.0", 31337), ForkingTCPHandler) as server: + server.serve_forever() + +class ClientHost(Host): + def entrypoint(self): + while True: + try: + with socket.create_connection(("10.0.0.1", 31337)) as client_socket: + client_socket.sendall(b"Hello, World!\n") + time.sleep(1) + except (OSError, ConnectionError, TimeoutError): + continue + +user_host = ServerHost("ip-10-0-0-1", privileged_uid=parent_process.uids().effective) +client_host_1 = ClientHost("ip-10-0-0-2") +client_host_2 = ClientHost("ip-10-0-0-3") +network = Network(hosts={user_host: "10.0.0.1", client_host_1: "10.0.0.2", client_host_2: "10.0.0.3"}, + subnet="10.0.0.0/24") +network.run() + +user_host.interactive(environ=parent_process.environ()) diff --git a/intercepting-communication/firewall-3/.init b/intercepting-communication/firewall-3/.init new file mode 120000 index 00000000..ea4ba499 --- /dev/null +++ b/intercepting-communication/firewall-3/.init @@ -0,0 +1 @@ +../.init \ No newline at end of file diff --git a/intercepting-communication/firewall-3/DESCRIPTION.md b/intercepting-communication/firewall-3/DESCRIPTION.md new file mode 100644 index 00000000..e69de29b diff --git a/intercepting-communication/firewall-3/run b/intercepting-communication/firewall-3/run new file mode 100755 index 00000000..1475c410 --- /dev/null +++ b/intercepting-communication/firewall-3/run @@ -0,0 +1,44 @@ +#!/usr/bin/exec-suid --real -- /usr/bin/python -I + +import os +import random +import socket +import subprocess + +import psutil +from dojjail import Host, Network + +flag = open("/flag").read() +parent_process = psutil.Process(os.getppid()) + +def drop_packets(dport): + subprocess.run(["/usr/sbin/iptables", + "-A", "OUTPUT", + "-p", "tcp", + "--dport", str(dport), + "-j", "DROP"], + stdin=subprocess.DEVNULL, + capture_output=True, + check=True) + +class ServerHost(Host): + def entrypoint(self): + server_socket = socket.socket() + server_socket.bind(("0.0.0.0", 31337)) + server_socket.listen() + while True: + try: + connection, _ = server_socket.accept() + connection.sendall(flag.encode()) + connection.close() + except ConnectionError: + continue + +user_host = Host("ip-10-0-0-1", privileged_uid=parent_process.uids().effective) +server_host = ServerHost("ip-10-0-0-2") +network = Network(hosts={user_host: "10.0.0.1", server_host: "10.0.0.2"}, subnet="10.0.0.0/24") +network.run() + +user_host.exec(lambda: drop_packets(31337)) + +user_host.interactive(environ=parent_process.environ()) diff --git a/intercepting-communication/module.yml b/intercepting-communication/module.yml index 18c522b1..eb8f1c17 100644 --- a/intercepting-communication/module.yml +++ b/intercepting-communication/module.yml @@ -21,6 +21,12 @@ challenges: name: Sniffing Cookies - id: level-7 name: Network Configuration +- id: firewall-1 + name: Firewall 1 +- id: firewall-2 + name: Firewall 2 +- id: firewall-3 + name: Firewall 3 - id: level-8 name: Ethernet - id: level-9 diff --git a/intercepting-communication/run b/intercepting-communication/run deleted file mode 100755 index 06731723..00000000 --- a/intercepting-communication/run +++ /dev/null @@ -1,751 +0,0 @@ -#!/opt/pwn.college/python - -import os -import time -import random -import socket -import socketserver -import subprocess -import multiprocessing -import enum -import signal -import ctypes -import pathlib - -import scapy.all as scapy - - -libc = ctypes.CDLL("libc.so.6") - -flag = open("/flag").read() -config = (pathlib.Path(__file__).parent / ".config").read_text() -level = int(config) -practice = os.getuid() == 0 - - -class CLONE(enum.IntFlag): - NEWNS = 0x00020000 # New mount namespace group - NEWCGROUP = 0x02000000 # New cgroup namespace - NEWUTS = 0x04000000 # New utsname namespace - NEWIPC = 0x08000000 # New ipc namespace - NEWUSER = 0x10000000 # New user namespace - NEWPID = 0x20000000 # New pid namespace - NEWNET = 0x40000000 # New network namespace - - -class CAP(enum.IntFlag): - CHOWN = 1 << 0 - DAC_OVERRIDE = 1 << 1 - DAC_READ_SEARCH = 1 << 2 - FOWNER = 1 << 3 - FSETID = 1 << 4 - KILL = 1 << 5 - SETGID = 1 << 6 - SETUID = 1 << 7 - SETPCAP = 1 << 8 - LINUX_IMMUTABLE = 1 << 9 - NET_BIND_SERVICE = 1 << 10 - NET_BROADCAST = 1 << 11 - NET_ADMIN = 1 << 12 - NET_RAW = 1 << 13 - IPC_LOCK = 1 << 14 - IPC_OWNER = 1 << 15 - SYS_MODULE = 1 << 16 - SYS_RAWIO = 1 << 17 - SYS_CHROOT = 1 << 18 - SYS_PTRACE = 1 << 19 - SYS_PACCT = 1 << 20 - SYS_ADMIN = 1 << 21 - SYS_BOOT = 1 << 22 - SYS_NICE = 1 << 23 - SYS_RESOURCE = 1 << 24 - SYS_TIME = 1 << 25 - SYS_TTY_CONFIG = 1 << 26 - MKNOD = 1 << 27 - LEASE = 1 << 28 - AUDIT_WRITE = 1 << 29 - AUDIT_CONTROL = 1 << 30 - SETFCAP = 1 << 31 - MAC_OVERRIDE = 1 << 32 - MAC_ADMIN = 1 << 33 - SYSLOG = 1 << 34 - WAKE_ALARM = 1 << 35 - BLOCK_SUSPEND = 1 << 36 - AUDIT_READ = 1 << 37 - - -def limit_capabilities(capabilities): - PR_CAP_AMBIENT = 47 - PR_CAP_AMBIENT_RAISE = 2 - PR_CAP_AMBIENT_LOWER = 3 - PR_GET_SECUREBITS = 27 - PR_SET_SECUREBITS = 28 - SECBIT_NOROOT = 1 << 1 - SECBIT_NOROOT_LOCKED = 1 << 2 - _LINUX_CAPABILITY_VERSION_3 = 0x20080522 - _LINUX_CAPABILITY_U32S_3 = 2 - - class __user_cap_header_struct(ctypes.Structure): - _fields_ = [ - ("version", ctypes.c_uint32), - ("pid", ctypes.c_int), - ] - - class __user_cap_data_struct(ctypes.Structure): - _fields_ = [ - ("effective", ctypes.c_uint32), - ("permitted", ctypes.c_uint32), - ("inheritable", ctypes.c_uint32), - ] - - secure_bits = libc.prctl(PR_GET_SECUREBITS) - assert secure_bits != -1 - - secure_bits |= SECBIT_NOROOT|SECBIT_NOROOT_LOCKED - assert libc.prctl(PR_SET_SECUREBITS, secure_bits) == 0 - - header = __user_cap_header_struct(version=_LINUX_CAPABILITY_VERSION_3, pid=0) - payload = (__user_cap_data_struct * _LINUX_CAPABILITY_U32S_3)() - assert libc.capget(ctypes.pointer(header), payload) == 0 - - payload[0].effective &= capabilities - payload[1].effective &= capabilities - payload[0].permitted &= capabilities - payload[1].permitted &= capabilities - payload[0].inheritable = payload[0].permitted - payload[1].inheritable = payload[1].permitted - - assert libc.capset(ctypes.pointer(header), payload) == 0 - - effective = (payload[1].effective << 32) | payload[0].effective - - cap_last_cap = int(open("/proc/sys/kernel/cap_last_cap").read()) - for cap in range(cap_last_cap): - if effective & (1 << cap): - assert libc.prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) == 0 - else: - assert libc.prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, cap, 0, 0) == 0 - - -def ip_run(command, *, check=True): - try: - return subprocess.run(["/sbin/ip", *command.split()], - stdin=subprocess.DEVNULL, - capture_output=True, - check=check) - except subprocess.CalledProcessError as e: - raise Exception(e.stderr.decode()) - - -class Host: - def __init__(self, id, network): - self.id = id - self.network = network - self._mac = multiprocessing.Array("B", 6) - self._ready = multiprocessing.Semaphore(0) - self._shutdown = multiprocessing.Semaphore(0) - - - @property - def ip(self): - return f"10.0.0.{self.id}" - - - @property - def mac(self): - return ":".join(f"{b:02x}" for b in self._mac) - - - @mac.setter - def mac(self, value): - self._mac[:] = [int(b, 16) for b in value.split(":")] - - - @property - def name(self): - return "-".join(["ip", *self.ip.split(".")]) - - - def run(self): - if practice and os.fork() == 0: - pcaps = pathlib.Path("/tmp/pcaps") - pcaps.mkdir(exist_ok=True) - subprocess.run(["tcpdump", - "-i", "eth0", - "-U", - "-G60", - "-w", pcaps / f"{self.name}.pcap"], - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL) - os._exit(0) - - - def manage_ip(self): - ip_run(f"addr add {self.ip}/16 dev eth0") - - - def start(self): - ip_run(f"link add veth{self.id} type veth peer name veth{self.id}-child") - ip_run(f"link set veth{self.id} master bridge0") - - sem_ns_child = multiprocessing.Semaphore(0) - sem_init_child = multiprocessing.Semaphore(0) - sem_ready_child = multiprocessing.Semaphore(0) - - host_pid = os.fork() - - if host_pid == 0: - assert libc.unshare(CLONE.NEWUTS|CLONE.NEWNET) == 0 - sem_ns_child.release() - - socket.sethostname(self.name) - - sem_init_child.acquire() - ip_run("link set lo up") - ip_run(f"link set veth{self.id}-child name eth0") - subprocess.run(["/usr/sbin/ethtool", - "-K", "eth0", - "rx", "off", - "tx", "off", - "tso", "off", - "gso", "off"], - stdin=subprocess.DEVNULL, - capture_output=True, - check=True) - ip_run("link set eth0 up") - scapy.conf.ifaces.reload() - self.mac = scapy.get_if_hwaddr("eth0") - self.manage_ip() - sem_ready_child.release() - - self._ready.acquire() - self.run() - self._shutdown.acquire() - os._exit(0) - - sem_ns_child.acquire() - ip_run(f"link set veth{self.id}-child netns {host_pid}") - ip_run(f"link set veth{self.id} up") - sem_init_child.release() - sem_ready_child.acquire() - - - def ready(self): - self._ready.release() - - - def shutdown(self): - self._shutdown.release() - - -class Network: - def __init__(self, host_configs): - self.host_configs = host_configs - self.hosts = [] - self._shutdown = multiprocessing.Semaphore(0) - - - def run(self): - pass - - - def start(self): - assert libc.unshare(CLONE.NEWUTS|CLONE.NEWNET) == 0 - socket.sethostname("challenge") - ip_run(f"link set lo up") - ip_run("link add name bridge0 type bridge") - - for host_id, host_config in enumerate(self.host_configs, start=2): - self.hosts.append(host_config(host_id, self)) - - for host in self.hosts: - host.start() - - ip_run("link set bridge0 up") - - for host in self.hosts: - host.ready() - - self.run() - - self._shutdown.acquire() - for host in self.hosts: - host.shutdown() - - - def shutdown(self): - self._shutdown.release() - - -def run_sandbox(target, *, privileged=True): - PR_SET_PDEATHSIG = 1 - PR_SET_DUMPABLE = 4 - - result = multiprocessing.Value("B", False) - - pid = os.fork() - if pid: - os.wait() - return result.value - - suid = os.geteuid() != os.getuid() - os.setpgrp() - os.seteuid(os.getuid()) - # TODO: - # libc.prctl(PR_SET_DUMPABLE, not suid) - libc.prctl(PR_SET_DUMPABLE, True) - libc.prctl(PR_SET_PDEATHSIG, signal.SIGKILL) - - sandbox_euid = os.geteuid() - sandbox_egid = os.getegid() - - unshare_result = libc.unshare( - CLONE.NEWUSER | - CLONE.NEWNS | - CLONE.NEWCGROUP | - CLONE.NEWUTS | - CLONE.NEWIPC | - CLONE.NEWPID | - CLONE.NEWNET - ) - assert unshare_result == 0 - - pid = os.fork() - if pid: - os.wait() - os._exit(0) - - proc_values = { - f"/proc/self/setgroups": "deny", - f"/proc/self/uid_map": f"0 {sandbox_euid} 1", - f"/proc/self/gid_map": f"0 {sandbox_egid} 1", - } - for path, value in proc_values.items(): - with open(path, "w") as f: - f.write(value) - - libc.prctl(PR_SET_DUMPABLE, False) - libc.prctl(PR_SET_PDEATHSIG, signal.SIGKILL) - - socket.sethostname("sandbox") - ip_run("link set dev lo up") - - result.value = target() - os._exit(0) - - -def spawn_limited_shell(capabilities): - if os.fork(): - os.wait() - return - limit_capabilities(capabilities) - os.execve("/bin/bash", ["bash"], os.environ) - - -class ShellHost(Host): - capabilities = CAP.NET_BIND_SERVICE|CAP.NET_BROADCAST|CAP.NET_ADMIN|CAP.NET_RAW - - def run(self): - super().run() - spawn_limited_shell(self.capabilities) - self.network.shutdown() - - -class SendingHost(Host): - send_delay = None - - def send(self, connection, data): - if self.send_delay: - time.sleep(self.send_delay) - connection.send(data) - - -class ServerHost(SendingHost): - @property - def address(self): - return ("0.0.0.0", 31337) - - - def handle(self, connection): - self.send(connection, flag.encode()) - - - def run(self): - super().run() - - server_handle = self.handle - class Handler(socketserver.BaseRequestHandler): - def handle(self): - try: - return server_handle(self.request) - except ConnectionError: - pass - - if os.fork() == 0: - with socketserver.ForkingTCPServer(self.address, Handler) as server: - server.serve_forever() - - -class ClientHost(SendingHost): - send_length = None - - @property - def server_address(self): - for host in self.network.hosts: - if hasattr(host, "address"): - ip, port = host.address - ip = host.ip if ip == "0.0.0.0" else ip - return (ip, port) - raise RuntimeError("ClientHost failed to find ServerHost") - - - def handle(self, connection): - data = flag.encode() - if self.send_length is None: - self.send(connection, data) - else: - for i in range((len(data) - 1) // self.send_length + 1): - self.send(connection, data[i*self.send_length:(i+1)*self.send_length]) - - - def run(self): - super().run() - while True: - try: - with socket.create_connection(self.server_address) as connection: - self.handle(connection) - except (ConnectionError, TimeoutError): - pass - time.sleep(1) - - -class EchoServerHost(ServerHost): - def handle(self, connection): - while True: - data = connection.recv(0x1000) - if not data: - break - self.send(connection, data) - - -class UnmanagedHost(Host): - def manage_ip(self): - def manage_arp_packet(packet): - WHO_HAS = 1 - IS_AT = 2 - if packet["ARP"].op == WHO_HAS and packet["ARP"].pdst == self.ip: - scapy.sendp( - scapy.Ether( - src=self.mac, - dst=packet["Ether"].src, - ) / - scapy.ARP( - op=IS_AT, - hwsrc=self.mac, - psrc=self.ip, - hwdst=packet["ARP"].hwsrc, - pdst=packet["ARP"].psrc, - ), - iface="eth0", - verbose=False, - ) - - if os.fork() == 0: - scapy.sniff(filter="arp", prn=manage_arp_packet, iface="eth0") - - -class RawPacketHost(UnmanagedHost): - def handle_packet(self, packet): - success = self.check_packet(packet) - if success: - print() - print(flag) - self.network.shutdown() - - def check_packet(self, packet): - pass - - def run(self): - super().run() - scapy.sniff(prn=self.handle_packet, iface="eth0") - - -class EtherPacketHost(RawPacketHost): - def check_packet(self, packet): - return ( - "Ether" in packet and - packet["Ether"].type == 0xFFFF - ) - - -class IPPacketHost(RawPacketHost): - def check_packet(self, packet): - return ( - "IP" in packet and - packet["IP"].dst == self.ip and - packet["IP"].proto == 0xFF - ) - - -class TCPPacketHost(RawPacketHost): - def check_packet(self, packet): - return ( - "TCP" in packet and - packet["TCP"].sport == 31337 and - packet["TCP"].dport == 31337 and - packet["TCP"].seq == 31337 and - packet["TCP"].ack == 31337 and - packet["TCP"].flags == "APRSF" - ) - - -class TCPHandshakeHost(RawPacketHost): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.seq = None - - def check_packet(self, packet): - if ("IP" in packet and "TCP" in packet["IP"] and len(packet["IP"].layers()) == 2 and - packet["IP"].dst == self.ip and - packet["TCP"].sport == 31337 and - packet["TCP"].dport == 31337 and - packet["TCP"].seq == 31337 and - packet["TCP"].flags == "S"): - - self.seq = random.randrange(0, 2**32) - - scapy.sendp( - scapy.Ether( - src=self.mac, - dst=packet["Ether"].src, - ) / - scapy.IP( - src=self.ip, - dst=packet["IP"].src, - ) / - scapy.TCP( - sport=packet["TCP"].dport, - dport=packet["TCP"].sport, - seq=self.seq, - ack=(packet["TCP"].seq + 1) & (2**32 - 1), - flags="SA", - ), - iface="eth0", - verbose=False, - ) - - self.seq = (self.seq + 1) & (2**32 - 1) - - if ("IP" in packet and "TCP" in packet["IP"] and len(packet["IP"].layers()) == 2 and - packet["IP"].dst == self.ip and - packet["TCP"].sport == 31337 and - packet["TCP"].dport == 31337 and - packet["TCP"].seq == (31337 + 1) and - packet["TCP"].ack == self.seq and - packet["TCP"].flags == "A"): - - return True - - -class ARPHost(RawPacketHost): - @property - def check_host(self): - return self.network.hosts[0] - - def check_packet(self, packet): - WHO_HAS = 1 - IS_AT = 2 - return ( - "Ether" in packet and "ARP" in packet["Ether"] and len(packet["Ether"].layers()) == 2 and - packet["Ether"].src == self.check_host.mac and - packet["ARP"].op == IS_AT and - packet["ARP"].hwsrc == self.check_host.mac and - packet["ARP"].psrc == self.check_host.ip - ) - - -class AuthenticatedServerHost(ServerHost): - def handle(self, connection): - client_ip, client_port = connection.getpeername() - - secret = os.urandom(32).hex() - for host in self.network.hosts: - if host.ip == client_ip and isinstance(host, AuthenticatedClientHost): - host.secret = secret - - self.send(connection, b"SECRET:\n") - client_secret = connection.recv(0x1000).decode("latin").strip() - - self.send(connection, - (b"COMMANDS:\n" - b"ECHO\n" - b"FLAG\n" - b"COMMAND:\n")) - command = connection.recv(0x1000).decode("latin").strip() - - if command == "ECHO": - data = connection.recv(0x1000) - self.send(connection, data) - - elif command == "FLAG": - if client_secret != secret: - self.send(connection, b"UNAUTHORIZED\n") - else: - self.send(connection, flag.encode()) - - else: - self.send(connection, b"???\n") - - -class AuthenticatedClientHost(ClientHost): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._secret = multiprocessing.Array("B", 32) - - - @property - def secret(self): - return bytes(self._secret).hex() - - - @secret.setter - def secret(self, value): - self._secret[:] = bytes.fromhex(value) - - - def handle(self, connection): - try: - secret_request = connection.recv(0x1000).decode("latin") - assert secret_request == "SECRET:\n" - - self.send(connection, f"{self.secret}\n".encode()) - - command_request = connection.recv(0x1000).decode("latin") - assert command_request == ("COMMANDS:\n" - "ECHO\n" - "FLAG\n" - "COMMAND:\n") - - self.send(connection, b"ECHO\n") - - data = b"Hello, World!\n" - self.send(connection, data) - echo_response = connection.recv(0x1000) - assert echo_response == data - - except AssertionError: - self.send(connection, b"???\n") - - -class WebHost(Host): - def run(self): - super().run() - - from flask import Flask - app = Flask(__name__) - - @app.route("/") - def index(): - return f"Hello, I am {self.name}\n" - - app.run("0.0.0.0", 80) - - -class RandomIPHost(Host): - subnet = 16 - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - while True: - network = sum(int(octet) << (24 - i * 8) for i, octet in enumerate("10.0.0.0".split("."))) - network |= int.from_bytes(os.urandom(((32 - self.subnet) - 1) // 8 + 1), "little") >> (self.subnet % 8) - self._ip = ".".join(str(network >> (24 - i * 8) & 0xFF) for i in range(4)) - if self.ip not in set(host.ip for host in self.network.hosts): - break - - @property - def ip(self): - return self._ip - - -class ARPFlushHost(Host): - def run(self): - if os.fork() == 0: - while True: - ip_run("neigh flush all") - time.sleep(1) - super().run() - - -def mixin_host(*hosts, **kwargs): - return type("MixinHost", hosts, kwargs) - - -def challenge(): - description = { - 1: "connect to a remote host.\nThe remote host at `10.0.0.3` is listening on port `31337`.", - 2: "listen for a connection from a remote host.\nYou should listen on port `31337`.", - 3: "find and connect to a remote host.\nThe remote host is somewhere on the `10.0.0.0/24` subnetwork, listening on port `31337`.", - 4: "find and connect to a remote host.\nThe remote host is somewhere on the `10.0.0.0/16` subnetwork, listening on port `31337`.", - 5: "monitor traffic from a remote host.\nYour host is already receiving traffic on port `31337`.", - 6: "monitor slow traffic from a remote host.\nYour host is already receiving traffic on port `31337`.", - 7: "hijack traffic from a remote host by configuring your network interface.\nThe remote host at `10.0.0.4` is communicating with the remote host at `10.0.0.2` on port `31337`.", - 8: "manually send an Ethernet packet.\nThe packet should have `Ether type=0xFFFF`.\nThe packet should be sent to the remote host at `10.0.0.3`.", - 9: "manually send an Internet Protocol packet.\nThe packet should have `IP proto=0xFF`.\nThe packet should be sent to the remote host at `10.0.0.3`.", - 10: "manually send a Transmission Control Protocol packet.\nThe packet should have `TCP sport=31337, dport=31337, seq=31337, ack=31337, flags=APRSF`.\nThe packet should be sent to the remote host at `10.0.0.3`.", - 11: "manually perform a Transmission Control Protocol handshake.\nThe initial packet should have `TCP sport=31337, dport=31337, seq=31337`.\nThe handshake should occur with the remote host at `10.0.0.3`.", - 12: "manually send an Address Resolution Protocol packet.\nThe packet should have `ARP op=is-at` and correctly inform the remote host of where the sender can be found.\nThe packet should be sent to the remote host at `10.0.0.3`.", - 13: "hijack traffic from a remote host using ARP.\nYou do not have the capabilities of a NET ADMIN.\nThe remote host at `10.0.0.4` is communicating with the remote host at `10.0.0.2` on port `31337`.", - 14: "man in the middle traffic between two remote hosts and inject extra traffic.\nThe remote host at `10.0.0.4` is communicating with the remote host at `10.0.0.3` on port `31337`.", - }[level] - description = f"In this challenge you will {description}" - - print("===== Welcome to Intercepting Communication! =====") - print("In this series of challenges, you will be working within a virtual network in order to intercept networked traffic.") - print(description) - print() - - def target(): - levels = { - 1: [ShellHost, ServerHost], - 2: [mixin_host(ShellHost, address=("10.0.0.2", 31337)), ClientHost], - 3: [ShellHost, mixin_host(ServerHost, RandomIPHost, subnet=24)], - 4: [ShellHost, mixin_host(ServerHost, RandomIPHost)], - 5: [mixin_host(ShellHost, EchoServerHost), ClientHost], - 6: [mixin_host(ShellHost, EchoServerHost), mixin_host(ClientHost, send_delay=1, send_length=1)], - 7: [ - EchoServerHost, - ShellHost, - mixin_host(ARPFlushHost, ClientHost), - ], - 8: [mixin_host(ShellHost, UnmanagedHost), EtherPacketHost], - 9: [mixin_host(ShellHost, UnmanagedHost), IPPacketHost], - 10: [mixin_host(ShellHost, UnmanagedHost), TCPPacketHost], - 11: [mixin_host(ShellHost, UnmanagedHost), TCPHandshakeHost], - 12: [mixin_host(ShellHost, UnmanagedHost), ARPHost], - 13: [ - EchoServerHost, - mixin_host(ShellHost, capabilities=CAP.NET_BIND_SERVICE|CAP.NET_BROADCAST|CAP.NET_RAW), - ClientHost, - ], - 14: [ - ShellHost, - mixin_host(AuthenticatedServerHost, send_delay=1), - mixin_host(AuthenticatedClientHost, send_delay=1), - ], - 15: [ - ShellHost, - AuthenticatedServerHost, - AuthenticatedClientHost, - ] - } - - network = Network(levels[level]) - network.start() - return False - - run_sandbox(target, privileged=False) - - -challenge()