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; +}