文章来源: 作者: 发布时间:2023-03-30 15:50:00
分享一个外部账户 EOA 漏洞案例。
以太坊的账户类型分为 外部账户 和 合约账户 两种类型。所谓外部账户,又称为 EOA(Externally Owned
Account),是指由用户掌握私钥的地址(用户的钱包地址一般为这种类型),而合约账户是部署到链上创建出的合约地址(多签账户的地址一般为这种类型)。
看下面的这段合约代码:
contract OnlyForEOA {
uint public flag;
// bad
modifier isNotContract(address _a){
uint len;
assembly { len :=extcodesize(_a) }
require(len==0);
_;
}
function setFlag(uint i) public isNotContract(msg.sender){
flag=i;
}
}
extcodesize 操作符返回某个地址关联的代码(code)长度,如果代码长度为0,表示为外部地址。而大于0表示为合约地址。
这段代码的本意是只允许 EOA 账户对该合约进行操作,也就是说普通用户可以直接用外部地址发起合约调用,而不允许用另一个合约进行调用。
那么上面这些代码的漏洞在哪里呢?会智能合约编程的同学可以稍微思考几分钟,再往下看。
下面的我们来部署一个攻击合约:
contract FakeEOA {
constructor(address _a) public {
OnlyForEOA c=OnlyForEOA(_a);
c.setFlag(1);
}
}
在 FakeEOA 攻击合约中,我们传入 _a 一个合约地址(准确来说是一个尚未生成的合约地址)。因为该合约地址尚未生成,当代码检查其
extcodesize 时,其结果为0。这样合约地址也被 OnlyForEOA 所接受了,这就是 OnlyForEOA 的漏洞所在。
在以太坊中,合约地址的产生是可以预先计算的。其计算逻辑为:根据发送者的地址,和交易 nonce 来确定。算法如下:
合约地址=address(uint160(uint256(keccak256(abi.encodePacked(byte(0xd6),
byte(0x94), _origin, byte(nonce))))))
讲解解决?
要解决这个问题,我们可以加上 tx.origin 的判断,如下:
require(tx.origin==msg.sender);
这样可以保证是外部账户直接调用。
另外对于 tx.origin 使用要注意相关可能的陷阱,详见之前的文章:安全|一段代码讲解偷走你的币。
韩国屎币真实行情 - 韩国加密货币屎币价格实时变动
云乐网 · 2024-05-31 16:01:00
越南狗币2024年官方最新消息 - 越南将于2024年正式发行数字货币:狗币
云乐网 · 2024-05-31 16:01:00
派币在美国等于多少美元呢 - 派币在美国等于多少美元汇率
云乐网 · 2024-05-31 16:01:00
运营虚拟货币项目违法吗 - 运营虚拟币项目是否违法?
云乐网 · 2024-05-31 16:01:00
马云炒比特币赚了多少钱 - 马云通过比特币获利多少?
云乐网 · 2024-05-31 16:01:00
shib货币什么时候上市的 - shib货币将于何时上市?
云乐网 · 2024-05-31 16:01:00