區塊鏈趣步DAPP合約模式系統開發丨去中心化DAPP系統開發方案

a1271916008發表於2023-04-25

什麼是短地址攻擊

大家都知道,如果我們想呼叫智慧合約的函式,需要在交易的payload欄位中填充一段位元組碼。以ERC20的transfer()的函式為例,函式原型為:


function transfer(address to, uint amount) public returns (bool success);


我們需要透過一段68個位元組的位元組碼來呼叫該函式進行轉賬,比如:


a9059cbb000000000000000000000000146aed09cd9dea7a64de689c5d3ef73d2ee5ca000000000000000000000000000000000000000000000000000000000000000001


具體可以分解為3個部分:


4位元組函式簽名:a9059cbb

to引數:000000000000000000000000146aed09cd9dea7a64de689c5d3ef73d2ee5ca00

amount引數:0000000000000000000000000000000000000000000000000000000000000001

大家可能注意到,這個轉賬地址有點特殊:最後兩個數字為0。


假如有個使用者“不小心”忘記輸入最後這兩個0了怎麼辦?這樣我們的輸入就只有67個位元組了。EVM是透過CALLDATALOAD指令從輸入資料中獲取函式引數的,因此它會先從後面的amount引數裡“借”兩個0來補足前面的地址引數。當它要載入amount引數的時候,發現位數不夠,會在右邊補0,參見以太坊原始碼:


所以,經過這麼一折騰,實際上EVM看到是下面這些引數:


4位元組函式簽名:a9059cbb

to引數:000000000000000000000000146aed09cd9dea7a64de689c5d3ef73d2ee5ca00(借0)

amount引數:0000000000000000000000000000000000000000000000000000000000000100(補0)

看到問題了沒?轉賬地址沒變,但是轉賬金額增大了256倍!如果你的轉賬地址後面有足夠多的0,那麼轉賬金額將會大得驚人~


但是有人會說,這沒啥毛用啊,難道智慧合約的作者會傻到不檢查你地址的餘額,就直接讓你提幣走人嗎?我猜想這跟目前中心化交易所的運營機制相關。考慮下面的場景:使用者充幣到交易所錢包,交易所又把這些幣轉移到了它們內部的合約賬戶中。等使用者發起提幣申請,並透過人工稽核後,再從合約中把幣打到使用者的賬戶中。



在這種情況下,交易的msg.sender就是交易所本身,因此可以透過餘額檢查。當然,這裡有個前提:你必須能夠透過人工稽核!也就是稽核員失職。實際上,從沒有人成功利用過這個漏洞,發現這個問題的GNT專案組,也僅僅是觀察到一筆異常交易而已,並沒有產生任何實質性損失。網路上流傳的攻擊方法是:先找到一個裡面有足夠數量代幣的交易所賬戶,充1000個幣進去,然後再申請提1000個幣,就可以提出來256000個幣。但是,在我看來這似乎並不可行,如果有讀友能想出可行的場景,歡迎給我留言~


2.現在還能重現嗎?

能。


當然,不能透過常規的方式。不能透過remix,因為客戶端會檢查地址長度。也不能透過sendTransaction(),因為web3中也加了保護。但是,我們可以使用sendRawTransaction()。


2.1先寫一個簡單合約

pragma solidity ^0.4.25;


contract ABC {

    mapping (address => uint) balances;


    event Transfer(address indexed _from, address indexed _to, uint256 _value);


    constructor() public {

        balances[msg.sender] = 10000;

    }


    function transfer(address to, uint amount) public returns(bool success) {

        if (balances[msg.sender] < amount) return false;

        balances[msg.sender] -= amount;

        balances[to] += amount;

        emit Transfer(msg.sender, to, amount);

        return true;

    }


    function getBalance(address addr) public view returns(uint) {

        return balances[addr];

    }

}


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69983064/viewspace-2948725/,如需轉載,請註明出處,否則將追究法律責任。

相關文章