Challenge / Blockchain

Token To Wonderland

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

EasyPublished 2024-02-15Sanitized local writeup

Scenario

Token To Wonderland attack path

session material To Wonderland 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.

Token To Wonderland sanitized attack graph

Walkthrough flow

01

Audit Setup.sol, Shop.sol, and SilverCoin.sol.

02

Identify the win condition: Setup.isSolved(player)...

03

Confirm the player starts with too few SilverCoin...

04

Identify the Solidity 0.7 unchecked arithmetic bug in...

05

Use JSON-RPC to derive the live session material...

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.

  • Blockchain/Token-To-Wonderland/writeup.md
  • htb-challenge/Blockchain/Token-To-Wonderland/notes.md
  • htb-challenge/Blockchain/Token-To-Wonderland/memory-summary.md
  • htb-challenge/Blockchain/Token-To-Wonderland/hypothesis-board.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Blockchain__Token-To-Wonderland__memory-summary.md.74b67e753d.md
  • HTB/_knowledge/exports/ctf-lightrag-latest-203412/documents/challenge__Blockchain__Token-To-Wonderland__notes.md.f90dff766d.md

Technical Walkthrough

Writeup

Challenge

  • Name: Token-To-Wonderland
  • Category: Blockchain
  • Difficulty: Easy
  • Mode: hybrid

Summary

The challenge deploys a shop where the win condition is owning the Golden Key item. The player starts with only 100 SilverCoin units, while the Golden Key costs 25,000,000. The provided ERC20-like token is compiled for Solidity 0.7 and has an unchecked subtraction bug in _transfer, allowing the player balance to underflow by transferring more tokens than they own to a different address. After inflating the balance, the player can approve the shop and buy the Golden Key.

Artifact Inventory

  • Original archive: files/a12c7357-d856-4e10-9a28-e90ea69a5e6c.zip
  • Extracted contracts:

- files/extracted/blockchain_token_to_wonderland/Setup.sol

- files/extracted/blockchain_token_to_wonderland/Shop.sol

- files/extracted/blockchain_token_to_wonderland/SilverCoin.sol

  • Remote surface:

- /connection_info for player and deployed contract metadata

- /rpc for JSON-RPC

- /flag for completion after isSolved

Analysis

Setup.isSolved(player) calls TARGET.viewItem(2) and checks that item 2 is owned by the player. In Shop.sol, item 2 is the Golden Key with price 25_000_000, and buyItem requires silverCoin.transferFrom(msg.sender, address(this), _item.price) to succeed.

SilverCoin.sol contains the bug:

solidity
uint256 fromBalance = _balances[from];
require(fromBalance - amount >= 0, "ERC20: transfer amount exceeds balance");
_balances[from] = fromBalance - amount;
_balances[to] += amount;

Because the contract uses Solidity 0.7, unsigned subtraction does not revert on underflow. The >= 0 check is always true for uint256. Sending more than the current balance to a different address makes the sender balance wrap to a huge value.

The source audit is recorded in analysis/source-audit.md. Read-only RPC validation in analysis/remote-readonly-state.json confirmed:

  • player balance was 100
  • Golden Key price was 25,000,000
  • item 2 owner was initially the shop
  • the Shop token address was recoverable from storage slot 1

RAG was queried but did not return useful challenge-specific guidance; this is recorded in analysis/rag-records.md.

Solve

The reproducible solver is solve/solve.py.

It performs:

  1. Fetch live /connection_info and store raw connection material under loot/, with redacted analysis copies.
  2. Read the SilverCoin address from Shop storage slot 1.
  3. Read Golden Key item 2 owner/price and the player balance.
  4. Call SilverCoin.transfer(shop, balance + 1) from the player to trigger underflow.
  5. Call SilverCoin.approve(shop, 25000000).
  6. Call Shop.buyItem(2).
  7. Confirm item 2 owner changed to the player.
  8. Request /flag and store the raw response in loot/flag-candidate.txt.

The successful live run is summarized in analysis/solve-run-summary.json. It changed item 2 owner to the player and /flag returned HTTP 200.

Flag

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

Lessons

  • Solidity private storage and contract state are still inspectable from JSON-RPC.
  • Solidity versions before 0.8 do not have checked arithmetic by default.
  • A self-transfer would not preserve the underflow effect here because the later addition restores the balance modulo 2^256; the transfer must go to a different address.
  • Keep /connection_info private keys and raw flags in loot/, with redacted copies in analysis/.

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: Token-To-Wonderland
  • Category: Blockchain
  • Difficulty: Easy
  • Mode: hybrid
  • Remote instance: <TARGET>:31858
  • Start time: 2026-06-09T10:52:59Z
  • 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/a12c7357-d856-4e10-9a28-e90ea69a5e6c.zip2573<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 4 shown in artifact inventory JSON
files/extracted/blockchain_token_to_wonderland/Setup.sol593<hash redacted>Java source, ASCII text
files/extracted/blockchain_token_to_wonderland/Shop.sol1063<hash redacted>Java source, ASCII text
files/extracted/blockchain_token_to_wonderland/SilverCoin.sol4578<hash redacted>ASCII text

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-09T10:52:59Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-09T10:53:15Zartifact inventoryanalysis/artifact-inventory.json4 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-09T10:53:15Zhypothesis recordedhypothesis-board.mdAudit provided Solidity contracts and remote JSON-RPC instance, identify token/key purchase or authorization flaw, then satisfy Setup.isSolved and capture /flag.MediumExtract contracts, identify Setup.isSolved condition, fetch /connection_info, and inspect target storage/function selectors before any transaction.
2026-06-09T10:54:23Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-09T10:54:43Zcheckpoint recordedanalysis/checkpoint-analysis-20260609T105443154310Z-bb5db5a0.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-09T10:55:02ZRAG queryanalysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-09T10:56:12ZRAG recordanalysis/rag-records.mdRetrieved memory tagged MISSINGMediumValidate or reject with live evidence
2026-06-09T10:55:55Zread-only RPC validationanalysis/remote-readonly-state.jsonShop storage slot 1 resolves to SilverCoin; Golden Key item 2 costs 25,000,000; player balance is 100 and allowance is 0HighRecord local fallback/evaluator, run exploit gate, then perform transfer-underflow/approve/buy chain
2026-06-09T10:56:59Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-09T10:58:11Zevaluatoranalysis/evaluator-20260609T105811063436Z-cc3f1a96.mdProceedHighRun exploit gate and execute solve/solve.py through challenge_exec.
2026-06-09T10:58:37Zflag captureloot/flag.txtHTB-format flag captured; raw value kept in loot onlyHighWrite solution and run completion gate
2026-06-09T10:59:54Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The remote exposes /connection_info, /rpc, and /flag.
  • Raw connection material from /connection_info is stored under loot/; redacted copies are in analysis/.
  • Setup.isSolved(player) requires Golden Key item 2 owner to be the player.
  • Shop.buyItem(2) requires transferFrom(player, shop, 25000000) to succeed.
  • SilverCoin._transfer uses unchecked Solidity 0.7 arithmetic: require(fromBalance - amount >= 0) cannot prevent unsigned underflow.
  • The live player starts with only 100 SilverCoin units, so normal purchase is impossible.
  • A transfer of more than the player balance to a different address should underflow the player balance high enough to approve and buy item 2.

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: Token-To-Wonderland
  • 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. Audit Setup.sol, Shop.sol, and SilverCoin.sol.
  2. Identify the win condition: Setup.isSolved(player) requires Shop item 2, the Golden Key, to be owned by the player.
  3. Confirm the player starts with too few SilverCoin units for a normal purchase.
  4. Identify the Solidity 0.7 unchecked arithmetic bug in SilverCoin._transfer: require(fromBalance - amount >= 0) does not prevent unsigned underflow.
  5. Use JSON-RPC to derive the live token address from Shop storage slot 1 and confirm the live balance/item state.
  6. Transfer more than the player balance to a different address, causing player balance underflow.
  7. Approve the Shop for the Golden Key price, buy item 2, then retrieve the flag.

Reusable Lessons

  • Solidity versions before 0.8 do not revert on arithmetic underflow/overflow unless SafeMath or explicit checks are used.
  • In ERC20-like code, a subtraction inside a require expression can already have wrapped before the comparison is evaluated.
  • Self-transfer can cancel out this specific underflow pattern because the balance is subtracted and then added back on the same address. Use a different recipient.
  • Storage layout can reveal linked contract addresses, such as a token address stored in a Shop contract slot.

Dead Ends

  • RAG returned no useful Token To Wonderland-specific or ERC20-underflow-specific memory. The solve used current source and live RPC evidence.

Tool Quirks

  • Local web3, solc, and foundry were unavailable, but Python requests plus pycryptodome Keccak were enough for JSON-RPC and selector encoding.
  • The HTB /connection_info private key must stay in loot/; analysis copies should be redacted.

Evidence Paths

  • analysis/source-audit.md
  • analysis/remote-readonly-state.json
  • analysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txt
  • analysis/rag-records.md
  • analysis/evaluator-20260609T105811063436Z-cc3f1a96.md
  • analysis/solve-run-summary.json
  • solve/solve.py
  • 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
1Audit provided Solidity contracts and remote JSON-RPC instance, identify token/key purchase or authorization flaw, then satisfy Setup.isSolved and capture /flag.Blockchain Easy hybrid challenge with source ZIP and remote host <TARGET>:31858; scenario mentions silver coins and buying/getting the first key from a dwarf.Extract contracts, identify Setup.isSolved condition, fetch /connection_info, and inspect target storage/function selectors before any transaction.MediumActive

Closed Branches

BranchEvidence TestedFailure OutputReason ClosedRevisit Condition

Memory Summary

approval_required: true

Sanitized Memory Summary

Metadata

  • Platform: HackTheBox Challenges
  • Category: Blockchain
  • Challenge: <REDACTED>
  • Difficulty: Easy
  • Source workspace: <REDACTED>

Validated Solve Chain

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

  1. Audit Setup.sol, Shop.sol, and SilverCoin.sol.
  2. Identify the win condition: Setup.isSolved(player) requires Shop item 2, the Golden Key, to be owned by the player.
  3. Confirm the player starts with too few SilverCoin units for a normal purchase.
  4. Identify the Solidity 0.7 unchecked arithmetic bug in SilverCoin._transfer: require(fromBalance - amount >= 0) does not prevent unsigned underflow.
  5. Use JSON-RPC to derive the live token address from Shop storage slot 1 and confirm the live balance/item state.
  6. Transfer more than the player balance to a different address, causing player balance underflow.
  7. Approve the Shop for the Golden Key price, buy item 2, then retrieve the flag.

Reusable Lessons

  • Solidity versions before 0.8 do not revert on arithmetic underflow/overflow unless SafeMath or explicit checks are used.
  • In ERC20-like code, a subtraction inside a require expression can already have wrapped before the comparison is evaluated.
  • Self-transfer can cancel out this specific underflow pattern because the balance is subtracted and then added back on the same address. Use a different recipient.
  • Storage layout can reveal linked contract addresses, such as a token address stored in a Shop contract slot.

Dead Ends

  • RAG returned no useful Token To Wonderland-specific or ERC20-underflow-specific memory. The solve used current source and live RPC evidence.

Tool Quirks

  • Local web3, solc, and foundry were unavailable, but Python requests plus pycryptodome Keccak were enough for JSON-RPC and selector encoding.
  • The HTB /connection_info private key must stay in loot/; analysis copies should be redacted.

Evidence Paths

  • analysis/source-audit.md
  • analysis/remote-readonly-state.json
  • analysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txt
  • analysis/rag-records.md
  • analysis/evaluator-20260609T105811063436Z-cc3f1a96.md
  • analysis/solve-run-summary.json
  • solve/solve.py
  • loot/flag.txt

Ingestion Decision

  • Proposed for LightRAG: yes
  • Requires user approval before ingestion: yes

Notes

Notes

Scope

  • Challenge: <REDACTED>
  • Category: Blockchain
  • Difficulty: Easy
  • Mode: hybrid
  • Remote instance: <TARGET>:31858
  • Start time: 2026-06-09T10:52:59Z
  • 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/a12c7357-d856-4e10-9a28-e90ea69a5e6c.zip2573<hash redacted>Zip archive data, at least v1.0 to extract, compression method=storezip entries: 4 shown in artifact inventory JSON
files/extracted/blockchain_token_to_wonderland/Setup.sol593<hash redacted>Java source, ASCII text
files/extracted/blockchain_token_to_wonderland/Shop.sol1063<hash redacted>Java source, ASCII text
files/extracted/blockchain_token_to_wonderland/SilverCoin.sol4578<hash redacted>ASCII text

Evidence Ledger

TimeActionOutput/FileFindingConfidenceNext
2026-06-09T10:52:59Zharness initchallenge-state.jsonWorkspace initialized with deterministic state fileHighInventory artifacts
2026-06-09T10:53:15Zartifact inventoryanalysis/artifact-inventory.json4 artifact(s) inventoriedHighBuild or update hypotheses
2026-06-09T10: <REDACTED>, identify token/key purchase or authorization flaw, then satisfy Setup.isSolved and capture /flag.MediumExtract contracts, identify Setup.isSolved condition, fetch /connection_info, and inspect target storage/function selectors before any transaction.
2026-06-09T10:54:23Zsource auditanalysis/source-audit.mdSource audit recordedHighGate before exploit
2026-06-09T10:54:43Zcheckpoint recordedanalysis/checkpoint-analysis-20260609T105443154310Z-bb5db5a0.mdCheckpoint for ANALYSISHighUse checkpoint to drive next decision
2026-06-09T10:55:02ZRAG queryanalysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txtRAG helper exited 0; output savedMediumRecord retrieval tag and validation
2026-06-09T10:56:12ZRAG recordanalysis/rag-records.mdRetrieved memory tagged MISSINGMediumValidate or reject with live evidence
2026-06-09T10:55:55Zread-only RPC validationanalysis/remote-readonly-state.jsonShop storage slot 1 resolves to SilverCoin; Golden Key item 2 costs 25,000,000; player balance is 100 and allowance is 0HighRecord local fallback/evaluator, run exploit gate, then perform transfer-underflow/approve/buy chain
2026-06-09T10:56:59Zlocal memory recordanalysis/local-memory-records.mdPrior local notes reviewed as fallback/advisory contextMediumValidate against current evidence
2026-06-09T10:58:11Zevaluatoranalysis/evaluator-20260609T105811063436Z-cc3f1a96.mdProceedHighRun exploit gate and execute solve/solve.py through challenge_exec.
2026-06-09T10: <REDACTED>
2026-06-09T10:59:54Zcompletion gatechallenge-state.jsonCompletion gate passed; state marked COMPLETEHighOptional sanitized memory summary approval

Key Findings

  • The remote exposes /connection_info, /rpc, and /flag.
  • Raw connection material from /connection_info is stored under loot/; redacted copies are in analysis/.
  • Setup.isSolved(player) requires Golden Key item 2 owner to be the player.
  • Shop.buyItem(2) requires transferFrom(player, shop, 25000000) to succeed.
  • SilverCoin._transfer uses unchecked Solidity 0.7 arithmetic: require(fromBalance - amount >= 0) cannot prevent unsigned underflow.
  • The live player starts with only 100 SilverCoin units, so normal purchase is impossible.
  • A transfer of more than the player balance to a different address should underflow the player balance high enough to approve and buy item 2.

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 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 Token To Wonderland, 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.