| .. | ||
| README.md | ||
Cyberia Grid
| Field | Value |
|---|---|
| Category | OT |
| Difficulty | Medium-Hard |
| Points | 500 |
| Author | Eun0us |
| CTF | Espilon 2026 |
Description
Cyberia is not just a nightclub. An industrial PLC controller manages the building power infrastructure over EtherNet/IP.
The previous owner — rumored to be connected to the Knights of the Eastern Calculus — added extra tags that do not correspond to any physical equipment.
Read the tags. Find the hidden data. The Psyche Processor awaits activation.
- EtherNet/IP:
tcp/<host>:44818
Format: ESPILON{flag}
TL;DR
Read all EtherNet/IP tags including hidden ones (KIDS_Subject, Knights_Cipher,
Psyche_Processor). Derive the 4-value activation sequence from infrastructure tag values
(BPM, sound level, lighting sum, hacker constant 0x1337). Write to Psyche_Processor[0-3].
After one scan cycle, read Decoded_Output for the flag.
Tools
| Tool | Purpose |
|---|---|
Python 3 + cpppo |
EtherNet/IP client (read/write tags) |
| Knowledge of EtherNet/IP | Understanding CIP tag addressing |
Solution
Step 1 — Connect and enumerate tags
Using the cpppo EtherNet/IP client:
from cpppo.server.enip.get_attribute import proxy_simple as device
with device(host="<HOST>", port=44818) as via:
# Read infrastructure tags
zone_main = via.read("Zone_Main_Power")
zone_vip = via.read("Zone_VIP_Power")
zone_bsmt = via.read("Zone_Basement_Power") # = 0 (OFF — suspicious)
sound_db = via.read("Sound_System_dB") # = 95
bpm = via.read("BPM") # = 140
lighting = [via.read(f"Lighting_Main[{i}]") for i in range(8)]
# = [255, 200, 180, 150, 100, 80, 60, 40]
# Read hidden tags
kids = [via.read(f"KIDS_Subject[{i}]") for i in range(16)]
cipher = [via.read(f"Knights_Cipher[{i}]") for i in range(4)]
# cipher = [0x4B, 0x6E, 0x69, 0] -- 4th byte missing
psyche_st = via.read("Psyche_Status") # = "DORMANT"
📸
[screenshot: cpppo client output listing all tag values including hidden ones]
Observation: Zone_Basement_Power = 0 — the basement is OFF. This is the first hint
that something is hidden underground.
Step 2 — Analyze the hidden tags
KIDS_Subject[0-15]: 16 DINT values containing XOR-encoded flag dataKnights_Cipher[0-3]: partial XOR key[0x4B, 0x6E, 0x69, 0]="Kni?"— 4th byte missingPsyche_Processor[0-3]: all zeros — needs activationPsyche_Status = "DORMANT"
Step 3 — Derive the activation sequence
Each Psyche_Processor value is derived from existing infrastructure tags:
| Index | Formula | Calculation | Value |
|---|---|---|---|
| 0 | Zone_Basement_Power XOR BPM |
0 ^ 140 |
140 |
| 1 | Sound_System_dB |
95 |
95 |
| 2 | sum(Lighting_Main) % 256 |
1065 % 256 |
17 |
| 3 | 0x1337 (hacker constant) |
4919 |
4919 |
📸
[screenshot: Python calculation showing the four derived activation values]
Step 4 — Activate the Psyche Processor
Write the derived values:
with device(host="<HOST>", port=44818) as via:
for i, val in enumerate([140, 95, 17, 4919]):
via.write(via.parameter_substitution(f"Psyche_Processor[{i}]"), val)
Step 5 — Wait one scan cycle and read the flag
After ~500ms (one PLC scan cycle):
import time
time.sleep(0.6)
with device(host="<HOST>", port=44818) as via:
flag = via.read("Decoded_Output")
print(flag)
# Also: Knights_Cipher[3] is now populated: 0x67 = 'g' → key = "Knig"
📸
[screenshot: Decoded_Output tag returning the flag after Psyche Processor activation]
Key concepts
Zone_Basement_Power = 0is the first hint that something is hidden underground- The
0x1337constant is a recurring hacker reference across the OT challenges - The PLC scan cycle polling pattern mirrors real industrial controller behavior
- EtherNet/IP has no built-in authentication — anyone can read/write tags on the network
Flag
ESPILON{cyb3r14_ps7ch3_pr0c3ss0r}