diff --git a/.devcontainer/virtual/docker-compose.yml b/.devcontainer/virtual/docker-compose.yml
index ef7c412e..687ac1a2 100644
--- a/.devcontainer/virtual/docker-compose.yml
+++ b/.devcontainer/virtual/docker-compose.yml
@@ -105,7 +105,11 @@ services:
context: ../..
dockerfile: software/landing/Dockerfile
restart: always
- network_mode: host
+ ports:
+ - "80:80"
+ networks:
+ virt-cybics:
+ ipv4_address: 172.18.0.20
cap_add:
- NET_ADMIN
- NET_RAW
diff --git a/software/landing/ctf_config.json b/software/landing/ctf_config.json
index c5fb48d8..dab9927b 100644
--- a/software/landing/ctf_config.json
+++ b/software/landing/ctf_config.json
@@ -257,6 +257,32 @@
"training_content": "training/defense_ids/README.md"
}
]
+ },
+ "reverse_engineering": {
+ "name": "๐ Reverse Engineering",
+ "description": "Recover credentials from compiled ICS maintenance utilities",
+ "challenges": [
+ {
+ "id": "plc_auth",
+ "title": "PLC Maintenance Auth Tool",
+ "description": "Reverse engineer a vendor maintenance binary to recover the operator password",
+ "points": 150,
+ "tag": "re",
+ "flag": "CybICS(x0r_1s_n0t_encrypti0n_4_PLC)",
+ "hint": "The password is XOR-encoded with a single constant key. Check byte arrays in the binary.",
+ "training_content": "training/reverse_engineering/challenge1/README.md"
+ },
+ {
+ "id": "hmi_validator",
+ "title": "HMI Safety Override Tool",
+ "description": "Derive a valid 16-char authorization code from the validation algorithm",
+ "points": 200,
+ "tag": "re",
+ "flag": "CybICS(HMI_byp4ss_PLC_s4f3ty_1nt3rl0ck)",
+ "hint": "Each byte is XORed with a rolling 4-byte key then offset by its index. Invert both operations.",
+ "training_content": "training/reverse_engineering/challenge2/README.md"
+ }
+ ]
}
}
}
diff --git a/software/landing/static/challenges/hmi_validator/hmi_validator b/software/landing/static/challenges/hmi_validator/hmi_validator
new file mode 100755
index 00000000..6ace9c74
Binary files /dev/null and b/software/landing/static/challenges/hmi_validator/hmi_validator differ
diff --git a/software/landing/static/challenges/plc_auth/plc_auth b/software/landing/static/challenges/plc_auth/plc_auth
new file mode 100755
index 00000000..4d7821c8
Binary files /dev/null and b/software/landing/static/challenges/plc_auth/plc_auth differ
diff --git a/software/landing/templates/ctf.html b/software/landing/templates/ctf.html
index 55841da2..edeb06f2 100644
--- a/software/landing/templates/ctf.html
+++ b/software/landing/templates/ctf.html
@@ -146,6 +146,7 @@
.tag-evasion { background: rgba(245,158,11,0.15); color: #fbbf24; }
.tag-defense { background: rgba(34,197,94,0.15); color: #4ade80; }
.tag-hardware { background: rgba(236,72,153,0.15); color: #f472b6; }
+ .tag-re { background: rgba(20,184,166,0.15); color: #2dd4bf; }
diff --git a/training/reverse_engineering/README.md b/training/reverse_engineering/README.md
new file mode 100644
index 00000000..511fe9e3
--- /dev/null
+++ b/training/reverse_engineering/README.md
@@ -0,0 +1,116 @@
+# ๐ Reverse Engineering โ CTF Challenges
+
+> **MITRE ATT&CK for ICS:** `Credential Access` `Collection` | [T0812 - Default Credentials](https://attack.mitre.org/techniques/T0812/) | [T0893 - Theft of Operational Information](https://attack.mitre.org/techniques/T0893/) | [T0110 - Brute Force โ Password Cracking](https://attack.mitre.org/techniques/T0110/)
+
+## ๐ Overview
+
+Vendors and system integrators often embed credentials or authorization logic directly in compiled
+binaries โ sometimes with minimal obfuscation โ leaving them exposed to anyone with access to the
+firmware or filesystem.
+
+Two maintenance utilities were recovered from CybICS field devices during an incident response
+exercise. Both binaries are compiled for Linux/x86-64. Your task is to reverse engineer each one,
+recover the embedded credentials or authorization code, and retrieve the hidden diagnostic flag.
+
+These challenges model a realistic ICS threat scenario: hardcoded secrets in maintenance tools
+are a persistent problem in OT environments, where firmware updates are rare and security audits
+even rarer.
+
+---
+
+## ๐ฏ Challenges
+
+### Challenge 1 โ PLC Maintenance Auth Tool (`plc_auth`) ยท 150 pts ยท Easy
+
+A maintenance binary found on the OpenPLC runtime filesystem.
+It prompts for an operator password before revealing a diagnostic code.
+
+**Goal:** Find the password โ get the flag.
+
+โ See [challenge1/README.md](challenge1/README.md)
+
+---
+
+### Challenge 2 โ HMI Safety Override Tool (`hmi_validator`) ยท 200 pts ยท Medium
+
+A safety-interlock override utility recovered from the HMI workstation.
+It validates a 16-character authorization code using a keyed transformation.
+
+**Goal:** Derive a valid authorization code โ get the flag.
+
+โ See [challenge2/README.md](challenge2/README.md)
+
+---
+
+## ๐ ๏ธ Getting the Binaries
+
+Download the challenge binaries from the CTF server:
+
+```bash
+# Challenge 1
+wget http://localhost/static/challenges/plc_auth/plc_auth
+chmod +x plc_auth
+
+# Challenge 2
+wget http://localhost/static/challenges/hmi_validator/hmi_validator
+chmod +x hmi_validator
+```
+
+---
+
+## ๐งฐ Recommended Tools
+
+| Tool | Purpose |
+|-------------|-----------------------------------|
+| `strings` | Quick plaintext extraction |
+| `ltrace` | Trace library calls (strcmp etc.) |
+| `strace` | Trace syscalls |
+| `gdb` | Dynamic analysis / breakpoints |
+| `ghidra` | Full decompilation (recommended) |
+| `radare2` | Disassembly + scripting |
+| `objdump` | Quick disassembly |
+| `python3` | Solve encoding math offline |
+
+---
+
+## ๐ก๏ธ Security Framework References
+
+
+ Click to expand
+
+### MITRE ATT&CK for ICS
+
+| Tactic | Technique | ID | Description |
+|--------|-----------|-----|-------------|
+| Initial Access | Default Credentials | [T0812](https://attack.mitre.org/techniques/T0812/) | Credentials embedded in vendor maintenance tools |
+| Collection | Theft of Operational Information | [T0893](https://attack.mitre.org/techniques/T0893/) | Extracting configuration and diagnostic data from ICS binaries |
+| Credential Access | Brute Force โ Password Cracking | [T0110](https://attack.mitre.org/techniques/T0110/) | Recovering passwords through analysis or offline cracking |
+
+**Why this matters:** Hardcoded credentials in ICS maintenance tools are a well-documented supply-chain risk. An attacker with physical or network access to a field device can extract these tools and recover credentials that grant privileged access to the wider control system โ bypassing all authentication controls on the management interface.
+
+### MITRE D3FEND โ Defensive Countermeasures
+
+| Technique | ID | Description |
+|-----------|-----|-------------|
+| Software Binary Analysis | [D3-SBA](https://d3fend.mitre.org/technique/d3f:SoftwareBinaryAnalysis/) | Analyzing binaries to detect embedded secrets before deployment |
+| Credential Hardening | [D3-CH](https://d3fend.mitre.org/technique/d3f:CredentialHardening/) | Eliminating hardcoded credentials from software and firmware |
+| Authentication Event Monitoring | [D3-AEM](https://d3fend.mitre.org/technique/d3f:AuthenticationEventMonitoring/) | Monitoring for use of shared or vendor maintenance credentials |
+
+### NIST SP 800-82r3 Reference
+
+| Control Family | Controls | Relevance |
+|----------------|----------|-----------|
+| **Identification and Authentication (IA)** | IA-5 | Authenticator management โ prohibiting hardcoded credentials |
+| **Supply Chain Risk Management (SR)** | SR-6, SR-11 | Supplier assessment and component authenticity verification |
+| **Configuration Management (CM)** | CM-7 | Least functionality โ restricting maintenance tool deployment |
+
+**Why NIST 800-82r3 matters here:** NIST 800-82r3 Section 6.2.3 explicitly calls out hardcoded credentials as a critical risk in OT environments. IA-5 (Authenticator Management) requires that credentials not be embedded in software, while SR-6 (Supplier Assessments) recommends auditing vendor-supplied tools for exactly this type of vulnerability before deployment. These challenges demonstrate why binary analysis of vendor tools should be part of every ICS security assessment.
+
+
+
+---
+
+## ๐ Solutions
+
+Full solution writeups are in `challengeN/solution/writeup.md`.
+**Do not open these before attempting the challenge yourself.**
diff --git a/training/reverse_engineering/challenge1/Makefile b/training/reverse_engineering/challenge1/Makefile
new file mode 100644
index 00000000..ae4f6e8e
--- /dev/null
+++ b/training/reverse_engineering/challenge1/Makefile
@@ -0,0 +1,19 @@
+CC := gcc
+CFLAGS := -O0 -g0 -Wall -Wextra -static
+TARGET := plc_auth
+SRC := src/plc_auth.c
+DOCKER_DEST := ../../../software/landing/static/challenges/$(TARGET)/$(TARGET)
+
+.PHONY: all clean deploy
+
+all: $(TARGET)
+
+$(TARGET): $(SRC)
+ $(CC) $(CFLAGS) -o $@ $<
+
+# Copy built binary into the Docker challenge folder
+deploy: $(TARGET)
+ cp $(TARGET) $(DOCKER_DEST)
+
+clean:
+ rm -f $(TARGET)
diff --git a/training/reverse_engineering/challenge1/README.md b/training/reverse_engineering/challenge1/README.md
new file mode 100644
index 00000000..39f5f7e8
--- /dev/null
+++ b/training/reverse_engineering/challenge1/README.md
@@ -0,0 +1,127 @@
+# ๐ PLC Maintenance Auth Tool
+
+> **MITRE ATT&CK for ICS:** `Credential Access` | [T0812 - Default Credentials](https://attack.mitre.org/techniques/T0812/) | [T0110 - Brute Force โ Password Cracking](https://attack.mitre.org/techniques/T0110/)
+
+## ๐ Overview
+
+During incident response on the CybICS gas pressure control system, forensic analysts
+discovered a custom maintenance utility on the OpenPLC runtime filesystem. The binary is believed
+to have been left by the original equipment vendor to allow privileged operator access
+without going through the standard OpenPLC web interface.
+
+The binary prompts for an operator password and โ if correct โ prints a vendor diagnostic
+code containing a flag hidden in the system configuration.
+
+```
+==========================================================
+ CybICS PLC Maintenance Tool v1.2.0
+ Gas Pressure Control System - Operator Access
+ (c) CybICS Industrial Systems
+==========================================================
+ System: HPT/GST Pressure Controller
+ Protocol: Modbus TCP
+ Status: CONNECTED
+----------------------------------------------------------
+
+Enter operator password: _
+```
+
+This models a realistic supply-chain risk: vendors frequently embed hardcoded credentials in
+maintenance tools shipped with field devices, often with only superficial obfuscation, leaving
+them recoverable by anyone with access to the binary.
+
+## ๐ฏ Task
+
+Reverse engineer `plc_auth` to recover the operator password and obtain the flag printed
+after successful authentication.
+
+The flag has the format `CybICS(flag)`.
+
+1. Download the binary from the CTF server and make it executable
+2. Run it to understand what it does
+3. Perform static analysis to recover the password โ start with `strings`, then examine the comparison logic in a disassembler if needed
+4. Use dynamic analysis to observe the comparison at runtime without knowing the password in advance
+5. Enter the recovered password to obtain the flag
+
+```bash
+wget http://localhost/static/challenges/plc_auth/plc_auth
+chmod +x plc_auth
+./plc_auth
+
+# Static recon
+file plc_auth
+strings plc_auth
+
+# Dynamic analysis
+ltrace ./plc_auth
+```
+
+## ๐ก๏ธ Security Framework References
+
+
+ Click to expand
+
+### MITRE ATT&CK for ICS
+
+| Tactic | Technique | ID | Description |
+|--------|-----------|-----|-------------|
+| Initial Access | Default Credentials | [T0812](https://attack.mitre.org/techniques/T0812/) | Vendor-supplied maintenance tools with embedded operator passwords |
+| Credential Access | Brute Force โ Password Cracking | [T0110](https://attack.mitre.org/techniques/T0110/) | Recovering the password by analyzing the binary offline |
+
+**Why this matters:** Hardcoded credentials in ICS maintenance binaries are a persistent supply-chain threat. An attacker with even read-only access to a field device can extract vendor tools, reverse engineer the password, and gain privileged access to the control system โ entirely bypassing the authentication controls visible to operators. The 2021 Oldsmar water treatment incident highlighted how single-factor, vendor-supplied credentials can become the weakest link in an OT environment.
+
+### MITRE D3FEND โ Defensive Countermeasures
+
+| Technique | ID | Description |
+|-----------|-----|-------------|
+| Software Binary Analysis | [D3-SBA](https://d3fend.mitre.org/technique/d3f:SoftwareBinaryAnalysis/) | Auditing vendor binaries for hardcoded secrets before deployment |
+| Credential Hardening | [D3-CH](https://d3fend.mitre.org/technique/d3f:CredentialHardening/) | Requiring vendors to eliminate hardcoded credentials from firmware |
+| Authentication Event Monitoring | [D3-AEM](https://d3fend.mitre.org/technique/d3f:AuthenticationEventMonitoring/) | Alerting on the use of shared maintenance credentials |
+
+### NIST SP 800-82r3 Reference
+
+| Control Family | Controls | Relevance |
+|----------------|----------|-----------|
+| **Identification and Authentication (IA)** | IA-5 | Authenticator management โ prohibiting embedded plaintext or weakly obfuscated credentials |
+| **Supply Chain Risk Management (SR)** | SR-6 | Supplier assessments โ verifying vendor tools do not contain hardcoded secrets |
+| **Configuration Management (CM)** | CM-7 | Least functionality โ restricting which maintenance tools may be deployed on OT systems |
+
+**Why NIST 800-82r3 matters here:** NIST 800-82r3 Section 6.2.3 explicitly calls out hardcoded credentials as a critical vulnerability class in OT environments. IA-5 requires that credentials not be embedded in software, while SR-6 recommends binary analysis of vendor-supplied tools as part of procurement security reviews. This challenge demonstrates exactly the kind of finding such a review would uncover.
+
+
+
+## ๐ก Hints
+
+The password is not stored as plaintext. Look for byte arrays and XOR operations โ a single constant key is XORed against each byte. Try `strings` first, then examine the comparison logic in a disassembler if needed.
+
+## ๐ Solution
+
+The password is XOR-encoded in the binary with the key `0x42`. `strings` won't reveal it directly since no byte of the encoded password falls in the printable ASCII range after XOR.
+
+**Option 1 โ GDB (dynamic)**
+
+Run the binary under GDB and break inside `check_credentials` after the decode loop executes. The decoded password sits in the `pw[]` buffer on the stack:
+
+```bash
+gdb ./plc_auth
+(gdb) break check_credentials
+(gdb) run
+(gdb) next
+(gdb) x/s pw
+```
+
+**Option 2 โ Disassembly + offline solve (static)**
+
+Open the binary in `objdump` or Ghidra, locate the `enc_pw` byte array and the XOR key `0x42` in the comparison logic, then invert the encoding in Python:
+
+```python
+enc_pw = [0x0F, 0x76, 0x73, 0x2C, 0x36, 0x71,
+ 0x2C, 0x76, 0x2C, 0x21, 0x71, 0x63]
+print(''.join(chr(b ^ 0x42) for b in enc_pw))
+```
+
+Enter the recovered password to print the flag.
+
+**Flag:** `CybICS(x0r_1s_n0t_encrypti0n_4_PLC)`
+
+
diff --git a/training/reverse_engineering/challenge1/solution/writeup.md b/training/reverse_engineering/challenge1/solution/writeup.md
new file mode 100644
index 00000000..c13599af
--- /dev/null
+++ b/training/reverse_engineering/challenge1/solution/writeup.md
@@ -0,0 +1,85 @@
+# Solution Writeup โ Challenge 1: PLC Maintenance Auth Tool
+
+> **Password:** `M41nt3n4nc3!`
+> **Flag:** `CybICS(x0r_1s_n0t_encrypti0n_4_PLC)`
+
+---
+
+## Step 1 โ Quick Recon with `strings`
+
+```
+$ strings plc_auth
+...
+Enter operator password:
+[-] Authentication FAILED. Invalid operator code.
+[+] Maintenance mode ACTIVE
+[+] Vendor diagnostic code: %s
+...
+```
+
+No plaintext password visible โ the credential is obfuscated.
+
+---
+
+## Step 2 โ Static Analysis with Ghidra / `objdump`
+
+Disassemble `check_credentials`. You will find:
+
+```c
+static const uint8_t enc_pw[12] = {
+ 0x0F, 0x76, 0x73, 0x2C, 0x36, 0x71,
+ 0x2C, 0x76, 0x2C, 0x21, 0x71, 0x63
+};
+
+// Inside check_credentials():
+for (int i = 0; i < 12; i++) {
+ pw[i] = enc_pw[i] ^ 0x42; // key = 0x42
+}
+return strncmp(input, pw, 13) == 0;
+```
+
+The password is XOR-encoded with the constant key `0x42`.
+
+---
+
+## Step 3 โ Decode the Password
+
+```python
+enc_pw = [0x0F,0x76,0x73,0x2C,0x36,0x71,0x2C,0x76,0x2C,0x21,0x71,0x63]
+pw = ''.join(chr(b ^ 0x42) for b in enc_pw)
+print(pw) # M41nt3n4nc3!
+```
+
+---
+
+## Step 4 โ Confirm with `ltrace`
+
+```
+$ ltrace ./plc_auth <<< "M41nt3n4nc3!"
+strncmp("M41nt3n4nc3!", "M41nt3n4nc3!", 13) = 0
+```
+
+---
+
+## Step 5 โ Decode the Flag
+
+The `print_flag` function decodes `enc_flag` with the same key `0x42`:
+
+```python
+enc_flag = [0x01,0x3B,0x20,0x0B,0x01,0x11,0x6A,0x3A,
+ 0x72,0x30,0x1D,0x73,0x31,0x1D,0x2C,0x72,
+ 0x36,0x1D,0x27,0x2C,0x21,0x30,0x3B,0x32,
+ 0x36,0x2B,0x72,0x2C,0x1D,0x76,0x1D,0x12,
+ 0x0E,0x01,0x42]
+flag = ''.join(chr(b ^ 0x42) for b in enc_flag)
+print(flag) # CybICS(x0r_1s_n0t_encrypti0n_4_PLC)
+```
+
+---
+
+## Key Takeaway
+
+Single-byte XOR with a fixed key is trivially reversible by inspection.
+In a real ICS incident, credentials stored this way in vendor maintenance tools
+represent a critical vulnerability โ the same binary distributed to all
+installations shares the same key.
diff --git a/training/reverse_engineering/challenge1/src/plc_auth.c b/training/reverse_engineering/challenge1/src/plc_auth.c
new file mode 100644
index 00000000..8bfaa553
--- /dev/null
+++ b/training/reverse_engineering/challenge1/src/plc_auth.c
@@ -0,0 +1,113 @@
+/*
+ * CybICS PLC Maintenance Authentication Tool
+ * Gas Pressure Control System - Operator Access Module
+ *
+ * Found on the CybICS PLC during incident response.
+ * This binary appears to be a custom maintenance utility
+ * used by the original vendor to access the PLC.
+ *
+ * CTF Challenge: Reverse engineer this binary to recover
+ * the operator password and retrieve the maintenance code.
+ */
+
+#include
+#include
+#include
+#include
+
+#define PW_LEN 12
+#define FLAG_LEN 35
+
+/* Operator password stored with simple encoding to protect it from
+ * accidental disclosure in plaintext memory dumps.
+ * Encoding key is embedded in the authentication logic. */
+static const uint8_t enc_pw[PW_LEN] = {
+ 0x0F, 0x76, 0x73, 0x2C, 0x36, 0x71,
+ 0x2C, 0x76, 0x2C, 0x21, 0x71, 0x63
+};
+
+/* System diagnostic flag - only shown after successful authentication */
+static const uint8_t enc_flag[FLAG_LEN] = {
+ 0x01, 0x3B, 0x20, 0x0B, 0x01, 0x11, 0x6A, 0x3A,
+ 0x72, 0x30, 0x1D, 0x73, 0x31, 0x1D, 0x2C, 0x72,
+ 0x36, 0x1D, 0x27, 0x2C, 0x21, 0x30, 0x3B, 0x32,
+ 0x36, 0x2B, 0x72, 0x2C, 0x1D, 0x76, 0x1D, 0x12,
+ 0x0E, 0x01, 0x6B
+};
+
+static void banner(void)
+{
+ printf("==========================================================\n");
+ printf(" CybICS PLC Maintenance Tool v1.2.0\n");
+ printf(" Gas Pressure Control System - Operator Access\n");
+ printf(" (c) CybICS Industrial Systems\n");
+ printf("==========================================================\n");
+ printf(" System: HPT/GST Pressure Controller\n");
+ printf(" Protocol: Modbus TCP\n");
+ printf(" Status: CONNECTED\n");
+ printf("----------------------------------------------------------\n\n");
+}
+
+static int check_credentials(const char *input)
+{
+ char pw[PW_LEN + 1];
+ memset(pw, 0, sizeof(pw));
+
+ /* Decode the stored password for comparison */
+ for (int i = 0; i < PW_LEN; i++) {
+ pw[i] = (char)(enc_pw[i] ^ 0x42);
+ }
+
+ return (strncmp(input, pw, PW_LEN + 1) == 0);
+}
+
+static void print_flag(void)
+{
+ char flag[FLAG_LEN + 1];
+ memset(flag, 0, sizeof(flag));
+
+ for (int i = 0; i < FLAG_LEN; i++) {
+ flag[i] = (char)(enc_flag[i] ^ 0x42);
+ }
+
+ printf("[+] Maintenance mode ACTIVE\n");
+ printf("[+] HPT current pressure : 87 bar [NORMAL]\n");
+ printf("[+] GST current pressure : 142 bar [NORMAL]\n");
+ printf("[+] Compressor state : OFF\n");
+ printf("[+] Blowout valve : CLOSED\n");
+ printf("\n");
+ printf("[+] Vendor diagnostic code: %s\n", flag);
+}
+
+int main(void)
+{
+ char input[64];
+ memset(input, 0, sizeof(input));
+
+ banner();
+
+ printf("Enter operator password: ");
+ fflush(stdout);
+
+ if (fgets(input, sizeof(input), stdin) == NULL) {
+ fprintf(stderr, "[-] Read error.\n");
+ return 1;
+ }
+
+ /* Strip newline */
+ size_t len = strlen(input);
+ if (len > 0 && input[len - 1] == '\n') {
+ input[len - 1] = '\0';
+ }
+
+ if (check_credentials(input)) {
+ print_flag();
+ } else {
+ printf("[-] Authentication FAILED. Invalid operator code.\n");
+ printf(" Contact your system administrator for access.\n");
+ printf(" Incident will be logged.\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/training/reverse_engineering/challenge2/Makefile b/training/reverse_engineering/challenge2/Makefile
new file mode 100644
index 00000000..a98e5990
--- /dev/null
+++ b/training/reverse_engineering/challenge2/Makefile
@@ -0,0 +1,19 @@
+CC := gcc
+CFLAGS := -O0 -g0 -Wall -Wextra -static
+TARGET := hmi_validator
+SRC := src/hmi_validator.c
+DOCKER_DEST := ../../../software/landing/static/challenges/$(TARGET)/$(TARGET)
+
+.PHONY: all clean deploy
+
+all: $(TARGET)
+
+$(TARGET): $(SRC)
+ $(CC) $(CFLAGS) -o $@ $<
+
+# Copy built binary into the Docker challenge folder
+deploy: $(TARGET)
+ cp $(TARGET) $(DOCKER_DEST)
+
+clean:
+ rm -f $(TARGET)
diff --git a/training/reverse_engineering/challenge2/README.md b/training/reverse_engineering/challenge2/README.md
new file mode 100644
index 00000000..3974c1c9
--- /dev/null
+++ b/training/reverse_engineering/challenge2/README.md
@@ -0,0 +1,147 @@
+# ๐ก๏ธ HMI Safety Override Tool
+
+> **MITRE ATT&CK for ICS:** `Credential Access` `Impair Process Control` | [T0893 - Theft of Operational Information](https://attack.mitre.org/techniques/T0893/) | [T0110 - Brute Force โ Password Cracking](https://attack.mitre.org/techniques/T0110/) | [T0836 - Modify Parameter](https://attack.mitre.org/techniques/T0836/)
+
+## ๐ Overview
+
+During forensic analysis of the CybICS HMI workstation, investigators found a safety-interlock
+override utility. This tool allows authorized engineers to modify the High Pressure Tank (HPT)
+and Gas Storage Tank (GST) safety thresholds โ configuration changes that could cause
+catastrophic overpressure events if misused.
+
+Access is gated by a 16-character authorization code. The validation algorithm is more complex
+than a simple password comparison: the code is derived from the plant's configuration key using
+a keyed byte transformation, making each installation's code theoretically unique.
+
+```
+############################################################
+# CybICS HMI Safety Override Tool v2.4.1 #
+# High Pressure Tank Interlock Configuration #
+# ** AUTHORIZED PERSONNEL ONLY ** #
+############################################################
+
+ WARNING: This tool modifies safety-critical parameters.
+ Unauthorized use may cause HPT overpressure events.
+ A blowout valve activation releases TOXIC GAS.
+
+------------------------------------------------------------
+ HPT Safe Range : 60 - 90 bar
+ GST Safe Range : 60 - 240 bar
+ Blowout trigger : > 220 bar
+------------------------------------------------------------
+
+Enter 16-character authorization code: _
+```
+
+This models a realistic and dangerous ICS scenario: safety-interlock bypass tools with
+authorization logic embedded in client-side binaries provide only the illusion of access
+control. Anyone able to reverse engineer the validation algorithm can compute a valid code
+without knowing the plant's configuration key.
+
+## ๐ฏ Task
+
+Reverse engineer `hmi_validator` to understand the authorization algorithm, derive a valid
+16-character code, and retrieve the override activation key (flag).
+
+The flag has the format `CybICS(flag)`.
+
+1. Download the binary from the CTF server and make it executable
+2. Run it to understand the interface
+3. Perform static analysis to locate the validation function โ use `strings` first, then a disassembler
+4. Identify the key array and expected-values array in the binary, then reverse the transformation to compute a valid 16-character code
+5. Enter the derived code to obtain the flag
+
+```bash
+wget http://localhost/static/challenges/hmi_validator/hmi_validator
+chmod +x hmi_validator
+./hmi_validator
+
+# Static recon
+file hmi_validator
+strings hmi_validator
+objdump -d hmi_validator | grep -A 40 'validate'
+```
+
+## ๐ก๏ธ Security Framework References
+
+
+ Click to expand
+
+### MITRE ATT&CK for ICS
+
+| Tactic | Technique | ID | Description |
+|--------|-----------|-----|-------------|
+| Credential Access | Brute Force โ Password Cracking | [T0110](https://attack.mitre.org/techniques/T0110/) | Recovering the authorization code by reversing the validation algorithm offline |
+| Collection | Theft of Operational Information | [T0893](https://attack.mitre.org/techniques/T0893/) | Extracting safety threshold configuration from the binary |
+| Impair Process Control | Modify Parameter | [T0836](https://attack.mitre.org/techniques/T0836/) | Using the recovered code to modify HPT/GST safety thresholds |
+
+**Why this matters:** Client-side authorization logic is fundamentally broken as a security boundary. When the validation algorithm and its keys are embedded in a binary that is distributed to field devices, any attacker with access to that binary can compute a valid code. Safety interlock override tools are particularly high-value targets: bypassing them can directly cause physical damage, hazardous material release, or injury. The 2017 TRITON/TRISIS attack against Schneider Electric safety instrumented systems demonstrated exactly how targeting safety systems can amplify the impact of an ICS compromise.
+
+### MITRE D3FEND โ Defensive Countermeasures
+
+| Technique | ID | Description |
+|-----------|-----|-------------|
+| Software Binary Analysis | [D3-SBA](https://d3fend.mitre.org/technique/d3f:SoftwareBinaryAnalysis/) | Auditing safety tool binaries to detect client-side-only validation |
+| Credential Hardening | [D3-CH](https://d3fend.mitre.org/technique/d3f:CredentialHardening/) | Moving authorization validation to a trusted server-side component |
+| Authentication Event Monitoring | [D3-AEM](https://d3fend.mitre.org/technique/d3f:AuthenticationEventMonitoring/) | Logging and alerting on all safety override activations |
+
+### NIST SP 800-82r3 Reference
+
+| Control Family | Controls | Relevance |
+|----------------|----------|-----------|
+| **Identification and Authentication (IA)** | IA-2, IA-5 | Multi-factor authentication and proper authenticator management for safety-critical operations |
+| **System and Communications Protection (SC)** | SC-28 | Protection of information at rest โ preventing extraction of keys from deployed binaries |
+| **Supply Chain Risk Management (SR)** | SR-6 | Supplier assessments โ verifying that safety tools implement server-side authorization |
+
+**Why NIST 800-82r3 matters here:** NIST 800-82r3 Section 6.2.9 addresses the specific challenges of securing safety instrumented systems. IA-2 requires multi-factor authentication for privileged operations โ a standard that client-side binary validation cannot meet by design. SC-28 (Protection of Information at Rest) recommends key storage practices that prevent offline extraction of secrets, and SR-6 requires vendors to demonstrate that safety tools do not expose authorization logic in distributed binaries.
+
+
+
+## ๐ก Hints
+
+There is a 4-byte key array and a 16-byte expected-values array in the binary. Find the validation loop in a disassembler and understand what transformation maps each input byte to its expected value โ then invert that transformation to solve for each byte independently.
+
+## ๐ Solution
+
+The validation is not a simple comparison โ each input byte undergoes a keyed transformation before being checked against a pre-computed expected array. There is no library call to intercept, so the solution requires understanding the algorithm from the disassembly.
+
+**Step 1 โ Find the algorithm**
+
+Disassemble `validate_code` in `objdump` or Ghidra:
+
+```bash
+objdump -d hmi_validator | grep -A 60 ''
+```
+
+The logic in the validation loop is:
+
+```c
+((input[i] ^ validate_key[i % 4]) + i) & 0xFF == expected_code[i]
+```
+
+**Step 2 โ Extract the arrays**
+
+From the disassembly, read out:
+
+```
+validate_key[] = { 0x13, 0x37, 0x42, 0x05 }
+expected_code[] = { 0x46, 0x7A, 0x10, 0x4D, 0x54, 0x81, 0x23, 0x5C,
+ 0x67, 0x7D, 0x27, 0x61, 0x5E, 0x7E, 0x15, 0x33 }
+```
+
+**Step 3 โ Invert the transformation**
+
+XOR is its own inverse; subtraction undoes the addition. Solve for each byte independently:
+
+```python
+key = [0x13, 0x37, 0x42, 0x05]
+expected = [0x46, 0x7A, 0x10, 0x4D, 0x54, 0x81, 0x23, 0x5C,
+ 0x67, 0x7D, 0x27, 0x61, 0x5E, 0x7E, 0x15, 0x33]
+
+code = ''.join(chr(((expected[i] - i) & 0xFF) ^ key[i % 4]) for i in range(16))
+print(code)
+```
+
+Enter the resulting 16-character code to unlock the override key.
+
+**Flag:** `CybICS(HMI_byp4ss_PLC_s4f3ty_1nt3rl0ck)`
\ No newline at end of file
diff --git a/training/reverse_engineering/challenge2/solution/writeup.md b/training/reverse_engineering/challenge2/solution/writeup.md
new file mode 100644
index 00000000..c3b89531
--- /dev/null
+++ b/training/reverse_engineering/challenge2/solution/writeup.md
@@ -0,0 +1,88 @@
+# Solution Writeup โ Challenge 2: HMI Safety Override Tool
+
+> **Authorization code:** `UNLOCK_PLC_SAFE!`
+> **Flag:** `CybICS(HMI_byp4ss_PLC_s4f3ty_1nt3rl0ck)`
+
+---
+
+## Step 1 โ Identify the Validation Logic
+
+In Ghidra or `objdump`, locate `validate_code`. The core loop is:
+
+```c
+static const uint8_t validate_key[4] = { 0x13, 0x37, 0x42, 0x05 };
+static const uint8_t expected_code[16] = {
+ 0x46, 0x7A, 0x10, 0x4D, 0x54, 0x81, 0x23, 0x5C,
+ 0x67, 0x7D, 0x27, 0x61, 0x5E, 0x7E, 0x15, 0x33
+};
+
+for (int i = 0; i < 16; i++) {
+ uint8_t t = ((uint8_t)input[i] ^ validate_key[i % 4]) + (uint8_t)i;
+ if (t != expected_code[i]) return 0;
+}
+```
+
+Each byte of the input is:
+1. XORed with `validate_key[i % 4]`
+2. Added to `i` (mod 256)
+3. Compared to `expected_code[i]`
+
+---
+
+## Step 2 โ Invert the Algorithm
+
+Both operations are independently reversible:
+
+```
+expected[i] == ((input[i] ^ key[i%4]) + i) mod 256
+โ input[i] = ((expected[i] - i) mod 256) ^ key[i%4]
+```
+
+```python
+validate_key = [0x13, 0x37, 0x42, 0x05]
+expected_code = [0x46,0x7A,0x10,0x4D, 0x54,0x81,0x23,0x5C,
+ 0x67,0x7D,0x27,0x61, 0x5E,0x7E,0x15,0x33]
+
+code = ''.join(
+ chr((expected_code[i] - i) & 0xFF ^ validate_key[i % 4])
+ for i in range(16)
+)
+print(code) # UNLOCK_PLC_SAFE!
+```
+
+---
+
+## Step 3 โ Confirm
+
+```
+$ ./hmi_validator <<< "UNLOCK_PLC_SAFE!"
+[+] Authorization ACCEPTED
+[+] Override activation key: CybICS(HMI_byp4ss_PLC_s4f3ty_1nt3rl0ck)
+```
+
+---
+
+## Step 4 โ Decode the Flag Independently
+
+The flag is XOR-encoded with the constant `0x37`:
+
+```python
+enc_flag = [0x74,0x4E,0x55,0x7E,0x74,0x64,0x1F,0x7F,
+ 0x7A,0x7E,0x68,0x55,0x4E,0x47,0x03,0x44,
+ 0x44,0x68,0x67,0x7B,0x74,0x68,0x44,0x03,
+ 0x51,0x04,0x43,0x4E,0x68,0x06,0x59,0x43,
+ 0x04,0x45,0x5B,0x07,0x54,0x5C,0x1E]
+flag = ''.join(chr(b ^ 0x37) for b in enc_flag)
+print(flag) # CybICS(HMI_byp4ss_PLC_s4f3ty_1nt3rl0ck)
+```
+
+---
+
+## Key Takeaway
+
+The rolling-key transformation (`XOR key[i%4] + i`) is marginally harder than
+single-byte XOR but equally invertible given the binary. In real ICS deployments,
+authorization codes for safety-interlock overrides must never be derived from
+static data embedded in a distributed binary. Hardware security modules or
+one-time codes tied to an online authorization service are required for
+safety-critical parameter changes.
diff --git a/training/reverse_engineering/challenge2/src/hmi_validator.c b/training/reverse_engineering/challenge2/src/hmi_validator.c
new file mode 100644
index 00000000..fb33bcf4
--- /dev/null
+++ b/training/reverse_engineering/challenge2/src/hmi_validator.c
@@ -0,0 +1,144 @@
+/*
+ * CybICS HMI Safety Parameter Validator
+ * High Pressure Tank Override Authorization Module
+ *
+ * Found on the HMI workstation during forensic analysis.
+ * This tool allows authorized engineers to override the
+ * HPT/GST safety interlock configuration.
+ *
+ * The tool requires a 16-character authorization code that
+ * is tied to the specific plant installation.
+ *
+ * CTF Challenge: Reverse engineer the authorization algorithm
+ * and derive a valid code to retrieve the override key.
+ */
+
+#include
+#include
+#include
+#include
+
+#define CODE_LEN 16
+#define FLAG_LEN 39
+
+/* Per-byte encoding key applied during code validation.
+ * Validation: encoded[i] == (input[i] ^ key[i % 4]) + i (mod 256) */
+static const uint8_t validate_key[4] = { 0x13, 0x37, 0x42, 0x05 };
+
+/* Pre-computed expected values for the authorization code */
+static const uint8_t expected_code[CODE_LEN] = {
+ 0x46, 0x7A, 0x10, 0x4D,
+ 0x54, 0x81, 0x23, 0x5C,
+ 0x67, 0x7D, 0x27, 0x61,
+ 0x5E, 0x7E, 0x15, 0x33
+};
+
+/* Override activation key - revealed after successful validation */
+static const uint8_t enc_flag[FLAG_LEN] = {
+ 0x74, 0x4E, 0x55, 0x7E, 0x74, 0x64, 0x1F, 0x7F,
+ 0x7A, 0x7E, 0x68, 0x55, 0x4E, 0x47, 0x03, 0x44,
+ 0x44, 0x68, 0x67, 0x7B, 0x74, 0x68, 0x44, 0x03,
+ 0x51, 0x04, 0x43, 0x4E, 0x68, 0x06, 0x59, 0x43,
+ 0x04, 0x45, 0x5B, 0x07, 0x54, 0x5C, 0x1E
+};
+
+static void banner(void)
+{
+ printf("############################################################\n");
+ printf("# CybICS HMI Safety Override Tool v2.4.1 #\n");
+ printf("# High Pressure Tank Interlock Configuration #\n");
+ printf("# ** AUTHORIZED PERSONNEL ONLY ** #\n");
+ printf("############################################################\n");
+ printf("\n");
+ printf(" WARNING: This tool modifies safety-critical parameters.\n");
+ printf(" Unauthorized use may cause HPT overpressure events.\n");
+ printf(" A blowout valve activation releases TOXIC GAS.\n");
+ printf("\n");
+ printf("------------------------------------------------------------\n");
+ printf(" HPT Safe Range : 60 - 90 bar\n");
+ printf(" GST Safe Range : 60 - 240 bar\n");
+ printf(" Blowout trigger : > 220 bar\n");
+ printf("------------------------------------------------------------\n\n");
+}
+
+/*
+ * Validates the 16-character authorization code.
+ *
+ * For each byte i of the input code, the following must hold:
+ * ((input[i] ^ validate_key[i % 4]) + i) & 0xFF == expected_code[i]
+ *
+ * This ties the authorization code to the plant's configuration key,
+ * making each installation's code unique.
+ */
+static int validate_code(const char *code, size_t len)
+{
+ if (len != CODE_LEN) {
+ return 0;
+ }
+
+ for (int i = 0; i < CODE_LEN; i++) {
+ uint8_t transformed = (uint8_t)(((uint8_t)code[i] ^ validate_key[i % 4]) + (uint8_t)i);
+ if (transformed != expected_code[i]) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void activate_override(void)
+{
+ char flag[FLAG_LEN + 1];
+ memset(flag, 0, sizeof(flag));
+
+ for (int i = 0; i < FLAG_LEN; i++) {
+ flag[i] = (char)(enc_flag[i] ^ 0x37);
+ }
+
+ printf("[+] Authorization ACCEPTED\n");
+ printf("[+] Safety interlock configuration unlocked.\n");
+ printf("\n");
+ printf("[+] Current safety parameters:\n");
+ printf(" HPT low threshold : 60 bar\n");
+ printf(" HPT high threshold : 90 bar\n");
+ printf(" GST low threshold : 60 bar\n");
+ printf(" GST high threshold : 240 bar\n");
+ printf(" Blowout valve open : 220 bar\n");
+ printf("\n");
+ printf("[+] Override activation key: %s\n", flag);
+ printf("\n");
+ printf("[!] WARNING: Modifying these thresholds can cause\n");
+ printf(" catastrophic overpressure events!\n");
+}
+
+int main(void)
+{
+ char input[64];
+ memset(input, 0, sizeof(input));
+
+ banner();
+
+ printf("Enter 16-character authorization code: ");
+ fflush(stdout);
+
+ if (fgets(input, sizeof(input), stdin) == NULL) {
+ fprintf(stderr, "[-] Read error.\n");
+ return 1;
+ }
+
+ /* Strip newline */
+ size_t len = strlen(input);
+ if (len > 0 && input[len - 1] == '\n') {
+ input[--len] = '\0';
+ }
+
+ if (validate_code(input, len)) {
+ activate_override();
+ } else {
+ printf("[-] INVALID authorization code.\n");
+ printf(" Code must be exactly 16 characters.\n");
+ printf(" Unauthorized override attempt logged.\n");
+ return 1;
+ }
+
+ return 0;
+}