默认分类

*CTF Treasure-hunter

这道题是rwctf中出现过的一道题改编。考察的主要是它本身定义的Merkle Tree的相关性质。
利用其中给出的部分操作码可以实现一些子树的添加、合并等等操作。最终满足在添加4个叶子的情况下还能正确计算root
并且在Pre_leave也就是检查是否验证过节点时也能通过根节点验证。(key和宝箱同时获得)

剩下需要注意的就是一些小条件了 比如 address的大小排序。以及左右合并顺序。

这在我的朋友 V4nish的这篇文章中介绍的非常详细。我这里不过多赘述了。
仅仅记录下题目的思考以及exp:

首先要了解题目允许我们来做什么:
用白话文讲
第一点 可以添加节点。在添加的时候会更新Root,并把你的这个叶子节点更新。
rw那道题只需要添加一次 就没有了从左合并还是从右合并这一顾虑。
而且 rw那道题其实也帮上了不少忙。感谢r3kapig的exp,不然第一次找计算root的hash还挺麻烦的。
这道题的难点要添加四次,而且后面再更新节点时因为Opcode再操作不同高度的时候会以不同的左右顺序来合并
子树往上继续合并。所以就要考虑到这点了。如果把高度同时也设置好。同时要记录每一次enter之后的合并成Root的2个hash。
这样比较方便后续操作我们的hash。
第二点就是操作我们的opcode了。
3种:

  1. 第一个是能把当前的叶子添加进去。也就是你之前enter的地址。
  2. 第二个是把你之前的2片叶子按照你输入的高度进行合并操作 会根据高度来 选择左右顺序。
  3. 第三个是你自己可以输入一个hash和你之前的叶子构成的hash合并。这也是我们解题非常重要的一点了。

enter就按规则往里面加就可以了。
第二步是要把整个team压进去且验证。然后难点就是要按照高度 和 之前我们压入时他的合并方向。再利用第二个opcode推一次。
第三步 是要把team压进去但不验证。这个就很简单。因为不验证的叶子都是0.只需要记得构成ROOT的hash的2个hash。在压入完前4个并合并之后。把2个hash按顺序压进去合并就可以了。

虽然不是 “合约安全” 相关的题,但是还是挺有趣的 还有点梦回OI 并且有点像公链的一些东西。想起来了西爹之前挖的CVE。。。

exp:

from web3 import Web3,HTTPProvider
from Crypto.Util.number import *
web3=Web3(HTTPProvider("http://123.60.45.88:8545/"))
print(web3.isConnected())
acct1= web3.eth.account.from_key('0xeadb4caca8d0088121549f2bf8ee0f6ee517110b4cfe36e3b655f85dfc035c1e')
acct2= web3.eth.account.from_key('0x00a6d7a2ee9220ae82d3dfbb9f4eb471bd8eacf80c31926855b5352866f8f5ee')
acct3=web3.eth.account.from_key('0x176e21af92fbfc31568f443b64922a630d0b27137c3e3c63c6aa6df2e9ee4b3f')
acct4=web3.eth.account.from_key('0x439ee9ada9f2991ecd3c4b1b2613b0d7f9eb08fefae687f14475105c1658da23')
tmp=(web3.eth.account.create())
print(tmp.address)
def deploy(rawTx,acct):
    signedTx = web3.eth.account.signTransaction(rawTx, private_key=acct.privateKey)
    hashTx = web3.eth.sendRawTransaction(signedTx.rawTransaction).hex()
    receipt = web3.eth.waitForTransactionReceipt(hashTx)
    print(receipt)
    return receipt
abi="""[
    {
        "inputs": [
            {
                "internalType": "bytes32[]",
                "name": "_proofs",
                "type": "bytes32[]"
            }
        ],
        "name": "enter",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "bytes32[]",
                "name": "_proofs",
                "type": "bytes32[]"
            }
        ],
        "name": "findKey",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "bytes32[]",
                "name": "_proofs",
                "type": "bytes32[]"
            }
        ],
        "name": "leave",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [],
        "stateMutability": "nonpayable",
        "type": "constructor"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "_from",
                "type": "address"
            }
        ],
        "name": "FindKey",
        "type": "event"
    },
    {
        "inputs": [],
        "name": "openTreasureChest",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "_from",
                "type": "address"
            }
        ],
        "name": "OpenTreasureChest",
        "type": "event"
    },
    {
        "inputs": [
            {
                "internalType": "bytes32[]",
                "name": "_proofs",
                "type": "bytes32[]"
            }
        ],
        "name": "pickupTreasureChest",
        "outputs": [],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "anonymous": false,
        "inputs": [
            {
                "indexed": true,
                "internalType": "address",
                "name": "_from",
                "type": "address"
            }
        ],
        "name": "PickupTreasureChest",
        "type": "event"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "name": "haveKey",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [
            {
                "internalType": "address",
                "name": "",
                "type": "address"
            }
        ],
        "name": "haveTreasureChest",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "isSolved",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "root",
        "outputs": [
            {
                "internalType": "bytes32",
                "name": "",
                "type": "bytes32"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "smtMode",
        "outputs": [
            {
                "internalType": "enum SMT.Mode",
                "name": "",
                "type": "uint8"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    },
    {
        "inputs": [],
        "name": "solved",
        "outputs": [
            {
                "internalType": "bool",
                "name": "",
                "type": "bool"
            }
        ],
        "stateMutability": "view",
        "type": "function"
    }
]"""


contract_address='0x6CC5804bb2a8FeBbE9b1babd7c3Bcc93735F5A81'
contract=web3.eth.contract(abi=abi,address=contract_address)

def to_bytes32(a: int) -> bytes:
  return a.to_bytes(32, 'big')
def change_it(a):
    tmp=[]
    for i in a:
        tmp.append(to_bytes32(int(i,16)))
    return tmp
if __name__ == '__main__':
    data1=["0x000000000000000000000000000000000000000000000000000000000000004c", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009c", "0xe9f810898db8dc62342eaa122fd26525362f2b70bd462edef6e4e34093d66c17", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009f", "0xba13a52ab72064627701ac75ab564f7e786d093c655849458536cc689abdf8e2"]
    data2=["0x000000000000000000000000000000000000000000000000000000000000004c", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009a", "0x423b9e9e1b3a1cfbd4c7d075391785c44def9c2884281cc63e0a78fcc3bb7f66", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009f", "0xba13a52ab72064627701ac75ab564f7e786d093c655849458536cc689abdf8e2"]
    data3=["0x000000000000000000000000000000000000000000000000000000000000004c", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009e", "0x4e951b5440112027d135d0f1dc97bff8127bec7439f8a54ff6a6b6a164764c64", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009f", "0xba13a52ab72064627701ac75ab564f7e786d093c655849458536cc689abdf8e2"]
    data4=["0x000000000000000000000000000000000000000000000000000000000000004c", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009e", "0x4b3f7ee71efd14747b6620f1919bcdc78724d8a862e987596a1179a6b460be05", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009f", "0xba13a52ab72064627701ac75ab564f7e786d093c655849458536cc689abdf8e2"]
    data5=["0x000000000000000000000000000000000000000000000000000000000000004c", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009c", "0xe9f810898db8dc62342eaa122fd26525362f2b70bd462edef6e4e34093d66c17", "0x000000000000000000000000000000000000000000000000000000000000004c", "0x0000000000000000000000000000000000000000000000000000000000000048", "0x000000000000000000000000000000000000000000000000000000000000009a", "0x000000000000000000000000000000000000000000000000000000000000004c","0x0000000000000000000000000000000000000000000000000000000000000048","0x000000000000000000000000000000000000000000000000000000000000009e","0x000000000000000000000000000000000000000000000000000000000000004c","0x0000000000000000000000000000000000000000000000000000000000000048", "0x000000000000000000000000000000000000000000000000000000000000009e","0x0000000000000000000000000000000000000000000000000000000000000050","0x000000000000000000000000000000000000000000000000000000000000009f","0xba13a52ab72064627701ac75ab564f7e786d093c655849458536cc689abdf8e2"]
    data6=["0x000000000000000000000000000000000000000000000000000000000000004c","0x000000000000000000000000000000000000000000000000000000000000004c","0x0000000000000000000000000000000000000000000000000000000000000048", "0x000000000000000000000000000000000000000000000000000000000000009a", "0x000000000000000000000000000000000000000000000000000000000000004c","0x0000000000000000000000000000000000000000000000000000000000000048","0x000000000000000000000000000000000000000000000000000000000000009e","0x000000000000000000000000000000000000000000000000000000000000004c","0x0000000000000000000000000000000000000000000000000000000000000048","0x000000000000000000000000000000000000000000000000000000000000009e","0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009c", "0x03f38c848024ba3006373acb4175da682ce9d4e1ade10e0472919f30f664b1e4", "0x0000000000000000000000000000000000000000000000000000000000000050", "0x000000000000000000000000000000000000000000000000000000000000009f", "0xba13a52ab72064627701ac75ab564f7e786d093c655849458536cc689abdf8e2"]
    final1=change_it(data1)
    final2 = change_it(data2)
    final3 = change_it(data3)
    final4 = change_it(data4)
    final5 = change_it(data5)
    final6 = change_it(data6)
    enter_txn1 = contract.functions.enter(final1).buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct1.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct1.signTransaction(enter_txn1)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)

    enter_txn2 = contract.functions.enter(final2).buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct2.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct2.signTransaction(enter_txn2)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)

    enter_txn3 = contract.functions.enter(final3).buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct3.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct3.signTransaction(enter_txn3)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)

    enter_txn4 = contract.functions.enter(final4).buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct4.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct4.signTransaction(enter_txn4)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)

    pickupTreasureChest_txn = contract.functions.pickupTreasureChest(final5).buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct4.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct4.signTransaction(pickupTreasureChest_txn)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)
    findKey_txn = contract.functions.findKey(final6).buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct4.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct4.signTransaction(findKey_txn)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)

    openTreasureChest_txn = contract.functions.openTreasureChest().buildTransaction({
        'nonce': web3.eth.getTransactionCount(acct4.address),
        'gas': 300000,
        'gasPrice': web3.eth.gasPrice
    })
    signed = acct4.signTransaction(openTreasureChest_txn)
    tx_id = web3.eth.sendRawTransaction(signed.rawTransaction)
    rec=web3.eth.waitForTransactionReceipt(tx_id)
    print(rec)

回复

This is just a placeholder img.