Capture The Ether. Fifty Years
Да уж, заставила эта задача посидеть несколько часов, чтобы понять ее логику.
Сам я не решил ее, поэтому пошел гуглить ответы, которые были в нескольких вариациях. И вот уже над ними просидел столько времени.
Я сам сразу понял, что вся уязвимость кроется в upsert() else логике, вот только исполнить ее не мог. Вот сам контракт и вот эта функция:
function upsert(uint256 index, uint256 timestamp) public payable {
require(msg.sender == owner);
if (index >= head && index < queue.length) {
Contribution storage contribution = queue[index];
contribution.amount += msg.value;
} else {
require(timestamp >= queue[queue.length - 1].unlockTimestamp + 1 days);
contribution.amount = msg.value;
contribution.unlockTimestamp = timestamp;
queue.push(contribution);
}
}
Как мы видим из контракта, msg.value переписывает первый слот памяти, где лежит длина массива, а timestamp - второй слот (head), который защищает от цикла повторного вывода денег с массива.
Что нужно тут понимать?
Во-первых, принципы работы overflow.
Нам нужно каким-то образом записать в timestamp значение 0. Но просто так этого мы сделать не можем, будет выдавать ошибку. Поэтому нам нужно для начала создать переполнение чисел uint256 (2256 - 1), а в нашем случае минус один день, т.е. (2256-86400 секунд). И уже только после этого действия (транзакции), мы сможем записать "0" в timestamp второй транзакцией, что также обновить head до 0 (каким он и должен быть!).
Во-вторых, msg.value обновляет длину массива, поэтому нам нужно отправлять не по одному Эфиру (10**18), а по 1 Wei, тогда каждый wei будет равен размерности длины массива.
В-третьих, когда выполняется действие queue.push(contribution) в данном примере, в массив записывается значение msg.value, которое мы отправляем в Wei и еще "+1". Другими словами, после того как мы в первый раз отправим транзакцию и передадим 1 Wei, то на балансе контракта будет 1 Эфир и 1 Wei, а в массиве 1 Эфир и 2 Wei.
При этой, учитывая, что длина массива на момент получения транзакций уже будет равна "1", и мы, как бы отправляя 1 Wei, пытаемся перезаписать это значение, то благодаря push, который увеличивает длину массива на 1, ошибки не будет. И длина массива всегда будет плюсоваться.
Представляю, что из моего текста это тоже звучит мало понятно, поэтому вот вам две ссылки на английском языке, которые помогут разобраться с этой задачей. Первая и вторая. Но придется тоже посидеть.
Для решение этой задача нам нужно будет отправить несколько транзакций, постепенно увеличивая количество отправляемых Wei на +1, и переписывая overflow timestamp с "2**256-86400" до "0" до "86400", и так по кругу.
Потом мы сможем вызвать withdraw() и забрать некоторое количество Wei и Эфир.
Теперь понимаю, почему эта задача является самой дорогостоящей среди заданий CTE.
#capture #cte
Post #477
170
- 👍 1