# Protocol Seven | Field | Value | |-------|-------| | Category | OT | | Difficulty | Hard | | Points | 513 | | Author | Eun0us | | CTF | Espilon 2026 | --- ## Description Eiri Masami split Protocol Seven across three industrial control systems before his death. The encryption key hides in BACnet harmonics. The encrypted payload lives on OPC-UA. The initialization vector is stored in an EtherNet/IP controller. No single system reveals the truth. Cross-reference all three protocols. Reconstruct Protocol Seven. Become one with the Wired. **Ports:** - 47809/udp: BACnet/IP - 4841/tcp: OPC-UA Binary - 44819/tcp: EtherNet/IP Format: **ESPILON{flag}** --- ## TL;DR Read 8 BACnet AnalogValue harmonics (integer parts spell `Eiri_Key`). Read OPC-UA for the 32-byte encrypted payload and hints pointing to BACnet and EtherNet/IP. Read EtherNet/IP for the rotation nonce (3). XOR the payload with the 8-byte repeating key, then rotate right by the nonce to recover the flag. --- ## Tools | Tool | Purpose | |------|---------| | Python 3 + `BAC0` | BACnet/IP read | | Python 3 + `asyncua` | OPC-UA anonymous read | | Python 3 + `cpppo` | EtherNet/IP tag read | --- ## Solution ### Step 1 — Port discovery ```bash nmap -sU -p 47809 # BACnet nmap -p 4841,44819 # OPC-UA, EtherNet/IP ``` All three ports are open. ### Layer 1 — BACnet key extraction ```python import BAC0 bacnet = BAC0.lite(ip="/24") bacnet.whois("*:47809") # → Device:7777 "Protocol-Seven-Key" ``` Device description: *"Key Harmonic Array — integer components matter"* Read all 8 harmonics: ```python harmonics = [] for i in range(8): val = bacnet.read(f"7777 analogValue {i} presentValue") harmonics.append(int(val)) # truncate float to integer ``` | Object | presentValue | int | Char | |--------|-------------|-----|------| | AnalogValue:0 | 69.14 | 69 | E | | AnalogValue:1 | 105.92 | 105 | i | | AnalogValue:2 | 114.37 | 114 | r | | AnalogValue:3 | 105.68 | 105 | i | | AnalogValue:4 | 95.44 | 95 | _ | | AnalogValue:5 | 75.81 | 75 | K | | AnalogValue:6 | 101.22 | 101 | e | | AnalogValue:7 | 121.55 | 121 | y | **XOR key = `Eiri_Key`** ![BACnet read output showing the 8 harmonic float values](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/p7_bacnet.png) ### Layer 2 — OPC-UA payload extraction ```python import asyncio from asyncua import Client async def get_payload(): async with Client("opc.tcp://:4841/protocol7/") as c: ns_array = await c.get_namespace_array() # Find urn:protocol-seven:payload ns = ns_array.index("urn:protocol-seven:payload") vault = await c.nodes.root.get_child( [f"0:Objects", f"{ns}:Protocol7_Vault"]) payload = await vault.get_child(f"{ns}:Payload_Encrypted") iv_hint = await vault.get_child(f"{ns}:IV_Hint") return (await payload.get_value()), (await iv_hint.get_value()) payload_bytes, iv_hint = asyncio.run(get_payload()) # iv_hint: "Rotation offset from CIP controller — read NONCE tag" ``` ![OPC-UA browse showing Protocol7_Vault contents and hints](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/p7_opcua.png) ### Layer 3 — EtherNet/IP nonce extraction ```python from cpppo.server.enip.get_attribute import proxy_simple as device with device(host="", port=44819) as via: nonce = via.read("NONCE") # = 3 check = [via.read(f"Assembly_Check[{i}]") for i in range(3)] # check = [47809, 4841, 44819] confirms all three ports ``` **Rotation nonce = 3** ### Decryption ```python key = b"Eiri_Key" nonce = 3 # Step 1: XOR with repeating 8-byte key xored = bytes(payload_bytes[i] ^ key[i % 8] for i in range(32)) # Step 2: rotate right by nonce (undo left rotation used during encryption) flag_bytes = xored[-nonce:] + xored[:-nonce] flag = flag_bytes.rstrip(b'\x00').decode() print(flag) ``` ![Python decryption script printing the reconstructed flag](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/p7_decrypt.png) --- ## Key insights - Device description *"integer components matter"* directs you to truncate float to int - OPC-UA hints explicitly name BACnet and EtherNet/IP as sources for key and nonce - `Assembly_Check = [47809, 4841, 44819]` confirms the three-protocol architecture - `Eiri_Key` (8 chars = 64-bit) as the XOR key is a lore-consistent choice --- ## Flag `ESPILON{pr0t0c0l_7_m3rg3_c0mpl3t3}`