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
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.
Walkthrough flow
Audit Setup.sol, Shop.sol, and SilverCoin.sol.
Identify the win condition: Setup.isSolved(player)...
Confirm the player starts with too few SilverCoin...
Identify the Solidity 0.7 unchecked arithmetic bug in...
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.
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:
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:
- Fetch live
/connection_infoand store raw connection material underloot/, with redacted analysis copies. - Read the SilverCoin address from Shop storage slot 1.
- Read Golden Key item 2 owner/price and the player balance.
- Call
SilverCoin.transfer(shop, balance + 1)from the player to trigger underflow. - Call
SilverCoin.approve(shop, 25000000). - Call
Shop.buyItem(2). - Confirm item 2 owner changed to the player.
- Request
/flagand store the raw response inloot/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
privatestorage 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_infoprivate keys and raw flags inloot/, with redacted copies inanalysis/.
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
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7357-d856-4e10-9a28-e90ea69a5e6c.zip | 2573 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 4 shown in artifact inventory JSON |
files/extracted/blockchain_token_to_wonderland/Setup.sol | 593 | <hash redacted> | Java source, ASCII text | |
files/extracted/blockchain_token_to_wonderland/Shop.sol | 1063 | <hash redacted> | Java source, ASCII text | |
files/extracted/blockchain_token_to_wonderland/SilverCoin.sol | 4578 | <hash redacted> | ASCII text |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-09T10:52:59Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-09T10:53:15Z | artifact inventory | analysis/artifact-inventory.json | 4 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-09T10:53:15Z | hypothesis recorded | hypothesis-board.md | Audit provided Solidity contracts and remote JSON-RPC instance, identify token/key purchase or authorization flaw, then satisfy Setup.isSolved and capture /flag. | Medium | Extract contracts, identify Setup.isSolved condition, fetch /connection_info, and inspect target storage/function selectors before any transaction. |
| 2026-06-09T10:54:23Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-09T10:54:43Z | checkpoint recorded | analysis/checkpoint-analysis-20260609T105443154310Z-bb5db5a0.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-09T10:55:02Z | RAG query | analysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-09T10:56:12Z | RAG record | analysis/rag-records.md | Retrieved memory tagged MISSING | Medium | Validate or reject with live evidence |
| 2026-06-09T10:55:55Z | read-only RPC validation | analysis/remote-readonly-state.json | Shop storage slot 1 resolves to SilverCoin; Golden Key item 2 costs 25,000,000; player balance is 100 and allowance is 0 | High | Record local fallback/evaluator, run exploit gate, then perform transfer-underflow/approve/buy chain |
| 2026-06-09T10:56:59Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-09T10:58:11Z | evaluator | analysis/evaluator-20260609T105811063436Z-cc3f1a96.md | Proceed | High | Run exploit gate and execute solve/solve.py through challenge_exec. |
| 2026-06-09T10:58:37Z | flag capture | loot/flag.txt | HTB-format flag captured; raw value kept in loot only | High | Write solution and run completion gate |
| 2026-06-09T10:59:54Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- The remote exposes
/connection_info,/rpc, and/flag. - Raw connection material from
/connection_infois stored underloot/; redacted copies are inanalysis/. Setup.isSolved(player)requires Golden Key item 2 owner to be the player.Shop.buyItem(2)requirestransferFrom(player, shop, 25000000)to succeed.SilverCoin._transferuses 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:
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.
- Audit
Setup.sol,Shop.sol, andSilverCoin.sol. - Identify the win condition:
Setup.isSolved(player)requires Shop item 2, the Golden Key, to be owned by the player. - Confirm the player starts with too few SilverCoin units for a normal purchase.
- Identify the Solidity 0.7 unchecked arithmetic bug in
SilverCoin._transfer:require(fromBalance - amount >= 0)does not prevent unsigned underflow. - Use JSON-RPC to derive the live token address from Shop storage slot 1 and confirm the live balance/item state.
- Transfer more than the player balance to a different address, causing player balance underflow.
- 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
requireexpression 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, andfoundrywere unavailable, but PythonrequestspluspycryptodomeKeccak were enough for JSON-RPC and selector encoding. - The HTB
/connection_infoprivate key must stay inloot/; analysis copies should be redacted.
Evidence Paths
analysis/source-audit.mdanalysis/remote-readonly-state.jsonanalysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txtanalysis/rag-records.mdanalysis/evaluator-20260609T105811063436Z-cc3f1a96.mdanalysis/solve-run-summary.jsonsolve/solve.pyloot/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 | Audit 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. | Medium | Active |
Closed Branches
| Branch | Evidence Tested | Failure Output | Reason Closed | Revisit 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.
- Audit
Setup.sol,Shop.sol, andSilverCoin.sol. - Identify the win condition:
Setup.isSolved(player)requires Shop item 2, the Golden Key, to be owned by the player. - Confirm the player starts with too few SilverCoin units for a normal purchase.
- Identify the Solidity 0.7 unchecked arithmetic bug in
SilverCoin._transfer:require(fromBalance - amount >= 0)does not prevent unsigned underflow. - Use JSON-RPC to derive the live token address from Shop storage slot 1 and confirm the live balance/item state.
- Transfer more than the player balance to a different address, causing player balance underflow.
- 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
requireexpression 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, andfoundrywere unavailable, but PythonrequestspluspycryptodomeKeccak were enough for JSON-RPC and selector encoding. - The HTB
/connection_infoprivate key must stay inloot/; analysis copies should be redacted.
Evidence Paths
analysis/source-audit.mdanalysis/remote-readonly-state.jsonanalysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txtanalysis/rag-records.mdanalysis/evaluator-20260609T105811063436Z-cc3f1a96.mdanalysis/solve-run-summary.jsonsolve/solve.pyloot/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
| File | Size | SHA256 | Type | Notes |
|---|---|---|---|---|
files/a12c7357-d856-4e10-9a28-e90ea69a5e6c.zip | 2573 | <hash redacted> | Zip archive data, at least v1.0 to extract, compression method=store | zip entries: 4 shown in artifact inventory JSON |
files/extracted/blockchain_token_to_wonderland/Setup.sol | 593 | <hash redacted> | Java source, ASCII text | |
files/extracted/blockchain_token_to_wonderland/Shop.sol | 1063 | <hash redacted> | Java source, ASCII text | |
files/extracted/blockchain_token_to_wonderland/SilverCoin.sol | 4578 | <hash redacted> | ASCII text |
Evidence Ledger
| Time | Action | Output/File | Finding | Confidence | Next |
|---|---|---|---|---|---|
| 2026-06-09T10:52:59Z | harness init | challenge-state.json | Workspace initialized with deterministic state file | High | Inventory artifacts |
| 2026-06-09T10:53:15Z | artifact inventory | analysis/artifact-inventory.json | 4 artifact(s) inventoried | High | Build or update hypotheses |
| 2026-06-09T10: <REDACTED>, identify token/key purchase or authorization flaw, then satisfy Setup.isSolved and capture /flag. | Medium | Extract contracts, identify Setup.isSolved condition, fetch /connection_info, and inspect target storage/function selectors before any transaction. | |||
| 2026-06-09T10:54:23Z | source audit | analysis/source-audit.md | Source audit recorded | High | Gate before exploit |
| 2026-06-09T10:54:43Z | checkpoint recorded | analysis/checkpoint-analysis-20260609T105443154310Z-bb5db5a0.md | Checkpoint for ANALYSIS | High | Use checkpoint to drive next decision |
| 2026-06-09T10:55:02Z | RAG query | analysis/rag/rag-query-20260609T105451416113Z-2db92e3b.txt | RAG helper exited 0; output saved | Medium | Record retrieval tag and validation |
| 2026-06-09T10:56:12Z | RAG record | analysis/rag-records.md | Retrieved memory tagged MISSING | Medium | Validate or reject with live evidence |
| 2026-06-09T10:55:55Z | read-only RPC validation | analysis/remote-readonly-state.json | Shop storage slot 1 resolves to SilverCoin; Golden Key item 2 costs 25,000,000; player balance is 100 and allowance is 0 | High | Record local fallback/evaluator, run exploit gate, then perform transfer-underflow/approve/buy chain |
| 2026-06-09T10:56:59Z | local memory record | analysis/local-memory-records.md | Prior local notes reviewed as fallback/advisory context | Medium | Validate against current evidence |
| 2026-06-09T10:58:11Z | evaluator | analysis/evaluator-20260609T105811063436Z-cc3f1a96.md | Proceed | High | Run exploit gate and execute solve/solve.py through challenge_exec. |
| 2026-06-09T10: <REDACTED> | |||||
| 2026-06-09T10:59:54Z | completion gate | challenge-state.json | Completion gate passed; state marked COMPLETE | High | Optional sanitized memory summary approval |
Key Findings
- The remote exposes
/connection_info,/rpc, and/flag. - Raw connection material from
/connection_infois stored underloot/; redacted copies are inanalysis/. Setup.isSolved(player)requires Golden Key item 2 owner to be the player.Shop.buyItem(2)requirestransferFrom(player, shop, 25000000)to succeed.SilverCoin._transferuses 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:
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.