script (⏱,💰)

script (⏱,💰)

NG#7 - calldata

ng7 start

本任务内容是关于 StarkNet 合约的 calldata。calldata 是指函数调用时传递给函数的数据,在 StarkNet 中,传入的数据可能是各种类型,但最终都会转换为多个 felt252 组成的 snapshot,具体见call_contract_syscall

有 2 个合约,PortalSpell 合约中的 cast 接受的是 1 个Array<PortalData>,DrunkenMage 合约中的 cast 是传入 2 个Array<felt252>。需要你传入 2 个 Array 构成的 calldata,和Array<PortalData>前 2 个 PortalData 是相同的(验证见合约代码)。

上面的 cast 会通过 dispatcher 跨合约调用,无需验证参数是否一样。

思路#

首先,先需要了解 StarkNet 合约是怎么解析 calldata 的,任务的 walkthough 里说得很清楚了。

编码的目标是 Array<PortalData>,值是
[ 0: { location: 'TAVERN', details: [ 'OPEN', 'PORTAL' ] }, 1: { location: 'HOME', details: [ 'CLOSE', 'PORTAL' ] } ]

由于实现了 Serde,可以对 struct 进行 serialize,在测试中打印后,单个 struct 是这样编码的

2 个 PortalData 加在一起,前面再加个 2,就是最终编码。

drunk_spell.cast(origin, destination) 传入是分开的,我们必须找到 origin 和 destination 的值,以便当它被解释为 Array<PortalData> 时,它反映了所需的 calldata。

需要验证的只是前 2 个 Portal,是 ['TAVERN', 2, 'OPEN', 'PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL']就行。

如果再加一个PortalData {location: 'VOID', details: ['ANY']},编码会变成[3, 'TAVERN', 2, 'OPEN', 'PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL','VOID', 1 , 'ANY'],也是可以通过验证的。

但如果要拆分成 2 个 array 分别传入,就是 [3, 'TAVERN', 2, 'OPEN', 'PORTAL']['PORTAL', 'HOME', 2, 'CLOSE', 'PORTAL','VOID', 1 , 'ANY'],'PORTAL' 转为 felt 的十进制数字会非常大,想要加长 details 满足长度要求,或添加更多的 portal,会消耗很多 gas,在测试时运行会报错。

那如果继续增加 Portal 的个数,直到第二个参数 array 的长度是个非常小的数字的话,就不用消耗很多 Gas 了。接下来你应该知道怎么写了。

我通过了测试后,准备部署合约去破解。先用 starkli invoke 发现不能传 array,snforge invoke --calldata可以传 calldata,但又不知道怎么传入账户等额外参数。如果部署 hack 合约有点麻烦了。最终发现直接走 voyager 浏览器发送两个 calldata 是最简单的。

总结#

StarkNet 的 calldata 组成规则很简单,这题烧脑的地方是需要添加更多 Portal 去组成特定的 calldata,蛮有意思的 CTF 题目。

ng7 end

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。