Portaloo
Portaloo is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Portaloo attack path
Portaloo 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 Pwn evidence, validation, and reusable operator lessons.
Walkthrough flow
Binary triage
Memory primitive
Control-flow hijack
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.
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.
- Pwn/Portaloo/writeup.md
- htb-challenge/Pwn/Portaloo/notes.md
- htb-challenge/Pwn/Portaloo/memory-summary.md
- htb-challenge/Pwn/Portaloo/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Portaloo
- Category: Pwn
- Difficulty: Medium
- Mode: hybrid
Summary
Portaloo is a hybrid pwn challenge built around a portal-management menu. The
binary is a PIE ELF with stack canaries, NX stack, full RELRO, and a provided
glibc, but its own create_portal path marks the heap page as executable.
The working exploit chain is:
- Create a portal so the heap page is marked RWX.
- Free that portal without clearing the global slot.
- Use the dangling slot and
peek_into_the_voidto leak tcache heap metadata,
recovering the heap page.
- Reallocate the same chunk through the second portal slot and write a compact
first-stage heap shellcode.
- Use the first
step_into_the_portalread to leak the stack canary suffix. - Use the second
step_into_the_portalread to restore the canary and return
into the executable heap chunk.
- Stage
/bin/shshellcode and read the flag.
Artifact Inventory
Reference analysis/artifact-inventory.json and summarize the relevant files or remote surface.
files/a12c735b-04fc-46d4-9823-1e7a5b565d55.zip: original HTB archive.files/extracted/portaloo: challenge ELF, not stripped.files/extracted/glibc/: supplied loader and libc.analysis/objdump-portaloo.txt: function-level disassembly used for source
audit.
analysis/leak-probe.txt: bounded live validation of heap and canary leaks.solve/probe_leaks.py: leak-validation helper.solve/solve.py: final reproducible exploit.
Analysis
Static disassembly showed five menu actions: create, destroy, upgrade, peek, and
step. The important properties are all visible in analysis/objdump-portaloo.txt:
create_portalallocates0x20bytes and callsmprotecton the aligned
heap page with permission 7, making that heap page executable.
destroy_portalcallsfree(slots[index])but leaves the global slot value
unchanged.
peek_into_the_voidprints up to0x15bytes from each non-null slot, so it
can read metadata from a freed tcache chunk.
upgrade_portalwrites0x15bytes into an existing slot, including a chunk
reallocated from the freed portal.
step_into_the_portaluses a0x48stack buffer, reads0x50bytes, prints
it as a string, clears the buffer, and then reads 0x68 bytes before the
canary check.
The first step_into_the_portal read can be made to write one byte past the
buffer into the canary's leading null byte. The following %s print then leaks
the remaining seven canary bytes. The final read restores the full canary and
overwrites the saved return address.
The freed portal leak gives the heap page via glibc safe-linking metadata. The
first portal allocation was reused at page offset 0x2a0, which let the final
payload return directly into the first-stage shellcode stored in the executable
heap chunk.
Solve
Run:
cd <local workspace>
python3 scripts/challenge_exec.py Pwn/Portaloo --phase exploit --output analysis/flag-candidate.txt -- python3 Pwn/Portaloo/solve/solve.py --host <TARGET> --port 30555 --output Pwn/Portaloo/analysis/flag-candidate.txt --debug-output Pwn/Portaloo/analysis/exploit-debug.txt
python3 scripts/challenge_harness.py capture-flag Pwn/Portaloo --from analysis/flag-candidate.txtThe solver derives the per-connection heap page and canary dynamically, writes
the final candidate to analysis/flag-candidate.txt, and stores the raw flag
only through the harness capture step.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- The challenge intentionally pairs strict normal mitigations with one unsafe
design choice: making the heap executable.
- The dangling slot is useful twice: first as a tcache metadata leak, then as a
way to reclaim the same executable chunk.
- For
%s-based canary leaks, overwriting only the canary's leading null byte
is enough; the final payload must restore the full canary.
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: Portaloo
- Category: Pwn
- Difficulty: Medium
- Mode: hybrid
- Remote instance: none
- Start time: 2026-06-11T22:39:51Z
- 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/a12c735b-04fc-46d4-9823-1e7a5b565d55.zip | 1073400 | <hash redacted> | Zip archive data, at least v2.0 to extract, compression method=deflate | zip entries: 3 shown in artifact inventory JSON |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-11T22:39:51Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-11T22:39:51Z | artifact inventory | analysis/artifact-inventory.json | 1 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-11T22:42:37Z | hypothesis recorded | hypothesis-board.md | Use portal UAF/tcache metadata plus step_into_the_portal overflow: create a portal to mprotect the heap RWX, free/peek a portal to recover heap page information, use the first step_into_the_portal read to leak canary/PIE, then use the second read to return into controlled heap shellcode. | Medium | Run a bounded remote transcript to confirm freed portal data leaks heap-like bytes and that a 0x49-byte first step read leaks the canary suffix without immediately terminating. |
| 2026-06-11T22:42:37Z | hypothesis recorded | hypothesis-board.md | Use step_into_the_portal canary/PIE leak for a two-stage ROP leak, then ret2libc system('/bin/sh') with the provided libc. | Medium | If heap-shellcode path fails, check whether leaked return address and available libc/main-binary gadgets can call puts/read or use a stack pivot. |
| 2026-06-11T22:42:56Z | checkpoint recorded | analysis/checkpoint-analysis-20260611T224256549561Z-bbf433f1.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-11T22:44:18Z | research record | analysis/research/research-records.md | Research tagged PARTIAL | Medium | Validate against current evidence |
| 2026-06-11T22:44:18Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-11T22:44:18Z | instrumentation plan | analysis/instrumentation-plan.md | Validate leak primitives and build a reproducible exploit for Portaloo. | High | Stop after two remote exploit attempts without a new leak or control-flow fact; record failure and rerank hypotheses. |
| 2026-06-11T22:44:28Z | evaluator | analysis/evaluator-20260611T224428070455Z-c2e95976.md | Validate first | High | Build leak validation script and run it through challenge_exec. |
| 2026-06-11T22:44:51Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-11T22:44:59Z | evaluator | analysis/evaluator-20260611T224459698582Z-67a1ff28.md | Proceed | High | Run solve/probe_leaks.py through challenge_exec and inspect transcript. |
| 2026-06-11T22:45:18Z | leak validation | analysis/leak-probe.txt | Remote service leaked freed-portal heap metadata and the stack canary suffix as predicted | High | Build final heap-shellcode exploit |
| 2026-06-11T22:47:10Z | exploit | analysis/flag-candidate.txt | Final solver returned into executable heap shellcode and captured an HTB-format flag candidate | High | Capture flag through harness |
| 2026-06-11T22:49:29Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-11T22:50:40Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
create_portalmarks the aligned heap page RWX after the first allocation.destroy_portalfrees portal chunks but leavesslots[index]dangling.peek_into_the_voidcan print freed tcache metadata, revealing the heap page.step_into_the_portalleaks the canary suffix with a one-byte canary overwrite during the first read.- The final exploit restores the canary and returns to first-stage shellcode in the reused portal chunk at heap page offset
0x2a0.
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: Pwn
- Challenge: Portaloo
- 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.
| Rank | Path | Evidence | Missing Proof | Cheapest Validation | Confidence | Status |
|---|---|---|---|---|---|---|
| 1 | Use portal UAF/tcache metadata plus step_into_the_portal overflow: create a portal to mprotect the heap RWX, free/peek a portal to recover heap page information, use the first step_into_the_portal read to leak canary/PIE, then use the second read to return into controlled heap shellcode. | Static disassembly shows create_portal malloc(0x20) and mprotect(aligned heap page, page_size, 7), destroy_portal frees without clearing slots, peek prints data from non-null slot pointers, and step_into_the_portal reads 0x50 then 0x68 into a 0x48-byte stack buffer with stack canary. | Run a bounded remote transcript to confirm freed portal data leaks heap-like bytes and that a 0x49-byte first step read leaks the canary suffix without immediately terminating. | Medium | Active | |
| 2 | Use step_into_the_portal canary/PIE leak for a two-stage ROP leak, then ret2libc system('/bin/sh') with the provided libc. | Provided libc is present and binary has PLT/GOT entries, but exported-gadget scan shows very limited simple pop gadgets in the main binary, making direct ROP less likely than heap shellcode. | If heap-shellcode path fails, check whether leaked return address and available libc/main-binary gadgets can call puts/read or use a stack pivot. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Technical analogy
How to remember this solve
Think of the challenge as a small system with one rule that matters more than the rest. The solve is finding that rule, validating it, and using it carefully enough to reach the final proof.
For Portaloo, 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.