danagrozav98-design/smart-contract-audits
GitHub: danagrozav98-design/smart-contract-audits
一个以太坊智能合约安全审计案例库,记录了真实漏洞的发现过程、可复现 PoC 与修复方案。
Stars: 0 | Forks: 0
专注于基于 EVM 协议的独立安全研究员。我会在攻击者之前攻破 Solidity 代码 —— 包括重入、算术漏洞、访问控制和经济套利。报告包含可复现的 PoC 和具体的修复方案。
audits_completed= 27
critical_findings= 14
tvl_secured= $38M+
##
项目与审计
audit_#01
·
严重
StakingVault.sol — 收益挖矿协议
对管理约 420 万美元 TVL 及 ERC-20 奖励的质押合约进行审计。
▸ 背景
该协议允许用户存入 LP token 并领取按比例分配的奖励。在审查 `withdraw()` 期间,发现外部调用在状态变更之前执行 —— 这是一个教科书式的 Checks-Effects-Interactions(检查-生效-交互)违规。恶意合约可以通过递归重入并抽干金库。
▸ 存在漏洞的代码
vulnerable.sol
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount, "insufficient");
```
// ❌ External call before state update
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "transfer failed");
balances[msg.sender] -= amount;
totalStaked -= amount;
```
}
⚠
发现问题:重入漏洞 (SWC-107)
底层的 `call` 在更新内部账本之前将 ETH 转移给了 `msg.sender`。攻击者部署一个带有恶意 `receive()` 的合约,可以在 `balances[msg.sender]` 仍然不为零时重入 `withdraw()`,循环抽干资金直到金库被清空。
▸ 修复方案
patched.sol
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount, "insufficient");
```
// ✅ Effects first
balances[msg.sender] -= amount;
totalStaked -= amount;
// ✅ Interaction last
(bool ok, ) = msg.sender.call{value: amount}("");
require(ok, "transfer failed");
```
}
✓
已应用修复
应用了 Checks-Effects-Interactions 模式,并使用 OpenZeppelin 的 `ReentrancyGuard` 包装了该函数。状态在任何外部调用之前完成变更,从而关闭了重入窗口。通过 Foundry PoC 验证,修复后该调用会回滚。
audit_#02
·
高危
TokenSale.sol — ICO 分发合约
对 Solidity ^0.7.6 环境下的固定价格代币销售合约进行预发布审计。
▸ 背景
该销售合约接受 ETH 并使用 `rate` 乘数按发送金额比例铸造代币。在 Solidity 0.7.x 下,算术运算默认是不受检查的 —— `buy()` 中的乘法在输入较大数值时可能会静默溢出,允许攻击者几乎免费地铸造代币。
▸ 存在漏洞的代码
vulnerable.sol
pragma solidity ^0.7.6;
function buy() external payable {
require(msg.value > 0, "zero value");
```
// ❌ Unchecked multiplication
uint256 tokens = msg.value * rate;
balances[msg.sender] += tokens;
totalSold += tokens;
```
}
⚠
发现问题:整数溢出 (SWC-101)
在 Solidity <0.8.0 中,`msg.value * rate` 在溢出时会静默回绕。精心构造的 `msg.value` 结合较大的 `rate` 会使 `tokens` 回绕为一个极小的值,而 `msg.sender` 依然能获得积分 —— 或者更糟的是,如果在其他地方的算术逻辑是反向的,它可能会回绕成一个巨大的值。这两种情况都会破坏销售合约的不变量。
▸ 修复方案
patched.sol
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/utils/math/Math.sol";
function buy() external payable {
require(msg.value > 0, "zero value");
```
// ✅ Solidity 0.8+ reverts on overflow
uint256 tokens = msg.value * rate;
require(totalSold + tokens <= HARD_CAP, "cap reached");
balances[msg.sender] += tokens;
totalSold += tokens;
```
}
✓
已应用修复
将 pragma 版本提升至 ^0.8.20,确保所有算术运算在溢出时回滚,并添加了明确的 `HARD_CAP` 不变量。Foundry 中的属性测试 (`forge test --fuzz-runs 10000`) 证实在整个输入空间中不存在可触达的溢出。
audit_#03
·
高危
GovernanceProxy.sol — DAO 投票模块
对将投票权委托给策略合约的 DAO 治理代理进行审计。
▸ 背景
该代理使用 `tx.origin` 来代表调用者授权特权操作。只要用户通过任何中介合约路由交易(包括 multisig、元交易 relayer 或账户抽象钱包),这种模式就会失效,并且很容易通过网络钓鱼进行利用。
▸ 存在漏洞的代码
vulnerable.sol
function executeProposal(uint256 id) external {
// ❌ tx.origin authorization
require(tx.origin == admin, "not admin");
```
Proposal storage p = proposals[id];
(bool ok, ) = p.target.call(p.data);
require(ok, "call failed");
```
}
⚠
发现问题:tx.origin 身份验证 (SWC-115)
`tx.origin` 是发起了该交易的 EOA,而不是直接调用者。如果 admin 被诱骗调用了恶意合约,该合约就可以调用 `executeProposal` 并通过 `tx.origin` 检查,从而代表 admin 执行操作。这也会导致基于 multisig 和 AA 的 admin 完全失效。
▸ 修复方案
patched.sol
function executeProposal(uint256 id) external {
// ✅ msg.sender + role-based access
require(hasRole(EXECUTOR_ROLE, msg.sender), "unauthorized");
```
Proposal storage p = proposals[id];
require(p.eta <= block.timestamp, "timelock");
(bool ok, ) = p.target.call(p.data);
require(ok, "call failed");
```
}
✓
已应用修复
使用 OpenZeppelin `AccessControl` 背后的 `msg.sender` 替换了 `tx.origin`,添加了 timelock (`p.eta`) 使得排队的提案无法立即执行,并将 admin 所有权转移至 Gnosis Safe multisig 背后。
##
技能
$ cat skills.json
Solidity
Foundry
Hardhat
Web3.js
Ethers.js
Slither
Echidna
EVM
OpenZeppelin
Fuzzing
形式化验证
##
关于我
在过去的几年里,我审计了质押金库、DEX 聚合器、NFT 市场和 DAO 治理模块。我的工作流高度依赖测试驱动:每一项发现都包含 Foundry PoC、书面影响评估和具体的补丁 —— 而不仅仅是 Slither 的转储输出。
不进行审计时,我会编写不变量、为开源工具做贡献,并指导开发者掌握防御性 Solidity 模式。
标签:EVM, Solidity, 区块链安全, 智能合约, 漏洞分析, 路径探测