跳转至

Short Address Attack

原理

短地址攻擊,利用 EVM 在參數長度不夠時自動在右方補 0 的特性,通過去除錢包地址末位的 0,達到將轉賬金額左移放大的效果。

例子

pragma solidity ^0.4.10;

contract Coin {
    address owner;
    mapping (address => uint256) public balances;

    modifier OwnerOnly() { require(msg.sender == owner); _; }

    function ICoin() { owner = msg.sender; }
    function approve(address _to, uint256 _amount) OwnerOnly { balances[_to] += _amount; }
    function transfer(address _to, uint256 _amount) {
        require(balances[msg.sender] > _amount);
        balances[msg.sender] -= _amount;
        balances[_to] += _amount;
    }
}

具體代幣功能的合約 Coin,當 A 賬戶向 B 賬戶轉代幣時調用 transfer() 函數,例如 A 賬戶(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c)向 B 賬戶(0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db)轉 8 個 Coin,msg.data 數據爲:

0xa9059cbb  -> bytes4(keccak256("transfer(address,uint256)")) 函數簽名
0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2db  -> B 賬戶地址(前補 0 補齊 32 字節)
0000000000000000000000000000000000000000000000000000000000000008  -> 0x8(前補 0 補齊 32 字節)

那麼短地址攻擊是怎麼做的呢,攻擊者找到一個末尾是 00 賬戶地址,假設爲 0x4b0897b0513fdc7c541b6d9d7e929c4e5364d200,那麼正常情況下整個調用的 msg.data 應該爲:

0xa9059cbb  -> bytes4(keccak256("transfer(address,uint256)")) 函數簽名
0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d200  -> B 賬戶地址(注意末尾 00)
0000000000000000000000000000000000000000000000000000000000000008  -> 0x8(前補 0 補齊 32 字節)

但是如果我們將 B 地址的 00 喫掉,不進行傳遞,也就是說我們少傳遞 1 個字節變成 4+31+32:

0xa9059cbb  -> bytes4(keccak256("transfer(address,uint256)")) 函數簽名
0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d2  -> B 地址(31 字節)
0000000000000000000000000000000000000000000000000000000000000008  -> 0x8(前補 0 補齊 32 字節)

當上面數據進入 EVM 進行處理時,對參數進行編碼對齊後補 00 變爲:

0xa9059cbb
0000000000000000000000004b0897b0513fdc7c541b6d9d7e929c4e5364d200
0000000000000000000000000000000000000000000000000000000000000800

也就是說,惡意構造的 msg.data 通過 EVM 解析補 0 操作,導致原本 0x8 = 8 變爲了 0x800 = 2048

上述 EVM 對畸形字節的 msg.data 進行補位操作的行爲其實就是短地址攻擊的原理

題目

這個目前沒有題目,基本已經被修復。不過可以復現成功,但是不能通過 Remix 復現,因爲客戶端會檢查地址長度;也不能通過 sendTransaction(),因爲 web3 中也加了保護。

但是,可以使用 geth 搭建私鏈,使用 sendRawTransaction() 發送交易復現,可自行嘗試。

Note

注:目前主要依靠客戶端主動檢查地址長度來避免該問題,另外 web3 層面也增加了參數格式校驗。雖然 EVM 層仍然可以復現,但是在實際應用場景中基本沒有問題。