Token to Wonderland
hackthebox
Task: Buy Golden Key from Shop contract with only 100 SVC tokens when price is 25,000,000. Solution: Exploit integer underflow in Solidity 0.7.x ERC20 token where transfer() skips balance check, causing balance to wrap to 2^256-1, then approve and purchase.
$ ls tags/ techniques/
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
Token to Wonderland — HackTheBox
Description
A group found a map to a treasure guarded by 3 keys. They need to acquire the "Golden Key" from a dwarf's shop, but they don't have enough "SilverCoins" (ERC20 tokens) to buy it. The challenge provides a private Ethereum chain with three contracts: Setup, SilverCoins (ERC20), and Shop.
Goal: Buy the Golden Key (item 2) from the Shop contract, having only 100 SVC tokens while the price is 25,000,000.
Analysis
Infrastructure
- Private Ethereum chain (chain ID 31337)
- Web interface:
/rpc,/flag,/connection_info,/docs - Player receives 5000 ETH and 100 SVC (SilverCoins) tokens
Contracts
Setup (0x4e07...):
isSolved(address)— checksitems[2].owner == player_addresson ShopTARGET()— Shop contract address
SilverCoins (0x045d...) — ERC20 token:
- Compiled with Solidity 0.7.06 (no built-in overflow/underflow protection!)
- Total supply: 1e15
- Player receives 100 tokens, the rest goes to Setup
Shop (0xf39e...) — shop with 3 items:
| Item | Name | Price | Owner |
|---|---|---|---|
| 0 | Diamond Necklace | 1,000,000 | Shop |
| 1 | Ancient Stone | 70,000 | Shop |
| 2 | Golden Key | 25,000,000 | Shop |
buyItem Function
1. Loads item from storage
2. Checks item.owner == address(this) (shop must own it)
3. Calls token.transferFrom(msg.sender, address(this), item.price)
4. Checks return value: require(success, "Payment failed!")
5. Sets items[id].owner = msg.sender
buyItem uses transferFrom(), which correctly checks the balance. This means we need to have enough tokens.
Vulnerability: Dual _transfer Implementation
When analyzing the ERC20 token bytecode, two different internal _transfer functions were discovered:
_transferfortransferFrom()(offset 0x63b): Correctly checksrequire(balances[from] >= amount).
...
$ grep --similar
Similar writeups
- [blockchain][free]Magic Vault— hackthebox
- [blockchain][free]False Bidding— hackthebox
- [blockchain][free]Honor Among Thieves— hackthebox
- [blockchain][free]Portal Noncense— HackTheBox
- [blockchain][free]Locked and Loaded— hackthebox