TGViewer
DEV: Рубиновые тона DEV: Рубиновые тона @dev_in_ruby_colors · 3.28K subscribers
Post #304 927
Немного подробнее.

Если атакующий делает проигрышную ставку с контракта, а в контракте есть receive, которая всегда возвращает ошибку, то попытки возместить проигравшую ставку заведомо обречены. В коде возврат делался стандартным образом:

            if (refund > 0) {
(bool sent, ) = bidData.bidder.call{value: refund}("");
require(sent, "Failed to refund bidder");
}


А коль скоро возвратов много и их надо обрабатывать в цикле, то как только попадается адрес контракта злоумышленника, то и вся цепочка обрывается. Правда потом злоумышленник всё-таки вышел на связь и сказал, что просто хотел показать ненадёжность аукциона и этот блок убрал (у него в receive просто стояло элементарное условие вида "возвращать ошибку/не возвращать ошибку"). Если ошибку не возвращать, то просто пробрасывать принятые деньги на кошелёк атакующего, что вернуть свою ставку. Ну, либо там мог быть бесконечный цикл, вот пример https://twitter.com/0xInuarashi/status/1517677597978599425

Вроде бы, всё ок, то есть возвраты стали работать и участники аукциона свои деньги назад таки получили.

Но есть и другой неприятный момент. В коде для функции claimProjectFunds, которая выводила деньги на счёт устроителей, было условие refundProgress >= totalBids (то есть нужно, чтобы было выплачено возвратов столько, сколько было ставок, видимо) - только после этого сами владельцы контракта могут забрать вырученные средства, то есть победившие ставки.

Однако дополнительно была введена переменная bidIndex, которая тоже была связана с числом ставок. В функцию bid, приходила не только денежная сумма, а ещё и количество ставок (amount), которое могло варьироваться от 1 до 3. Но владельцы контракта это не учли и вместо того, чтобы написать bidIndex += amount написали bidIndex++ (ну, либо им надо было ещё одну переменную выделять на это).

А дальше самое интересное. В функции для обработки возвратов стояло ещё одно условие require(_refundProgress < _bidIndex, "Refunds already processed");. То есть кол-во возвратов не могло превышать кол-во bidIndex. Но если вспомнить, что bidIndex считается неправильно (а многие юзеры делали сразу много ставок), то refundProgress всегда будет меньше, чем totalBids (то есть кол-во ставок в принципе, которое как раз считалось правильно). А теперь мы вспомним условие в функции "забрать себе деньги за аукцион": require(refundProgress >= totalBids, "Refunds not yet processed");.

Фактически, это условие истинным не будет никогда, то есть устроители аукциона обманули самих себя, и деньги застряли на их же контракте.
Twitter 0xInuarashi 7/ As for a solidity readable, it was probably something similar to this. If block, fail the receivable fallback. Otherwise, let it succeed. Demonstration was written by @notchefbob which tried to notify the team of the issue.
  • 👍 9
  • 🔥 4
More from @dev_in_ruby_colors
  1. Oct 4, 2026В этом уроке по абстрактной алгебре говорим про ideals (идеалы), factor (quotient) rings и…
  2. Sep 28, 2026Github буйствует
  3. Sep 26, 2026В этом уроке по абстрактной алгебре говорим про области целостности, делители нуля, характ…
  4. Sep 22, 2026А тем временем сказ о ведьмаке, потерявшем память, уже доступен в виде аудиокниги. Целых 1…
  5. Sep 20, 2026В этом уроке по абстрактной алгебре продолжаем говорить о кольцах: в частности о subrings…
  6. Sep 17, 2026Сделал обзор актуальных библиотек JS для сбора данных, 10 штук бодрых решений на все случа…
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 →