Portal Noncense
Portal Noncense is a sanitized challenge note from the local HTB archive, organized for quick review by category, difficulty, evidence flow, and reusable operator
Scenario
Portal Noncense attack path
Portal Noncense 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 Blockchain evidence, validation, and reusable operator lessons.
Walkthrough flow
Audit Setup.sol: solved when...
Audit Portal.sol: requestPortal() is impractical due...
Probe live destination addresses and confirm the...
Compute standard CREATE addresses for the player...
Identify that player nonce 130 deploys to the exact...
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.
- Blockchain/Portal-Noncense/writeup.md
- htb-challenge/Blockchain/Portal-Noncense/notes.md
- htb-challenge/Blockchain/Portal-Noncense/memory-summary.md
- htb-challenge/Blockchain/Portal-Noncense/hypothesis-board.md
Technical Walkthrough
Writeup
Challenge
- Name: Portal-Noncense
- Category: Blockchain
- Difficulty: Medium
- Mode: hybrid
Summary
Portal Noncense was solved by using deterministic CREATE addresses. The PortalStation contract hardcoded the orcKingdom destination to an address that had no code yet. Live nonce analysis showed that the player account would deploy a contract exactly at that address when its nonce reached 130.
After advancing the player nonce, a connector contract was deployed at the hardcoded orcKingdom destination. Calling createPortal("orcKingdom") then delegatecalled into the connector, whose connect() function set isPortalActive["orcKingdom"] = true in PortalStation storage.
Artifact Inventory
files/extracted/blockchain_portal_noncense/Portal.sol: vulnerable portal station logic.files/extracted/blockchain_portal_noncense/Setup.sol: solved condition.analysis/live-readonly-probe.json: live destination, code, and solved-state probe.analysis/nonce-address-analysis.txt: proof that player CREATE nonce130maps to the hardcodedorcKingdomaddress.solve/OrcConnector.sol: connector contract deployed at the deterministic destination.solve/solve.py: reusable read-only and execute solver.analysis/solve-run-summary.json: successful transaction receipts and solved-state proof.
Analysis
Setup.isSolved() returns TARGET.isPortalActive("orcKingdom"). The direct requestPortal() route is impractical because isExpertStandby is false and the payable requirement is greater than 1337 ether.
The real route is createPortal(destination), which performs a delegatecall into destinations[destination] and expects connect() to return true. Read-only probing showed that destinations("orcKingdom") pointed to 0xFC31...4007, but that address had no code.
The nonce analysis proved this was not random: with the live player account at nonce 0, the standard CREATE address at nonce 130 matched the hardcoded orcKingdom destination. That meant the player could burn 130 nonces, deploy code at the exact destination address, and then make createPortal("orcKingdom") execute that code through delegatecall.
The connector contract mirrors the storage prefix of PortalStation, so under delegatecall, assigning isPortalActive["orcKingdom"] = true writes to the target contract's mapping slot.
Solve
Read-only validation:
python3 Blockchain/Portal-Noncense/solve/solve.py \
--base-url http://<TARGET>:32209 \
--workspace Blockchain/Portal-Noncense \
--readonlyExecution through the harness:
python3 scripts/challenge_exec.py Blockchain/Portal-Noncense \
--phase <secret redacted> \
--output analysis/solve-run-summary.json \
-- python3 Blockchain/Portal-Noncense/solve/solve.py \
--base-url http://<TARGET>:32209 \
--workspace Blockchain/Portal-Noncense \
--executeThe execute run burned nonce up to 130, deployed OrcConnector at the hardcoded orc destination, called createPortal("orcKingdom"), confirmed Setup.isSolved() == true, and wrote the flag candidate to loot/flag-candidate.txt. The harness captured the final flag into loot/flag.txt.
The challenge backend reset to an initial-looking state after the /flag request. The reliable evidence is the transaction summary showing successful connector deployment at the hardcoded destination, successful createPortal, and setup_solved_after: true.
Flag
Raw flag is stored in loot/flag.txt and intentionally not reproduced here.
Lessons
- Hardcoded addresses can be future deterministic CREATE addresses.
- Nonce manipulation is a valid blockchain exploit primitive when a contract trusts code at a not-yet-deployed address.
delegatecalllets destination code mutate caller storage, so storage layout compatibility matters.- Snapshot important solved-state data before calling
/flag, because challenge infrastructure may reset after successful flag retrieval.
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: Portal-Noncense
- Category: Blockchain
- Difficulty: Medium
- Mode: hybrid
- Remote instance: none
- Start time: 2026-06-12T23:24:49Z
- 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/a12c738a-4cb3-4c5d-8ed1-2bdf94b007bf.zip | 1467 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 3 shown in artifact inventory JSON |
files/extracted/blockchain_portal_noncense/Portal.sol | 1246 | <hash redacted> | ASCII text | |
files/extracted/blockchain_portal_noncense/Setup.sol | 357 | <hash redacted> | Java source, ASCII text |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-12T23:24:49Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-12T23:24:50Z | artifact inventory | analysis/artifact-inventory.json | 3 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-12T23:29:01Z | hypothesis recorded | hypothesis-board.md | Advance the player nonce to 130, deploy OrcConnector at the hardcoded orcKingdom destination, then call createPortal("orcKingdom") so delegatecall sets isPortalActive["orcKingdom"] in PortalStation storage. | High | Run solve/solve.py --readonly, then execute the scripted nonce-burn/deploy/createPortal path once through challenge_exec. |
| 2026-06-12T23:29:01Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-12T23:29:01Z | instrumentation plan | analysis/instrumentation-plan.md | Use deterministic CREATE nonce control plus delegatecall storage mutation to activate the orc portal. | High | Stop if the player nonce is already above 130, the predicted address does not match the destination, the destination already has code, deployment lands at the wrong address, or createPortal fails. |
| 2026-06-12T23:29:15Z | RAG query | analysis/rag/rag-query-20260612T232908438623Z-2cad4b6b.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-12T23:29:41Z | RAG record | analysis/rag-records.md | Retrieved memory tagged GENERIC | Medium | Validate or reject with live evidence |
| 2026-06-12T23:29:41Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-12T23:29:41Z | checkpoint recorded | analysis/checkpoint-analysis-20260612T232941499488Z-9295689a.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-12T23:29:41Z | evaluator | analysis/evaluator-20260612T232941555855Z-1d499751.md | Proceed | High | Run solve.py --execute via challenge_exec; capture flag only after Setup.isSolved is true. |
| 2026-06-12T23:35:25Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-12T23:37:08Z | 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: Blockchain
- Challenge: Portal-Noncense
- 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.
- Audit
Setup.sol: solved whenTARGET.isPortalActive("orcKingdom")is true. - Audit
Portal.sol:requestPortal()is impractical dueisExpertStandbyand high ETH requirement;createPortal()usesdelegatecallintodestinations[_destination].connect(). - Probe live destination addresses and confirm the hardcoded
orcKingdomdestination has no code. - Compute standard CREATE addresses for the player account.
- Identify that player nonce
130deploys to the exact hardcodedorcKingdomdestination. - Compile a connector contract with matching storage layout prefix and
connect()that setsisPortalActive["orcKingdom"] = true. - Burn player nonce up to
130, deploy connector at the destination, callcreatePortal("orcKingdom"), and confirmSetup.isSolved(). - Fetch and harness-capture the flag.
Reusable Lessons
- CREATE address prediction can be an intended exploit path when contracts point to not-yet-deployed addresses.
- Nonce burning through zero-value transactions is enough to reach a target CREATE nonce on Anvil-style HTB chains.
delegatecallexecutes callee code against caller storage; matching storage layout or direct storage-slot writes can mutate target state.- Record solved-state evidence before fetching
/flag, because some challenge backends reset or rotate after flag retrieval.
Dead Ends
requestPortal("orcKingdom")is not viable because the expert standby flag is false and the ETH requirement is intentionally unrealistic.- Treating the destination addresses as existing contracts is misleading; live code at the hardcoded destinations was empty.
- RAG did not contain challenge-specific memory for this solve.
Tool Quirks
- Plain JSON-RPC was sufficient; no Python
web3dependency was needed. npx solc@0.8.13compiled the connector when no nativesolcwas available.- The final flag request reset the live state, so future solvers should snapshot nonce/code/solved state before calling
/flag.
Evidence Paths
analysis/source-audit.mdanalysis/live-readonly-probe.jsonanalysis/nonce-address-analysis.txtanalysis/readonly-nonce-validation.jsonsolve/solve.pysolve/OrcConnector.solanalysis/solve-run-summary.jsonloot/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 | Advance the player nonce to 130, deploy OrcConnector at the hardcoded orcKingdom destination, then call createPortal("orcKingdom") so delegatecall sets isPortalActive["orcKingdom"] in PortalStation storage. | Source audit shows createPortal delegatecalls destinations[_destination]; live nonce analysis proves the player CREATE address at nonce 130 equals destinations("orcKingdom"); destination currently has no code. | Need execute-time proof that nonce burns succeed, deployment lands at the destination, createPortal succeeds, and Setup.isSolved becomes true. | Run solve/solve.py --readonly, then execute the scripted nonce-burn/deploy/createPortal path once through challenge_exec. | High | active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit Condition |
|---|
Technical analogy
How to remember this solve
Think of the smart contract like a transparent bank ledger with strict but imperfect rules. The trick is to make the rules execute in an order the author did not protect against.
For Portal Noncense, 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.