""" Test 2 -- Why a single shared secret fails structurally. Models the actual shape of CVE-2021-22681: not a specific protocol byte-format, but the architectural decision to use ONE static credential across an entire product line, with no per-device uniqueness and no revocation path. Two simulated device endpoints (Device A, Device B) each require a key before granting engineering access -- exactly like Rockwell's real "verify this is Studio 5000" check. Both are configured with the SAME shared key, mirroring the real vulnerability precisely. We show: a credential that authenticates against Device A ALSO authenticates against Device B, unmodified. Nothing about Device B's specific identity is ever checked -- only "does the presented value match the one universal secret." This is precisely why a single compromised key compromises an entire fleet, not one device. Run: python test2_shared_secret_fails.py """ import socket import socketserver import threading import time # The one static value shared across "every device in the fleet" -- # recreates the real architecture flaw: one secret, no per-device # binding, no rotation path. SHARED_KEY = "ROCKWELL-STUDIO5000-UNIVERSAL-KEY-2019" class VulnerablePLCHandler(socketserver.BaseRequestHandler): """Checks ONLY whether the presented value matches the shared key -- never checks who is asking, or which device they believe they're talking to. This is the structural core of the real flaw.""" device_name = "UNSET" def handle(self): presented = self.request.recv(1024).decode().strip() if presented == SHARED_KEY: self.request.sendall( f"ACCESS GRANTED to {self.device_name} (key matched)".encode() ) else: self.request.sendall(b"ACCESS DENIED (key mismatch)") def make_server(port, name): class Handler(VulnerablePLCHandler): device_name = name srv = socketserver.TCPServer(("127.0.0.1", port), Handler) srv.allow_reuse_address = True threading.Thread(target=srv.serve_forever, daemon=True).start() return srv def attempt(port, key): with socket.create_connection(("127.0.0.1", port), timeout=3) as s: s.sendall(key.encode()) return s.recv(1024).decode() def main(): print("[*] Standing up two simulated devices, each configured with") print(" the SAME shared authentication key -- mirroring the real") print(" architecture of CVE-2021-22681.\n") server_a = make_server(50001, "Device-A") server_b = make_server(50002, "Device-B") time.sleep(0.5) try: print("[*] Attacker obtains the key by testing/observing Device A only:\n") result_a = attempt(50001, SHARED_KEY) print(" Device A response:", result_a) print() print("[*] Attacker now presents that SAME key -- never issued for") print(" Device B specifically -- to a completely different device:\n") result_b = attempt(50002, SHARED_KEY) print(" Device B response:", result_b) print() if "GRANTED" in result_a and "GRANTED" in result_b: print("[!] RESULT: One key, obtained from a single device, granted") print(" access to a second, entirely unrelated device. Nothing about") print(" Device B's identity was ever verified -- only whether the") print(" presented value matched the ONE universal secret. This is") print(" structurally identical to why CVE-2021-22681 compromises the") print(" entire Logix product line from one leaked key, not just one") print(" installation.") finally: server_a.shutdown() server_b.shutdown() if __name__ == "__main__": main()