使用Crytic进行漏洞挖掘 - Trail of Bits博客
Crytic是我们用于发现智能合约缺陷的GitHub应用程序,它非常重要:它可以在无需人工干预的情况下检测安全问题,在您工作时提供持续保证,并在部署前保护您的代码库。
Crytic发现许多其他工具无法检测的漏洞,包括一些尚未广为人知的问题。目前,Crytic拥有90多个检测器,并且我们持续添加新的检查并改进现有检测器。它在每次提交时运行这些漏洞和优化检测器,并评估您自己添加的自定义安全属性!
今天,我们将分享Crytic在主要项目中发现的12个问题,其中包括一些高严重性的漏洞。
以下是我们发现的问题。继续阅读以了解我们在哪里发现了它们:
- 未使用的返回值可能允许免费代币铸造
- registerVerificationKey始终返回空字节
- MerkleTree.treeHeight和MerkleTree.zero可以是常量
- 修饰符可以返回默认值
- 危险的严格相等可能使合约陷入陷阱
- ABI encodePacked碰撞
- 缺少继承容易出错
- Msg.value在fundWithAward中被使用两次
- 重入可能导致代币被盗
- [待确认]
- [待确认]
- [待确认]
Ernst & Young Nightfall
Crytic在EY的Nightfall项目中发现了三个漏洞,包括一个可能允许任何人铸造免费代币的关键漏洞。
问题1:未使用的返回值可能允许铸造免费代币
描述 FTokenShield.mint不检查transferFrom的返回值。如果FTokenShield与在错误转账时不回退、仅返回false的代币(例如BAT)一起使用,任何人都可以免费铸造承诺——攻击者可以免费铸造代币。
在FTokenShield.burn中存在一个影响较小的类似问题:这里不检查fToken.transfer。
建议 检查transferFrom和transfer的返回值。
Crytic报告
问题2:registerVerificationKey始终返回空字节
描述 FTokenShield.registerVerificationKey和NFTokenShield.registerVerificationKey返回一个空的bytes32。不清楚正确的返回值应该是什么。这可能导致第三方和合约出现意外行为。
建议 考虑返回一个值,或从签名中移除返回值。
Crytic报告
问题3:MerkleTree.treeHeight和MerkleTree.zero可以是常量
描述 treeHeight和zero可以在MerkleTree.sol中声明为常量,以允许编译器优化此代码。
建议 永不更改的状态变量可以声明为常量以节省gas。
Crytic报告
DeFiStrategies
Crytic在DeFiStrategies中发现了一个不寻常的问题:修饰符中缺少占位符执行导致调用函数返回默认值。此外,Crytic发现了一个与balanceOf调用返回值的严格相等相关的问题。
问题4:修饰符可以返回默认值
描述 SuperSaverZap.onlyInEmergency()和SuperSaverZap.stopInEmergency()修饰符在无效访问时不回退。如果修饰符不执行或回退,函数的执行将返回默认值,这可能误导调用者。
建议 在两个修饰符中用require替换if()条件。
Crytic报告
问题5:危险的严格相等可能使合约陷入陷阱
描述 ERC20toUniPoolZapV1_General.addLiquidity对_UniSwapExchangeContractAddress余额使用严格相等。此行为可能允许攻击者通过向_UniSwapExchangeContractAddress发送代币来使合约陷入陷阱。
建议 在比较中将==改为<=。
Crytic报告
DOSNetwork
Crytic在DOSNetwork中发现了另一个不太为人所知的问题:如果使用多个动态参数调用abi.encodedPacked,它可能返回具有不同参数的相同值。
问题6:ABI encodePacked碰撞
描述 DOSProxy使用带有两个连续字符串(dataSource和selector)的encodePacked Solidity函数:eth-contracts/contracts/DOSProxy.sol。
此模式容易发生碰撞,其中具有不同dataSource和selector的两个调用可能导致相同的queryId(有关更多信息,请参阅Solidity文档)。
建议 不要在encodePacked中使用多个动态类型,并考虑首先使用keccak256对dataSource和selector进行哈希处理。
Crytic报告
ethereum-oasis/Baseline
Crytic在Baseline协议中发现了一个架构问题,其中包括:实现接口的合约未从其继承。
问题7:缺少继承容易出错
描述 Shield是ERC1820ImplementerInterface的实现,但它不从该接口继承。此行为容易出错,并可能阻止实现或接口正确更新。
建议 使Shield继承ERC1820ImplementerInterface。
Crytic报告
EthKids
Crytic在EthKids中发现了另一个不寻常的问题:this.balance包括当前交易的数量(msg.value),这可能导致不正确的值计算。
问题8:Msg.value在fundWithAward中被使用两次
描述 在fundWithAward中使用this.balance不考虑已由msg.value添加的值。因此,价格计算不正确。
fundWithAward通过使用msg.value调用calculateReward来计算代币数量:
|
|
calculateReward调用calculatePurchaseReturn,其中_reserveBalance是this.balance,_depositAmount是msg.value:
|
|
在calculatePurchaseReturn中,然后通过将_depositAmount(msg.value)添加到_reserveAmount(this.balance)来计算baseN:
|
|
msg.value已经存在于this.balance中。例如,如果this.balance在交易前是10 ether,而msg.value是1 eth,this.balance在交易期间将是11 ether。因此,msg.value被使用两次,calculatePurchaseReturn被错误计算。
建议 更改价格计算,使_reserveBalance不包括交易中发送的金额。
Crytic报告
HQ20
最后,Crytic在HQ20中发现了一个众所周知的问题:重入。如果合约与具有回调功能的代币(如ERC777)一起使用,则会发生此重入。这与最近的uniswap和lendf.me黑客攻击类似。
问题9:重入可能导致代币被盗
描述 Classifieds在外部代币上调用transferFrom,而不遵循检查-效果-交互模式。如果目标代币具有回调机制(例如ERC777代币),这会导致重入,攻击者可以利用此漏洞。
有两个方法存在重入问题:
- Classifieds.cancelTrade(uint256)
- Classifieds.executeTrade(uint256)
建议 遵循检查-效果-交互模式。
Crytic报告
立即开始使用Crytic!
Crytic可以保护您的代码库免受关键缺陷的影响,并帮助您设计更安全的代码。有什么不喜欢的呢?
立即注册Crytic。有问题吗?加入我们的Slack频道(#crytic)或在Twitter上关注@CryticCi。
如果您喜欢这篇文章,请分享: Twitter LinkedIn GitHub Mastodon Hacker News