Secure Server
Secure Server is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Secure Server attack path
Secure Server 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 Misc evidence, validation, and reusable operator lessons.
Walkthrough flow
The app logged attacker-controlled User-Agent strings...
our-projects.php used the project parameter directly...
Traversing to the Nginx access log caused logged PHP...
The first restrictive whitelist patch blocked the...
The final patch accepted only safe filename-shaped...
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.
- Misc/Secure-server/writeup.md
- htb-challenge/Misc/Secure-server/notes.md
- htb-challenge/Misc/Secure-server/memory-summary.md
- htb-challenge/Misc/Secure-server/hypothesis-board.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Misc__Secure-server__memory-summary.md.63bc56bed0.md
- HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Misc__Secure-server__notes.md.03c50a69ac.md
Technical Walkthrough
Writeup
Challenge
- Name: Secure-server
- Category: Misc
- Difficulty: Easy
- Mode: hybrid
Summary
Secure server is a secure-coding style Misc challenge. The supplied app is a PHP/Nginx/php-fpm site with a writable SMB share exposing /www/app, plus a checker service.
The bundled exploit.py shows the compromise path:
- put PHP code in the
User-Agent - let Nginx write it to
/var/log/nginx/access.log - abuse
our-projects.php?project=../../../../var/log/nginx/access.log includeexecutes the log as PHP
The fix preserves dynamic project pages but stops path traversal and PHP execution from project content.
Artifact Inventory
Key artifacts:
files/a12c73a0-d6be-4e63-b21e-d77bc05bd905.zip: original challenge archivefiles/extracted/secure_server/challenge/app/our-projects.php: vulnerable pagefiles/extracted/secure_server/config/nginx.conf: Nginx logs user agent into access logfiles/extracted/secure_server/Dockerfile: SMB exposes/www/app; project content lives under/www/projectsfiles/extracted/secure_server/exploit.py: incident proofanalysis/patched-our-projects.php: applied patchanalysis/source-audit.md: vulnerability and patch reasoningsolve/solve.py: reproducible patch/checker solver
Analysis
our-projects.php originally lowercased the project parameter and concatenated it into an include path:
include "../projects/" . $project;That allowed traversal outside /www/projects. Because PHP include executes PHP code, including the Nginx access log became remote code execution once attacker-controlled PHP was logged in the User-Agent.
The first patch used a strict whitelist, but the checker creates additional project files and expects the same URL pattern to serve them. The final patch therefore allows only safe filename-shaped project names:
^[a-z0-9_-]+$It then resolves the target with realpath, verifies it remains under the project directory, and reads it as text with file_get_contents instead of executing it with include.
Regression checks confirmed the exploit path no longer executes and normal project pages still render. The checker confirmed all project pages remain dynamic and the vulnerabilities are removed.
Solve
Run:
python3 Misc/Secure-server/solve/solve.py
python3 scripts/challenge_harness.py capture-flag Misc/Secure-server --from loot/flag-candidate.txtThe solver:
- uploads
analysis/patched-our-projects.phpto the SMBappshare asour-projects.php - performs a regression check against the log-include exploit
- runs the checker service on port
31131 - writes the flag candidate under
loot/ - writes only a redacted checker transcript under
analysis/remote/
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- Blocking traversal with a fixed whitelist can break intended extensibility; safe filename validation plus
realpathpreserves the feature. includeis dangerous for content files because it executes PHP. Use file reads for content rendering.- Log injection becomes RCE only when another bug lets the app include or execute the log.
- Secure-coding checker failures are often about preserving original behavior, not just closing the exploit.
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: Secure-server
- Category: Misc
- Difficulty: Easy
- Mode: hybrid
- Remote instance: <TARGET>:31131,<TARGET>:30272,<TARGET>:31461
- Start time: 2026-06-08T11:17:14Z
- 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/a12c73a0-d6be-4e63-b21e-d77bc05bd905.zip | 343658 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 93 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-08T11:17:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-08T11:17:28Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-08T11:17:49Z | hypothesis recorded | hypothesis-board.md | Source-first incident reconstruction: inspect PHP app, nginx/fpm config, project file access, and bundled exploit.py; identify the vulnerable server/port and use the exploit only after validating the bug from source. | Medium | Read exploit.py and compare it to nginx/fpm/PHP routing before any remote execution. |
| 2026-06-08T11:17:49Z | checkpoint recorded | analysis/checkpoint-triage-20260608T111749794868Z-8af446e6.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-08T11:18:03Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-08T11:20:03Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11:20:40Z | evaluator | analysis/evaluator-20260608T112040801432Z-f4160674.md | Proceed | High | Validate remote web behavior and run checker on <TARGET>:31131. |
| 2026-06-08T11:21:59Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11:23:13Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11:24:13Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-08T11:26:03Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-08T11:26:14Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
-
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: Misc
- Challenge: Secure-server
- 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.
- The app logged attacker-controlled User-Agent strings through Nginx access logging.
our-projects.phpused theprojectparameter directly in a PHPinclude.- Traversing to the Nginx access log caused logged PHP to execute.
- The first restrictive whitelist patch blocked the exploit but failed the checker's extensibility test.
- The final patch accepted only safe filename-shaped project names, resolved the path under
/www/projects, and rendered file content withfile_get_contentsinstead ofinclude. - The checker confirmed dynamic project pages still worked and both vulnerabilities were removed.
Reusable Lessons
- Preserve intended dynamic behavior in secure-coding challenges; do not overfit to only the known default files.
realpathplus a safe basename-style pattern is stronger than raw concatenation and less restrictive than a hardcoded allowlist.- Reading content and including content are different security models in PHP.
- If the provided exploit uses log poisoning, the root bug is usually an include/execute path, not just the log entry itself.
Dead Ends
- Fixed whitelist patch was rejected because new project files also had to be served dynamically.
Tool Quirks
- The host lacked
smbclient, but Impacket'sSMBConnectionwas available and worked for guest SMB upload. - The three live ports mapped to checker, SMB, and web respectively.
- The raw checker output was immediately redacted under
analysis/; raw flag is kept only underloot/.
Evidence Paths
analysis/source-audit.mdanalysis/patched-our-projects.phpanalysis/remote-exploit-regression-3.txtanalysis/checker-output-full-3.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes, after user approval
- 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 | Source-first incident reconstruction: inspect PHP app, nginx/fpm config, project file access, and bundled exploit.py; identify the vulnerable server/port and use the exploit only after validating the bug from source. | Archive contains PHP app/config plus bundled exploit.py and three remote service ports. | Exact vulnerable endpoint/port and flag retrieval path. | Read exploit.py and compare it to nginx/fpm/PHP routing before any remote execution. | 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: Misc
- Challenge: Secure-server
- 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.
- The app logged attacker-controlled User-Agent strings through Nginx access logging.
our-projects.phpused theprojectparameter directly in a PHPinclude.- Traversing to the Nginx access log caused logged PHP to execute.
- The first restrictive whitelist patch blocked the exploit but failed the checker's extensibility test.
- The final patch accepted only safe filename-shaped project names, resolved the path under
/www/projects, and rendered file content withfile_get_contentsinstead ofinclude. - The checker confirmed dynamic project pages still worked and both vulnerabilities were removed.
Reusable Lessons
- Preserve intended dynamic behavior in secure-coding challenges; do not overfit to only the known default files.
realpathplus a safe basename-style pattern is stronger than raw concatenation and less restrictive than a hardcoded allowlist.- Reading content and including content are different security models in PHP.
- If the provided exploit uses log poisoning, the root bug is usually an include/execute path, not just the log entry itself.
Dead Ends
- Fixed whitelist patch was rejected because new project files also had to be served dynamically.
Tool Quirks
- The host lacked
smbclient, but Impacket'sSMBConnectionwas available and worked for guest SMB upload. - The three live ports mapped to checker, SMB, and web respectively.
- The raw checker output was immediately redacted under
analysis/; raw flag is kept only underloot/.
Evidence Paths
analysis/source-audit.mdanalysis/patched-our-projects.phpanalysis/remote-exploit-regression-3.txtanalysis/checker-output-full-3.txtsolve/solve.pyloot/flag.txt
Ingestion Decision
- Proposed for LightRAG: yes, after user approval
- Requires user approval before ingestion: yes
Notes
Notes
Scope
- Challenge: Secure-server
- Category: Misc
- Difficulty: Easy
- Mode: hybrid
- Remote instance: <TARGET>:31131,<TARGET>:30272,<TARGET>:31461
- Start time: 2026-06-08T11:17:14Z
- 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/a12c73a0-d6be-4e63-b21e-d77bc05bd905.zip | 343658 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 93 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-08T11:17:14Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-08T11:17:28Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-08T11:17:49Z | hypothesis recorded | hypothesis-board.md | Source-first incident reconstruction: inspect PHP app, nginx/fpm config, project file access, and bundled exploit.py; identify the vulnerable server/port and use the exploit only after validating the bug from source. | Medium | Read exploit.py and compare it to nginx/fpm/PHP routing before any remote execution. |
| 2026-06-08T11:17:49Z | checkpoint recorded | analysis/checkpoint-triage-20260608T111749794868Z-8af446e6.md | Checkpoint for TRIAGE | High | Use checkpoint to drive next decision |
| 2026-06-08T11:18:03Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-08T11:20:03Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11:20:40Z | evaluator | analysis/evaluator-20260608T112040801432Z-f4160674.md | Proceed | High | Validate remote web behavior and run checker on <TARGET>:31131. |
| 2026-06-08T11:21:59Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11:23:13Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-08T11: <REDACTED> | |||||
| 2026-06-08T11: <REDACTED> | |||||
| 2026-06-08T11:26:14Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
-
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 challenge like a timed puzzle booth. If the task is too fast or repetitive for a person, the intended move is usually to write a small helper that performs the simple action perfectly.
For Secure Server, 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.