script (⏱,💰)

script (⏱,💰)

NG#10 - 無序多簽賬戶

NG10

關於合約帳戶有 3 個子任務,共同組成了 bad account 系列。本文是系列的第三個任務 Orderless Hashing。

分析#

這題和上一題一樣有 2 個合約,帳戶合約 GrandPharaoh 的構造器中指定了特定的公鑰。__validate__中的get_multicall_hash會對每個 Call 遞迴計算 hash,最後用 异或操作 (^) 組成一個 hash,hash 是個 u256,low * high 是最終的 messageHash。我們構造傳入簽名的 r 和 s 能被 ECDSA 驗證。

需要執行的 Calls 是另一個合約的 RoyalSpear.equip (0),限制了只能被帳戶合約 GrandPharaoh 調用。

由於沒有私鑰,需要找到 ECDSA 中的漏洞去破解。

過程#

首先需要看看 Calls 列表計算的無序多簽最終是怎樣的,可以是 [equip(0)],是[equip(1),equip(0)]或者更多,只需要最後一個值是 equip (0) 就行。

這一部分可以用 snforge 寫 cairo 的 test,在帳戶合約中添加(multicall_hash.low.into() * multicall_hash.high.into()).print();讀取。

很明顯能發現,[equip(0),equip(0)]對應的 hash 是 0,可以從此入手。

接下來深入 check_ecdsa_signature 中,看看具體是怎麼進行簽名驗證的。

註釋掉use ecdsa::check_ecdsa_signature;,把源碼貼上到 test 中,方便在必要的地方添加 print。

首先注意到是 let zG: EcPoint = gen_point.mul(message_hash);,由於 message_hash 是 0(無窮點),橢圓曲線中 P*0=0,所以 zG 是 0。如果用以下代碼檢查,會輸出 'zG_x not exist',因為無窮點是沒有 x 坐標的。

    match zG.try_into() {
        Option::Some(pt) => {
            let (x, _) = ec::ec_point_unwrap(pt);
            'zG_x'.print();
            x.print();
        },
        Option::None => { 'zG_x not exist'.print(); },
    };

註釋中寫清楚了驗證的公式是 (zG +/- rQ).x = sR.x,對應 zG + rQ 的代碼是:

    match (zG + rQ).try_into() {
        Option::Some(pt) => {
            let (x, _) = ec::ec_point_unwrap(pt);
            if (x == sR_x) {
                return true;
            }
        },
        Option::None => {},
    };

其中 zG 是 0,所以需要 rQ.x = sR.x,r 是傳入的 sigature.r,Q 是公鑰對應曲線上的點,s 是傳入的sigature.r,R 是 r 對應曲線上的點。

很明顯只需要 r=s 且 Q=R 就可以通過驗證。其中公鑰可以在區塊鏈瀏覽器的部署交易中找到。

最後和上題同樣方法使用 Starknet.js 中的 invokeFunction 傳入特定 signature 和 calls 完成任務。

總結#

NG10 end

如果深入學習了 ECDSA,知道橢圓曲線的運算規則,這題是很容易找到答案的。合約的多簽設計太簡單,可以利用簽名重放進行攻擊。

就此 Account 部分的三題就全部完成了。整個 Cairo 系列還有一題用 Cairo 寫虛擬機的不打算做了,我討厭算法題。未來會更新在 Starknet 實際開發中遇到的有趣的東西,敬請期待。

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。