合约审计比赛
wp
NUMEN CTF writeup
by ChaMd5 | CN-SEC 中文网
DeFiHackLabs’s Substack
| SunWeb3Sec | Substack
SimpleCall
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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 pragma solidity ^0.7.0; contract ExistingStock { address public owner; address private reserve; string public name = "Existing Stock"; string public symbol = "ES"; uint256 public decimals = 18; uint256 public totalSupply = 200000000000; uint8 public frequency = 1; bool public Lock = false; bool public result; bool public flag; event Approval(address indexed from, address indexed to, uint number); event Transfer(address indexed from, address indexed to, uint number); event Deposit(address indexed to, uint number); event Withdraw(address indexed from, uint number); event Target(address indexed from, bool result); mapping (address => uint) public balanceOf; mapping (address => mapping (address => uint)) public allowance; constructor() public { owner = msg.sender; balanceOf[owner] = totalSupply; } function approve(address to, uint number) public returns (bool) { allowance[msg.sender][to] = number; emit Approval(msg.sender, to, number); return true; } function transfer(address _to, uint _value) public returns (bool) { require(balanceOf[msg.sender] - _value >= 0); balanceOf[msg.sender] -= _value; balanceOf[_to] += _value; return true; } function transferFrom(address from, address to, uint number) public returns (bool){ require(balanceOf[from] >= number); if (from != msg.sender && allowance[from][msg.sender] != uint256(-1)) { require(allowance[from][msg.sender] >= number); allowance[from][msg.sender] -= number; } balanceOf[from] -= number; balanceOf[to] += number; emit Transfer(from, to, number); return true; } function privilegedborrowing(uint256 value,address secure,address target,bytes memory data) public { require(Lock == false && value >= 0 && value <= 1000); balanceOf[address(this)] -= value; balanceOf[target] += value; address(target).call(data); Lock = true; require(balanceOf[target] >= value); balanceOf[address(this)] += value; balanceOf[target] -= value; Lock = false; } function withdraw(uint number) public { require(balanceOf[msg.sender] >= number); balanceOf[msg.sender] -= number; (msg.sender).transfer(number); emit Withdraw(msg.sender, number); } function setflag() public { if(balanceOf[msg.sender] > 200000 && allowance[address(this)][msg.sender] > 200000){ flag = true; } } function isSolved() public view returns(bool){ return flag; } }
要满足的是:
balanceOf会下溢,利用transfer函数可以满足第一个条件:
只有approve能修改allowance,但是发起方只能是msg.sender
privilegeborrowing存在任意地址的call调用,调用后会使得msg.sender改为target,所以可以满足第二个条件
counter
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 pragma solidity ^0.8.13; contract Deployer { constructor(bytes memory code) { assembly { return (add(code, 0x20), mload(code)) } } } contract SmartCounter{ address public owner; address public target; bool flag=false; constructor(address owner_){ owner=owner_; } function create(bytes memory code) public{ require(code.length<=24); target=address(new Deployer(code)); } function A_delegateccall(bytes memory data) public{ (bool success,bytes memory returnData)=target.delegatecall(data); require(owner==msg.sender); flag=true; } function isSolved() public view returns(bool){ return flag; } }
先学一下solidity的内存布局Solidity - 内存布局 - 简书
(jianshu.com)
delegatecall调用的时候我们的地址会变成tx.origin,又由于owner在slot0,所以把tx.origin存到slot0即可满足owner=msg.sender。构造的code为:
1 2 3 4 5 ORIGIN PUSH1 0x00 SSTORE 32600055