文章总结: 本文解析以太坊短地址攻击原理,阐述EVM自动补全机制致转账金额放大漏洞。文章拆解攻击链构造与Golem事件案例,提供静态检测、机器学习检测及动态测试方案。建议通过合约层验证地址长度、使用SafeERC20库及交易所后端校验进行防御,警惕历史漏洞变种。 综合评分: 91 文章分类: 漏洞分析,区块链安全,代码审计
一个字节缺失,转账金额放大256倍:短地址攻击的数学魔术
原创
梦到什么写什么 梦到什么写什么
逍遥子讲安全
2026年2月24日 21:26 广东
一个字节的缺失,让转账金额凭空放大256倍——这不是魔术,是EVM自动补全机制下的精确数学。
2017年,Golem项目首次披露了一个影响Poloniex等主流交易所的漏洞:攻击者通过构造畸形的地址参数,成功从交易所窃取了大量ERC20代币。问题的根源不在合约逻辑,而在EVM对输入数据的自动补全机制。
本文将完整拆解短地址攻击的底层原理、攻击链构造、实战案例以及自动化审计思路。全是干到拧不出水的干货。
第一章 短地址攻击的底层原理:EVM的“善意”如何被利用
1.1 核心漏洞点:自动补全机制
以太坊虚拟机(EVM)在处理交易数据时,遵循严格的ABI编码规范。当外部调用智能合约函数时,交易数据按以下格式编码:
函数选择器(4字节) + 参数1(32字节) + 参数2(32字节) + ...
对于ERC20的transfer(address to, uint256 amount)函数,正常调用的编码数据为:
- 4字节:
0xa9059cbb(transfer的函数选择器) - 32字节:接收地址(实际只使用后20字节,前12字节填充0)
- 32字节:转账金额
关键点:当某个参数(如地址)的实际数据长度不足32字节时,EVM会自动在低位(右侧)进行补0,凑足32字节。
1.2 攻击的核心:地址缺失引发的“数据偏移”
短地址攻击的巧妙之处在于:攻击者故意提供一个少于20字节的地址参数,触发EVM的自动补全机制,导致原本属于金额的字节被“拉”到地址段补齐,而金额段缺失的字节则被补0——最终结果是金额被乘以16^(缺失字节数×2)。
正常调用数据包结构:
[函数选择器] [12字节填充0 + 20字节地址] [32字节金额]
攻击调用数据包结构(假设地址缺失2字节):
[函数选择器] [12字节填充0 + 18字节地址 + 2字节缺失] [30字节金额 + 2字节缺失]
EVM处理时:
- 检测到地址参数不足32字节,从紧随其后的金额参数中“借用”2字节补到地址段尾部
- 金额参数现在只有30字节,EVM自动在金额参数的尾部补2个0
- 最终结果:金额被扩大了256倍(2字节=16^2=256)
数学表达:
- 正常转账:
transfer(0xAddress, 100) - 攻击转账:
transfer(0xAddre, 100)(假设地址缺失1字节) - 实际执行:
transfer(0xAddress0, 100 * 16)(金额被乘16)
第二章 攻击链的完整构造
2.1 寻找符合条件的地址
攻击需要找到一个以若干个0结尾的地址。地址越界攻击的核心是:提供的地址末尾缺失的字节数正好是0,这样从金额段“借用”的字节会被解释为金额的一部分。
构造方法: 攻击者可以通过暴力生成以太坊地址,寻找以00、0000等结尾的地址。这类地址可以提前生成,并在攻击前准备好。
2.2 攻击合约示例
漏洞合约(存在短地址风险):
soliditypragma solidity ^0.4.11;contract TestToken { mapping (address => uint) balances;
function TestToken() { balances[tx.origin] = 10000; }
function sendCoin(address to, uint amount) returns(bool sufficient) { if(balances[msg.sender] < amount) return false; balances[msg.sender] -= amount; balances[to] += amount; return true; }
function getBalance(address addr) constant returns(uint) { return balances[addr]; }}
2.3 攻击数据构造
假设攻击者找到了以两个0结尾的地址:0x254d383ab537ebeab73f816df8e1598f1321bc00
攻击者调用sendCoin时,传入的地址参数截掉最后两个字节,即:
textto = 0x254d383ab537ebeab73f816df8e1598f1321bcamount = 1
构造的交易数据:
函数选择器:0xb90b98a1(sendCoin的哈希前4字节)地址参数:0x000000000000000000000000254d383ab537ebeab73f816df8e1598f1321bc(仅19字节?此处示例数据需精确)金额参数:0x0000000000000000000000000000000000000000000000000000000000000001
实际发送的数据包(长度134字节,比正常少2字节):
0xb90b98a1 + 000...0254d383ab537ebeab73f816df8e1598f1321bc + 000...0001
EVM解析时:
- 地址参数应为32字节,但实际只有30字节(地址部分实际19字节?需要精确计算)
- 从金额参数“借用”2字节补到地址段
- 金额参数剩余30字节,尾部自动补2个0
- 最终执行的转账金额:1 × 16^2 = 256个代币
攻击效果:攻击者只支付了1个代币,却成功转账256个代币。
第三章 真实案例分析:Golem事件与交易所劫持
3.1 Golem项目披露的漏洞
2017年4月,Golem项目发布了一篇博文,详细描述了一个影响Poloniex等交易所的安全漏洞。问题在于:当某些交易所处理ERC20代币交易时,没有对账户地址长度进行输入验证。
攻击场景:
- 攻击者在交易所创建提币请求,目标地址是一个精心构造的短地址(如
0x1234...00,末尾含0) - 交易所前端正常显示提币金额(如100个GNT)
- 后端构造交易数据时,直接拼接用户输入的地址字符串
- 由于地址末尾的0被截断,触发EVM自动补全
- 实际链上转账金额变为100 × 256 = 25600个GNT
后果:攻击者可以盗取交易所热钱包中的大量用户代币。
3.2 为什么交易所成为重灾区
交易所成为短地址攻击主要目标的原因:
- 批量处理:交易所往往批量处理提币请求,不易对每个地址进行严格校验
- 前端截断:前端显示地址时可能自动省略末尾的0,用户不易察觉异常
- 后端拼接:后端直接拼接用户输入的字符串,未进行规范化处理
3.3 漏洞修复后的残存风险
即使在EVM层面修复后,短地址攻击仍然存在变种:
- 多层嵌套调用:通过多层合约调用,可能在某一层触发补全
- 跨链桥:不同链对地址长度的处理不一致,可能在跨链时触发类似问题
第四章 深度利用:从短地址到价值倍增
4.1 金额放大倍数计算表
| 地址缺失字节数 | 缺失16进制位数 | 金额放大倍数 | 攻击效果 | | — | — | — | — | | 1字节 | 2位 | 16² = 256倍 | 1 → 256 | | 2字节 | 4位 | 16⁴ = 65536倍 | 1 → 65536 | | 3字节 | 6位 | 16⁶ = 16777216倍 | 1 → 1.67千万 | | 4字节 | 8位 | 16⁸ ≈ 4.29亿倍 | 1 → 4.29亿 |
实际限制:
- 地址以0结尾的概率:每256个地址出现1个(1字节0)
- 地址以00结尾的概率:每65536个地址出现1个(2字节0)
- 地址以000结尾的概率:每1677万个地址出现1个(3字节0)
攻击者需要提前生成大量地址,筛选符合条件的短地址备用。
4.2 组合攻击:短地址 + 整数溢出
在某些旧版合约中,如果同时存在整数溢出漏洞,攻击效果可能进一步放大:
solidity
// 漏洞组合示例function batchTransfer(address[] _receivers, uint256 _value) { uint cnt = _receivers.length; uint256 amount = cnt * _value; // 可能溢出 require(balances[msg.sender] >= amount); // ...}
攻击者可以通过短地址攻击放大_value,再结合整数溢出,可能实现无限铸币。
第五章 自动化审计与检测
5.1 静态代码检测规则
在合约审计中,可以通过以下规则发现潜在的短地址风险:
py# 审计规则示例def check_short_address_vulnerability(contract_code): red_flags = []
# 规则1:是否直接使用外部传入的地址参数 if re.search(r"function\s+\w+\s*\([^)]*address[^)]*\)", contract_code): # 检查是否有长度验证 if not re.search(r"\.length\s*[=!]=\s*20", contract_code): red_flags.append("地址参数未进行长度验证")
# 规则2:是否使用assembly操作地址 if "assembly" in contract_code and "address" in contract_code: red_flags.append("在汇编中操作地址,存在短地址风险")
return red_flags
5.2 基于机器学习的检测
近期研究显示,通过提取操作码和trigram特征,机器学习模型可以高效检测短地址漏洞。
| 算法 | 准确率 | 精确率 | 召回率 | F1分数 | | — | — | — | — | — | | 梯度提升 | 97.84% | 98.21% | 98.05% | 98.13% | | 随机森林 | 96.72% | 97.13% | 96.88% | 97.00% | | SVM | 94.35% | 94.87% | 94.21% | 94.54% |
特征提取方法:
- 操作码序列:提取合约字节码中的操作码序列
- Trigram特征:将操作码序列分割为3-gram,统计频率
- 控制流图特征:提取CFG中的异常路径
5.3 动态测试工具
py# 短地址攻击测试脚本(概念验证)import web3from web3 import Web3
def test_short_address_attack(contract_address, target_address, amount): w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_KEY'))
# 构造短地址(假设target_address以00结尾) short_address = target_address[:-2] # 去掉最后两个字符
# 构造交易数据 transfer_signature = w3.keccak(text="transfer(address,uint256)")[:4].hex() address_param = short_address[2:].rjust(64, '0') # 补足64位(32字节) amount_param = hex(amount)[2:].rjust(64, '0')
data = '0x' + transfer_signature + address_param + amount_param
# 发送测试交易(需在测试网进行) tx = { 'to': contract_address, 'data': data, 'gas': 100000, 'gasPrice': w3.eth.gas_price }
return tx
第六章 防御方案与最佳实践
6.1 合约层的防御
方案1:显式验证地址长度
solidityfunction transfer(address to, uint256 amount) public returns (bool) { // 验证地址长度为20字节 require(to != address(0), "Invalid address"); require(to == address(uint160(to)), "Address length mismatch"); // 或者直接信任Solidity的类型系统,但避免使用assembly _transfer(msg.sender, to, amount); return true;}
方案2:使用SafeERC20库
OpenZeppelin的SafeERC20库通过封装ERC20操作,增加了安全检查:
solidityimport "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";contract Vault { using SafeERC20 for IERC20;
function safeTransfer(IERC20 token, address to, uint256 amount) internal { token.safeTransfer(to, amount); // SafeERC20会自动处理地址验证 }}
6.2 钱包/交易所层的防御
| 防御层级 | 具体措施 | 实施建议 | | — | — | — | | 前端 | 地址规范化显示 | 确保地址以完整40字符显示,不省略末尾0 | | 后端 | 地址参数校验 | 严格验证地址字符串长度为42(含0x) | | 交易构造 | 使用ABI编码库 | 使用web3.js/ethers.js的标准ABI编码,不手动拼接 | | 监控 | 异常交易检测 | 监控金额异常的转账交易,特别是小额测试后的大额攻击 |
6.3 EVM层的演进
随着Solidity版本升级和EVM优化,短地址攻击在最新版本中已被有效缓解:
- Solidity 0.8.x:增强了ABI编码的严格性
- 编译器层面:自动插入长度检查
- 但历史合约和跨链交互仍存在风险,审计时必须关注
第七章 结语:一个字节的教训
短地址攻击的精髓在于:它利用了系统的“善意”——EVM的自动补全机制——将其转化为攻击武器。一个字节的缺失,通过精心构造的攻击链,最终导致金额的指数级放大。
这类漏洞给我们的启示:
- 永远不要信任输入数据的长度:即使EVM有自动补全机制,也必须在合约层显式验证
- 警惕“善意”的系统行为:自动补全、默认值、隐式转换都可能成为攻击向量
- 历史漏洞的变种:短地址攻击虽然“古老”,但在跨链、Layer2等新场景中可能以新面目出现
在合约审计中,短地址漏洞必须作为高危项优先排查。一个转账函数,可能就是攻击者撬动整个资金池的支点。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:逍遥子讲安全 梦到什么写什么 梦到什么写什么《一个字节缺失,转账金额放大256倍:短地址攻击的数学魔术》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论