Challenge / Misc

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

EasyPublished 2024-12-21Sanitized local writeup

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.

Secure Server sanitized attack graph

Walkthrough flow

01

The app logged attacker-controlled User-Agent strings...

02

our-projects.php used the project parameter directly...

03

Traversing to the Nginx access log caused logged PHP...

04

The first restrictive whitelist patch blocked the...

05

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.

100% coverage
Evidence verdict

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
  • include executes 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 archive
  • files/extracted/secure_server/challenge/app/our-projects.php: vulnerable page
  • files/extracted/secure_server/config/nginx.conf: Nginx logs user agent into access log
  • files/extracted/secure_server/Dockerfile: SMB exposes /www/app; project content lives under /www/projects
  • files/extracted/secure_server/exploit.py: incident proof
  • analysis/patched-our-projects.php: applied patch
  • analysis/source-audit.md: vulnerability and patch reasoning
  • solve/solve.py: reproducible patch/checker solver

Analysis

our-projects.php originally lowercased the project parameter and concatenated it into an include path:

php
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:

text
^[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:

bash
python3 Misc/Secure-server/solve/solve.py
python3 scripts/challenge_harness.py capture-flag Misc/Secure-server --from loot/flag-candidate.txt

The solver:

  1. uploads analysis/patched-our-projects.php to the SMB app share as our-projects.php
  2. performs a regression check against the log-include exploit
  3. runs the checker service on port 31131
  4. writes the flag candidate under loot/
  5. 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 realpath preserves the feature.
  • include is 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

FileSizeSHA256TypeNotes
files/a12c73a0-d6be-4e63-b21e-d77bc05bd905.zip343658<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 93 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-08T11:17:14Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-08T11:17:28Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-08T11:17:49Zhypothesis recordedhypothesis-board.mdSource-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.MediumRead exploit.py and compare it to nginx/fpm/PHP routing before any remote execution.
2026-06-08T11:17:49Zcheckpoint recordedanalysis/checkpoint-triage-20260608T111749794868Z-8af446e6.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-08T11:18:03Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-08T11:20:03Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11:20:40Zevaluatoranalysis/evaluator-20260608T112040801432Z-f4160674.mdProceedHighValidate remote web behavior and run checker on <TARGET>:31131.
2026-06-08T11:21:59Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11:23:13Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11:24:13Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-08T11:26:03Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-08T11:26:14Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

-

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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.

  1. The app logged attacker-controlled User-Agent strings through Nginx access logging.
  2. our-projects.php used the project parameter directly in a PHP include.
  3. Traversing to the Nginx access log caused logged PHP to execute.
  4. The first restrictive whitelist patch blocked the exploit but failed the checker's extensibility test.
  5. The final patch accepted only safe filename-shaped project names, resolved the path under /www/projects, and rendered file content with file_get_contents instead of include.
  6. 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.
  • realpath plus 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's SMBConnection was 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 under loot/.

Evidence Paths

  • analysis/source-audit.md
  • analysis/patched-our-projects.php
  • analysis/remote-exploit-regression-3.txt
  • analysis/checker-output-full-3.txt
  • solve/solve.py
  • loot/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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Source-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.Mediumactive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit 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.

  1. The app logged attacker-controlled User-Agent strings through Nginx access logging.
  2. our-projects.php used the project parameter directly in a PHP include.
  3. Traversing to the Nginx access log caused logged PHP to execute.
  4. The first restrictive whitelist patch blocked the exploit but failed the checker's extensibility test.
  5. The final patch accepted only safe filename-shaped project names, resolved the path under /www/projects, and rendered file content with file_get_contents instead of include.
  6. 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.
  • realpath plus 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's SMBConnection was 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 under loot/.

Evidence Paths

  • analysis/source-audit.md
  • analysis/patched-our-projects.php
  • analysis/remote-exploit-regression-3.txt
  • analysis/checker-output-full-3.txt
  • solve/solve.py
  • loot/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

FileSizeSHA256TypeNotes
files/a12c73a0-d6be-4e63-b21e-d77bc05bd905.zip343658<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 93 shown in artifact inventory JSON

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-08T11:17:14Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-08T11:17:28Zartifact inventoryanalysis/artifact-inventory.json1 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-08T11:17:49Zhypothesis recordedhypothesis-board.mdSource-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.MediumRead exploit.py and compare it to nginx/fpm/PHP routing before any remote execution.
2026-06-08T11:17:49Zcheckpoint recordedanalysis/checkpoint-triage-20260608T111749794868Z-8af446e6.mdCheckpoint for TRIAGEHighUse checkpoint to drive next decision
2026-06-08T11:18:03Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-08T11:20:03Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11:20:40Zevaluatoranalysis/evaluator-20260608T112040801432Z-f4160674.mdProceedHighValidate remote web behavior and run checker on <TARGET>:31131.
2026-06-08T11:21:59Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11:23:13Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-08T11: <REDACTED>
2026-06-08T11: <REDACTED>
2026-06-08T11:26:14Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

-

RAG / Advisory Memory

RAG output is advisory only. Record evaluated retrievals with:

bash
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.