合约安全:使用外部合约隐藏恶意代码
2022-12-07 本文已影响0人
梁帆
本文说的其实不算是漏洞,更像是一个项目方的后门,也是需要注意的,尤其是使用者需要警惕的。
这个案例告诉我们:开源给你看的合约代码,未必是实际执行的代码。
一、案例说明
合约开发者暴露给外部的合约如下:
contract Foo {
Bar bar;
constructor(address _bar) {
bar = Bar(_bar);
}
function callBar() public {
bar.log();
}
}
contract Bar {
event Log(string message);
function log() public {
emit Log("Bar was called");
}
}
这个Foo合约是给使用者操作的合约,如果我们单看这两个合约,似乎没看到什么问题。但是请注意这里的构造函数,初始化了Bar合约的地址,但是我们并不是非得传入Bar合约的地址,我们可以传入另外一个钓鱼合约的地址,如下:
contract Mal {
event Log(string message);
function log() public {
emit Log("Mal was called");
}
}
这个Mal合约,并没有暴露给外界,而且有和Bar合约一样的方法log
,这个合约部署完成后,Foo合约用Mal合约地址进行初始化,这样使用者在调用callBar
的时候,实际上调用的是Mal合约上的log方法。
这种后门是比较隐蔽的,如果没有看到它每一步的部署,很可能会被项目方骗。
二、预防方法
- 在构造函数中初始化新的合约
- 公开外部合同的地址,以便审查外部合同的代码
contract Foo {
Bar public bar;
constructor() public {
bar = new Bar();
}
function callBar() public {
bar.log();
}
}
可以看到改进后,bar
的可见性从默认的internal
变成了public
;此外,constructor也不用合约地址初始化了,而是在里面new
了Bar合约,这样就保证bar变量确实是Bar合约的地址,自行证明了合约的清白性。