Challenge / Blockchain

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

MediumPublished 2024-02-10Sanitized local writeup

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.

Portal Noncense sanitized attack graph

Walkthrough flow

01

Audit Setup.sol: solved when...

02

Audit Portal.sol: requestPortal() is impractical due...

03

Probe live destination addresses and confirm the...

04

Compute standard CREATE addresses for the player...

05

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.

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.

  • 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 nonce 130 maps to the hardcoded orcKingdom address.
  • 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:

bash
python3 Blockchain/Portal-Noncense/solve/solve.py \
  --base-url http://<TARGET>:32209 \
  --workspace Blockchain/Portal-Noncense \
  --readonly

Execution through the harness:

bash
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 \
    --execute

The 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.
  • delegatecall lets 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

FileSizeSHA256TypeNotes
files/a12c738a-4cb3-4c5d-8ed1-2bdf94b007bf.zip1467<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 3 shown in artifact inventory JSON
files/extracted/blockchain_portal_noncense/Portal.sol1246<hash redacted>ASCII text
files/extracted/blockchain_portal_noncense/Setup.sol357<hash redacted>Java source, ASCII text

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-12T23:24:49Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-12T23:24:50Zartifact inventoryanalysis/artifact-inventory.json3 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-12T23:29:01Zhypothesis recordedhypothesis-board.mdAdvance the player nonce to 130, deploy OrcConnector at the hardcoded orcKingdom destination, then call createPortal("orcKingdom") so delegatecall sets isPortalActive["orcKingdom"] in PortalStation storage.HighRun solve/solve.py --readonly, then execute the scripted nonce-burn/deploy/createPortal path once through challenge_exec.
2026-06-12T23:29:01Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-12T23:29:01Zinstrumentation plananalysis/instrumentation-plan.mdUse deterministic CREATE nonce control plus delegatecall storage mutation to activate the orc portal.HighStop 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:15ZRAG queryanalysis/rag/rag-query-20260612T232908438623Z-2cad4b6b.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-12T23:29:41ZRAG recordanalysis/rag-records.mdRetrieved memory tagged GENERICMediumValidate or reject with live evidence
2026-06-12T23:29:41Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-12T23:29:41Zcheckpoint recordedanalysis/checkpoint-analysis-20260612T232941499488Z-9295689a.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-12T23:29:41Zevaluatoranalysis/evaluator-20260612T232941555855Z-1d499751.mdProceedHighRun solve.py --execute via challenge_exec; capture flag only after Setup.isSolved is true.
2026-06-12T23:35:25Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-12T23:37:08Zcompletion 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: 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.

  1. Audit Setup.sol: solved when TARGET.isPortalActive("orcKingdom") is true.
  2. Audit Portal.sol: requestPortal() is impractical due isExpertStandby and high ETH requirement; createPortal() uses delegatecall into destinations[_destination].connect().
  3. Probe live destination addresses and confirm the hardcoded orcKingdom destination has no code.
  4. Compute standard CREATE addresses for the player account.
  5. Identify that player nonce 130 deploys to the exact hardcoded orcKingdom destination.
  6. Compile a connector contract with matching storage layout prefix and connect() that sets isPortalActive["orcKingdom"] = true.
  7. Burn player nonce up to 130, deploy connector at the destination, call createPortal("orcKingdom"), and confirm Setup.isSolved().
  8. 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.
  • delegatecall executes 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 web3 dependency was needed.
  • npx solc@0.8.13 compiled the connector when no native solc was 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.md
  • analysis/live-readonly-probe.json
  • analysis/nonce-address-analysis.txt
  • analysis/readonly-nonce-validation.json
  • solve/solve.py
  • solve/OrcConnector.sol
  • analysis/solve-run-summary.json
  • loot/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.

RankPathEvidenceMissing ProofCheapest ValidationConfidenceStatus
1Advance 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.Highactive

Closed Branches

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