分享一个外部账户 EOA 漏洞案例。
以太坊的账户类型分为 外部账户 和 合约账户 两种类型。所谓外部账户,又称为 EOA(External Owned
Account),是指由用户掌握私钥的地址(用户的钱包地址一般为这种类型),而合约账户是部署到链上创建出的合约地址(多签账户的地址一般为这种类型)。
看下面的这段合约代码:
contract OnForEOA {
uint public flag;
// bad
modifier isNotContract(address _a){
uint len;
assemb { 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 {
OnForEOA c=OnForEOA(_a);
c.setFlag(1);
}
}
在 FakeEOA 攻击合约中,我们传入 _a 一个合约地址(准确来说是一个尚未生成的合约地址)。因为该合约地址尚未生成,当代码检查其
extcodesize 时,其结果为0。这样合约地址也被 OnForEOA 所接受了,这就是 OnForEOA 的漏洞所在。
在以太坊中,合约地址的产生是可以预先计算的。其计算逻辑为:根据发送者的地址,和交易 nonce 来确定。算法如下:
合约地址=address(uint160(uint256(keccak256(abi.encodePacked(byte(0xd6),
byte(0x94), _origin, byte(nonce))))))
讲解解决?
要解决这个问题,我们可以加上 tx.origin 的判断,如下:
require(tx.origin==msg.sender);
这样可以保证是外部账户直接调用。
另外对于 tx.origin 使用要注意相关可能的陷阱,详见之前的文章:安全|一段代码讲解偷走你的币。
免责声明: 文章源于会员发布,不作为任何投资建议,如有侵权请联系我们删除!
文章标题:以太坊EOA检查和漏洞是什么意思?
文章链接:https://www.btchangqing.cn/457930.html
更新时间:2023年02月07日
本站大部分内容均收集于网络,若内容若侵犯到您的权益,请联系我们,我们将第一时间处理。