Factory
Factory is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Factory attack path
Factory is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Objective
Challenge walkthrough focused on ICS evidence, validation, and reusable operator lessons.
Walkthrough flow
Extract the challenge archive and inspect the PLC...
Identify the PLC Modbus slave address from the setup...
Extract the documented coil map for manual-mode...
Read the ladder logic to determine the intended...
Probe the remote menu and status endpoint to...
Source coverage
High source coverage
Status: complete. This article is generated from 6 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.
High confidence: the page is reconstructed from a primary walkthrough plus multiple supporting notes or evidence sources. Treat the chain as source-backed, while still checking the listed source files for sensitive values.
- ICS/Factory/writeup.md
- htb-challenge/ICS/Factory/notes.md
- htb-challenge/ICS/Factory/memory-summary.md
- htb-challenge/ICS/Factory/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__ICS__Factory__memory-summary.md.efb8cc30c2.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__ICS__Factory__notes.md.7f48b7a817.md
Technical Walkthrough
Writeup
Challenge
- Name: Factory
- Category: ICS
- Difficulty: Easy
- Mode: hybrid
Summary
Factory provides a PLC ladder-logic printout and a diagram for a remote Modbus bridge. The target system starts in automatic mode with the inlet valve open and the outlet valve closed. The artifact documents the PLC slave address and key control coils. By switching the PLC into manual mode, stopping the inlet, and forcing the outlet valve, the system reaches the desired safe state and the remote status endpoint returns the flag.
Artifact Inventory
Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.
files/a12c7392-447e-40ef-9afd-3572c30cd698.zip: original HTB archive.analysis/extracted/PLC_Ladder_Logic.pdf: one-page ladder logic forwater_storage_facility.analysis/extracted/interface_setup.png: remote Modbus bridge diagram with PLC slave address and coil map.analysis/menu-probes.txt: remote menu/status probing.analysis/modbus-read-probes.txt: accepted read-only Modbus frame tests.analysis/solve-transcript.txt: sanitized transcript from the successful run.solve/solve.py: reproducible solver.
Analysis
interface_setup.pngidentifies PLC-1 as Modbus slave address82decimal, or0x52.- The same diagram lists the relevant coils:
- manual_mode_control at decimal 9947
- cutoff_in at decimal 26
- force_start_out at decimal 52
- cutoff at decimal 5
- force_start_in at decimal 1336
- The ladder-logic PDF shows automatic/manual mode selection. When
manual_mode_controlis set,auto_modedrops andmanual_modebecomes active. - In manual mode,
cutoff_indrivesstop_in, which stops the inlet branch. - In manual mode,
force_start_outcan drive the outlet valve branch as long asstop_outis not asserted. - The remote menu exposes status and Modbus command forwarding. Initial status showed automatic mode active, inlet open, outlet closed, and an empty flag placeholder.
- Read-only frames such as
52 01 00 0c 00 01were accepted without CRC, confirming the bridge expects a hex-encoded Modbus RTU ADU without the CRC bytes. - The solve sequence writes the minimum controls with function
0x05single-coil writes:
- clear cutoff
- clear force_start_in
- set manual_mode_control
- set cutoff_in
- set force_start_out
- Final status showed manual mode active, inlet closed, outlet open, and a returned HTB-format flag.
Solve
Run:
python3 ICS/Factory/solve/solve.py --host <TARGET> --port 31344The solver connects to the menu service, sends documented Modbus single-coil writes without CRC, requests final system status, and prints the returned flag for harness capture.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- In ICS challenges, diagrams and ladder-logic labels are often the primary exploit documentation.
- Confirm the transport framing with read-only commands before writing coils.
- For PLC logic puzzles, target the control conditions that the ladder already exposes instead of trying to directly overwrite output coils that may be recalculated every scan.
Source-Backed Dossier
The sections below are merged from companion Markdown notes for the same case. They are rendered after sanitization so the article stays precise without publishing raw flags, credentials, or target-specific secrets.
Notes
Scope
- Challenge: Factory
- Category: ICS
- Difficulty: Easy
- Mode: hybrid
- Remote instance: none
- Start time: 2026-06-10T10:46:11Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7392-447e-40ef-9afd-3572c30cd698.zip | 193358 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-10T10:46:11Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-10T10:46:27Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-10T10:47:47Z | hypothesis recorded | hypothesis-board.md | Use Modbus coil writes against slave 82 to force manual mode, stop inlet, and start outlet | Medium | Connect to <TARGET>:31344, determine accepted Modbus command encoding, then perform minimal read/write probes on documented coil addresses |
| 2026-06-10T10:49:02Z | research record | analysis/research/research-records.md | Research tagged MISSING | Medium | Validate against current evidence |
| 2026-06-10T10:51:18Z | checkpoint recorded | analysis/checkpoint-analysis-20260610T105118425429Z-94a0eafe.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-10T10:52:27Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-10T10:52:53Z | evaluator | analysis/evaluator-20260610T105253208217Z-4d44afd5.md | Proceed | High | Run gate before exploit, then execute solve/controlled coil write sequence. |
| 2026-06-10T10:55:35Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-10T10:57:21Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- Artifact extraction produced
PLC_Ladder_Logic.pdfandinterface_setup.png. - The interface setup shows PLC-1 at Modbus slave address
82decimal (0x52) and lists coil addresses forin_valve,out_valve,start,manual_mode_control,cutoff,cutoff_in,force_start_out, andforce_start_in. - The ladder logic shows manual mode can be selected via
manual_mode_control; in manual modecutoff_indrivesstop_in, andforce_start_outcan driveout_valve. - The remote instance at
<TARGET>:31344is a menu interface withGet status of systemandSend modbus command. - Initial live status shows
auto_mode=1,manual_mode=0,in_valve=1,out_valve=0, and an empty placeholder flag. - Read-only Modbus frames in the form
slave function address countwithout CRC are accepted by the remote bridge and did not alter the system state. - Current intended branch: use function
0x05single-coil writes against documented coils to set manual mode, stop inlet flow, and force the outlet valve. solve/solve.pyexecuted the controlled coil sequence and final status showed manual mode active, inlet closed, outlet open, and an HTB-format flag returned.- The harness captured the raw flag into
loot/flag.txt; non-loot transcripts are sanitized.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: ICS
- Challenge: Factory
- Difficulty: Easy
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Extract the challenge archive and inspect the PLC ladder-logic PDF plus remote-interface diagram.
- Identify the PLC Modbus slave address from the setup diagram.
- Extract the documented coil map for manual-mode selection, inlet stop, and outlet force controls.
- Read the ladder logic to determine the intended safe-state controls instead of guessing raw output writes.
- Probe the remote menu and status endpoint to establish baseline state.
- Send read-only Modbus frames to validate that the bridge accepts hex-encoded Modbus RTU ADU bytes without CRC.
- Use function
0x05single-coil writes to enter manual mode, stop the inlet branch, and force the outlet branch. - Request final status and capture the returned flag through the harness.
Reusable Lessons
- For Modbus bridge challenges, test read-only frames first to learn whether CRC bytes are expected.
- Ladder logic can reveal safer intermediate control coils that survive PLC scan recalculation better than direct output-coil writes.
- Manual-mode interlocks are often the intended way to override broken sensor logic in an ICS puzzle.
- Keep raw status responses with flags out of
analysis/; redact them and store the raw flag only underloot/.
Dead Ends
- RAG/private memory had no matching Factory solve pattern, so the solve relied on current artifact and live-service evidence.
- Direct source of the flag was not in the PDF/image; it required reaching the correct PLC state through the remote service.
Tool Quirks
- The remote service is a menu wrapper around a Modbus command forwarder, not a raw Modbus TCP endpoint.
- The command prompt accepts no-CRC hex frames such as
slave function address value. - The status JSON contains a flag string with braces; parsers should slice from the first
{to the last}rather than using a non-greedy brace regex.
Evidence Paths
analysis/extracted/PLC_Ladder_Logic.pdfanalysis/extracted/interface_setup.pnganalysis/rendered/plc_ladder-1.pnganalysis/menu-probes.txtanalysis/modbus-read-probes.txtanalysis/solve-transcript.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes
- Requires user approval before ingestion: yes
Hypothesis Board
Keep no more than 3 active hypotheses on Easy/Medium and 5 on Hard unless the user explicitly asks for breadth.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | Use Modbus coil writes against slave 82 to force manual mode, stop inlet, and start outlet | interface_setup.png lists PLC slave address 82 and coil addresses for manual_mode_control, cutoff_in, force_start_out; PLC_Ladder_Logic.pdf shows manual controls drive stop_in and out_valve | Connect to <TARGET>:31344, determine accepted Modbus command encoding, then perform minimal read/write probes on documented coil addresses | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Memory Summary
approval_required: true
Sanitized Memory Summary
Metadata
- Platform: HackTheBox Challenges
- Category: ICS
- Challenge: Factory
- Difficulty: Easy
- Source workspace:
<local workspace>
Validated Solve Chain
Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.
- Extract the challenge archive and inspect the PLC ladder-logic PDF plus remote-interface diagram.
- Identify the PLC Modbus slave address from the setup diagram.
- Extract the documented coil map for manual-mode selection, inlet stop, and outlet force controls.
- Read the ladder logic to determine the intended safe-state controls instead of guessing raw output writes.
- Probe the remote menu and status endpoint to establish baseline state.
- Send read-only Modbus frames to validate that the bridge accepts hex-encoded Modbus RTU ADU bytes without CRC.
- Use function
0x05single-coil writes to enter manual mode, stop the inlet branch, and force the outlet branch. - Request final status and capture the returned flag through the harness.
Reusable Lessons
- For Modbus bridge challenges, test read-only frames first to learn whether CRC bytes are expected.
- Ladder logic can reveal safer intermediate control coils that survive PLC scan recalculation better than direct output-coil writes.
- Manual-mode interlocks are often the intended way to override broken sensor logic in an ICS puzzle.
- Keep raw status responses with flags out of
analysis/; redact them and store the raw flag only underloot/.
Dead Ends
- RAG/private memory had no matching Factory solve pattern, so the solve relied on current artifact and live-service evidence.
- Direct source of the flag was not in the PDF/image; it required reaching the correct PLC state through the remote service.
Tool Quirks
- The remote service is a menu wrapper around a Modbus command forwarder, not a raw Modbus TCP endpoint.
- The command prompt accepts no-CRC hex frames such as
slave function address value. - The status JSON contains a flag string with braces; parsers should slice from the first
{to the last}rather than using a non-greedy brace regex.
Evidence Paths
analysis/extracted/PLC_Ladder_Logic.pdfanalysis/extracted/interface_setup.pnganalysis/rendered/plc_ladder-1.pnganalysis/menu-probes.txtanalysis/modbus-read-probes.txtanalysis/solve-transcript.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: Factory
- Category: ICS
- Difficulty: Easy
- Mode: hybrid
- Remote instance: none
- Start time: 2026-06-10T10:46:11Z
- Operator: harness
- State file:
challenge-state.json
Harness Status
- Current phase: see
challenge-state.json - Next allowed actions: see
next-action.json - Raw flags and sensitive material stay in
loot/only. Do not paste them here.
Artifact Inventory
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7392-447e-40ef-9afd-3572c30cd698.zip | 193358 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 2 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-10T10:46:11Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-10T10:46:27Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-10T10:47:47Z | hypothesis recorded | hypothesis-board.md | Use Modbus coil writes against slave 82 to force manual mode, stop inlet, and start outlet | Medium | Connect to <TARGET>:31344, determine accepted Modbus command encoding, then perform minimal read/write probes on documented coil addresses |
| 2026-06-10T10:49:02Z | research record | analysis/research/research-records.md | Research tagged MISSING | Medium | Validate against current evidence |
| 2026-06-10T10:51:18Z | checkpoint recorded | analysis/checkpoint-analysis-20260610T105118425429Z-94a0eafe.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-10T10:52:27Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-10T10:52:53Z | evaluator | analysis/evaluator-20260610T105253208217Z-4d44afd5.md | Proceed | High | Run gate before exploit, then execute solve/controlled coil write sequence. |
| 2026-06-10T10: <REDACTED> | |||||
| 2026-06-10T10:57:21Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- Artifact extraction produced
PLC_Ladder_Logic.pdfandinterface_setup.png. - The interface setup shows PLC-1 at Modbus slave address
82decimal (0x52) and lists coil addresses forin_valve,out_valve,start,manual_mode_control,cutoff,cutoff_in,force_start_out, andforce_start_in. - The ladder logic shows manual mode can be selected via
manual_mode_control; in manual modecutoff_indrivesstop_in, andforce_start_outcan driveout_valve. - The remote instance at
<TARGET>:31344is a menu interface withGet status of systemandSend modbus command. - Initial live status shows
auto_mode=1,manual_mode=0,in_valve=1,out_valve=0, and an empty placeholder flag. - Read-only Modbus frames in the form
slave function address countwithout CRC are accepted by the remote bridge and did not alter the system state. - Current intended branch: use function
0x05single-coil writes against documented coils to set manual mode, stop inlet flow, and force the outlet valve. solve/solve.pyexecuted the controlled coil sequence and final status showed manual mode active, inlet closed, outlet open, and an HTB-format flag returned.- The harness captured the raw flag into
loot/flag.txt; non-loot transcripts are sanitized.
RAG / Advisory Memory
RAG output is advisory only. Record evaluated retrievals with:
scripts/challenge_harness.py rag-record <workspace> --query "..." --tag MATCHED|PARTIAL|MISSING|<secret redacted>|GENERIC --validation "..."Secrets/Flags
Raw flags and sensitive material stay in loot/ only. Use scripts/challenge_harness.py capture-flag to validate and record flag capture without printing the value.
Technical analogy
How to remember this solve
Think of the industrial system like a control-room checklist. You map the inputs, outputs, and assumptions, then find the one control path that accepts a state it should have rejected.
For Factory, keep the mental model simple: identify the trusted assumption, prove it with the smallest safe test, then automate or repeat only the part that directly leads to the flag.