[Blockchain] Tx.origin attack
Где можно допустить ошибку?
- При использовании перевода эфира через address.call.value(amount)()
- Идентификация владельца контракта через tx.origin
TxOriginVictim - контракт-кошелёк
pragma solidity ^0.4.18; contract TxOriginVictim { address owner; function TxOriginVictim() { owner = msg.sender; } function transferTo(address to, uint amount) public { require(tx.origin == owner); to.call.value(amount)(); } function() payable public {}}
TxOriginAttacker - контракт атакующего
pragma solidity ^0.4.18; interface TxOriginVictim { function transferTo(address to, uint amount); } contract TxOriginAttacker { address owner; function TxOriginAttacker() public { owner = msg.sender; } function getOwner() public returns (address) { return owner; } function() payable public { TxOriginVictim(msg.sender).transferTo(owner, msg.sender.balance); } }
Как это работает:
- Пользователь переводит средства со своего контракта на адрес контракта атакующего
- Эфир попадает в контракт атакующего и вызывается callback функция
TxOriginVictim(msg.sender).transferTo(owner, msg.sender.balance); - Команды в этой функции будут "представляться" как TxOriginVictim, используя его адрес из msg.sender чтобы перевести все средства
message.sender.balance владельцу TxOriginAttacker контракта - Это работает благодаря тому, что в контракте TxOriginVictim мы проверяем tx.origin, а не msg.sender
- tx.origin - отправитель транзакции, а msg.sender - непосредственный отправитель
- Злоумышленнику ничего не мешает отправить себе всю оставшуюся сумму с контракта отправителя
Решение:
- Никогда не используйте tx.origin вместо msg.sender при идентификации отправителя
- Никогда не используйте address.call.value(amount)(); вместо address.transfer()
- address.transfer() будет обладать газом = 2300, что означает, что у возможных контрактов на атаку не хватит газа для дальнейших вычислений, кроме испускания событий
- Также address.transfer() выкинет ошибку