Challenge / SecureCoding

Phoenix Pipeline

Phoenix Pipeline is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator

MediumPublished 2025-08-18Sanitized local writeup

Scenario

Phoenix Pipeline attack path

Phoenix Pipeline 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 SecureCoding evidence, validation, and reusable operator lessons.

Phoenix Pipeline sanitized attack graph

Walkthrough flow

01

Artifact review

02

Hypothesis

03

Validated solve path

04

Proof captured

Source coverage

High source coverage

Status: complete. This article is generated from 4 sanitized Markdown sources and keeps raw flags, credentials, keys, cookies, and reusable secrets out of the rendered blog.

96% 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.

  • SecureCoding/Phoenix-Pipeline/writeup.md
  • htb-challenge/SecureCoding/Phoenix-Pipeline/notes.md
  • htb-challenge/SecureCoding/Phoenix-Pipeline/memory-summary.md
  • htb-challenge/SecureCoding/Phoenix-Pipeline/hypothesis-board.md

Technical Walkthrough

Writeup

Challenge

  • Name: Phoenix-Pipeline
  • Category: SecureCoding
  • Difficulty: Medium
  • Mode: remote

Summary

Phoenix Pipeline was a remote SecureCoding challenge exposed through an HTB Editor interface. The editor allowed source review through /api/directory and /api/file, and validation through /api/verify. The source and bundled exploit scripts pointed to two intended defects: session pollution during registration and a race condition in report photo uploads.

Artifact Inventory

The challenge had no downloadable artifact in this workspace. The remote editor exposed the PHP application source and two proof scripts:

  • files/remote-source/exploit/exploit_session_puzzling.py
  • files/remote-source/exploit/exploit_file_upload.py
  • files/remote-source/app/Controllers/AuthController.php
  • files/remote-source/app/Controllers/OperatorController.php

The API and frontend route analysis is captured in analysis/remote/api-wrapper-contexts.txt and analysis/remote/directory.json.

Analysis

exploit_session_puzzling.py registered the username admin and then reused the resulting PHP session to request /challenge/admin. The source explained why this worked: AuthController::register() wrote $_SESSION['username'] and $_SESSION['area'] before checking whether the username already existed. Admin authorization was based on $_SESSION['username'] === 'admin', so the duplicate-user path could still leave a privileged session value.

exploit_file_upload.py uploaded shell.php and raced a request to /challenge/uploads/temp_<md5>_<date>.php. The source showed that OperatorController::submitReport() moved the uploaded file into public app/uploads/ before validating MIME type and extension, giving the exploit a short window to execute a public PHP file before it was removed.

RAG output was recorded as partial advisory support only. The source audit in analysis/source-audit.md and the two bundled exploit scripts were the actual evidence.

Solve

The patch was intentionally narrow:

  • In AuthController.php, session assignment was moved until after duplicate username validation and successful user creation.
  • In OperatorController.php, extension validation now happens before any move, MIME validation reads the PHP-upload temporary file directly, and only validated images are moved to the final public documentation path.

The remote editor did not overwrite existing files through /api/create-file, so the solve uses the same Socket.IO save path as the frontend. The reproducible sync script is solve/apply_securecoding_patch.py. Its remote readback proof is saved in analysis/remote/patch-sync-result.json.

After both files were saved, /api/verify returned the challenge flag. A redacted verification marker is in analysis/remote/api-verify-final-redacted.json.

Flag

Raw flag is stored in loot/flag.txt and intentionally not reproduced here.

Lessons

  • For SecureCoding editor challenges, treat bundled exploit scripts as tests for the intended patch, not as the final action.
  • State mutations before validation can create authorization bugs even when database writes fail.
  • Never publish untrusted uploads to a web-accessible path before all validation is complete.
  • Keep raw flags in loot/ only; use redacted analysis artifacts for walkthrough material.

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: Phoenix-Pipeline
  • Category: SecureCoding
  • Difficulty: Medium
  • Mode: remote
  • Remote instance: none
  • Start time: 2026-06-11T12:30:19Z
  • 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
0remote-only or no provided filesNo local artifacts found under files/

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-11T12:30:19Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-11T12:30:33Zartifact inventoryanalysis/artifact-inventory.json0 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-11T12:31:00Zhypothesis recordedhypothesis-board.mdHTB editor exposes source files and a test/submission API for a secure-coding patch workflowMediumFetch the frontend bundle, enumerate API routes, and identify file/test/submit endpoints before mutation
2026-06-11T12:31:00Zresearch taskanalysis/research/task-20260611T123100732565Z-11d34f75.mdResearch task created for advisory investigationMediumRecord research output
2026-06-11T12:32:27Zcheckpoint recordedanalysis/checkpoint-analysis-20260611T123227864556Z-aacba9dc.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-11T12:35:35ZRAG queryanalysis/rag/rag-query-20260611T123520512045Z-9b8c9866.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-11T12:36:20Zinstrumentation plananalysis/instrumentation-plan.mdPatch and verify the two source-backed secure-coding vulnerabilities without broad remote probing.HighStop if /api/verify reports a different vulnerability, if save fails, or if syntax checks fail locally; do not attempt unrelated exploit chains.
2026-06-11T12:36:20Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-11T12:36:20ZRAG recordanalysis/rag-records.mdRetrieved memory tagged PARTIALMediumValidate or reject with live evidence
2026-06-11T12:37:58Zevaluatoranalysis/evaluator-20260611T123758722876Z-ec1d6621.mdProceedHighApply only AuthController.php and OperatorController.php patches, then call /api/verify.
2026-06-11T12:41:20Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-11T12:42:22Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • Port 31332 served an HTB Editor frontend with /api/directory, /api/file, /api/verify, and Socket.IO save functionality. Port 32223 refused TCP connections during baseline.
  • The recovered source exposed two intended vulnerable flows and matching proof scripts under exploit/.
  • AuthController::register() set $_SESSION['username'] before duplicate username validation. Registering admin could leave an admin-valued session even when the account insert did not occur.
  • OperatorController::submitReport() moved uploaded content into public app/uploads/ as a predictable temp_<md5>_<date>.<ext> before MIME and extension validation, creating a race window for a public PHP upload.
  • Minimal fixes were applied only to app/Controllers/AuthController.php and app/Controllers/OperatorController.php.
  • Remote patch synchronization used the editor Socket.IO save path and verified readback hashes in analysis/remote/patch-sync-result.json.
  • Final /api/verify returned success. The raw flag is stored only in loot/flag.txt.

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: SecureCoding
  • Challenge: Phoenix-Pipeline
  • Difficulty: Medium
  • Source workspace: <local workspace>

Validated Solve Chain

Concepts only. Do not include raw flags, reusable credentials, tokens, cookies, private keys, or live secrets.

1.

Reusable Lessons

-

Dead Ends

-

Tool Quirks

-

Evidence Paths

-

Ingestion Decision

  • Proposed for LightRAG: yes/no
  • 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
1HTB editor exposes source files and a test/submission API for a secure-coding patch workflowPort 31332 serves an HTB Editor Vite frontend with bundled JS assetsFetch the frontend bundle, enumerate API routes, and identify file/test/submit endpoints before mutationMediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

Technical analogy

How to remember this solve

Think of it like inspecting a building plan for missing guardrails. You follow each trust boundary and ask what happens if a user controls more input than the developer expected.

For Phoenix Pipeline, 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.