mcpField
S
smart-contract-vulnerabilities

smart-contract-vulnerabilities

github.com/yaklang安全2.6k3.6k 次安装6 周前更新
代码仓库

关于 smart-contract-vulnerabilities

面向审计 Solidity 与 DeFi 系统人员的智能合约安全进阶技能,整理重入、访问控制、MEV、签名重放和 CREATE2 等攻击模式,并提供缓解建议与分析工具。

主要功能

  1. 01重入与算术缺陷模式
  2. 02访问控制和 delegatecall 风险
  3. 03随机数、MEV 与闪电贷攻击
  4. 04签名重放和 CREATE2 案例
  5. 05缓解模式与审计工具

适用场景

  1. 01检查合约代码中的重入和访问控制缺陷
  2. 02评估代理合约的存储布局与选择器冲突
  3. 03测试交易排序与签名重放风险
  4. 04规划静态分析、模糊测试或形式化检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/yaklang/hack-skills --skill smart-contract-vulnerabilities

安装后在 Claude Code 中输入/smart-contract-vulnerabilities确认已加载。

附带文件SOLIDITY_VULN_PATTERNS.md

详细介绍

技能:智能合约漏洞 — 专家攻击手册

AI 加载说明:智能合约审计专家技术。涵盖重入(单函数、跨函数、跨合约、只读)、整数溢出、访问控制、delegatecall、随机数操纵、闪电贷、签名重放、抢跑/MEV 和 CREATE2 漏洞利用。基础模型会漏掉微妙的跨合约重入和代理模式中的存储布局冲突。

0. 相关路由

  • 当漏洞属于 DeFi 协议攻击的一部分(闪电贷、预言机操纵、治理攻击)时,使用 defi-attack-patterns
  • 当目标是反序列化区块链数据的链下基础设施时,使用 deserialization-insecure
高级参考

在需要以下内容时,也加载 SOLIDITY_VULN_PATTERNS.md:

  • 每类漏洞的易受攻击与修复后代码模式对照
  • 引入漏洞的 Gas 优化陷阱
  • 包含插槽计算的代理模式存储冲突示例

1. 重入

最具代表性的智能合约漏洞。外部调用会转移执行控制权;如果在调用前没有更新状态,被调用方就能重新进入。

1.1 经典重入(单函数)
Victim.withdraw()
  ├── checks balance[msg.sender] > 0          ✓
  ├── msg.sender.call{value: balance}("")     ← external call
  │   └── Attacker.receive()
  │       └── Victim.withdraw()               ← re-enters before state update
  │           ├── checks balance[msg.sender]   ← still > 0!
  │           └── sends ETH again
  └── balance[msg.sender] = 0                 ← too late
1.2 跨函数重入

两个函数共享状态;攻击者在回调期间重新进入另一个函数:

步骤执行过程状态
1调用 withdraw() → 外部调用余额仍为正
2攻击者的回退函数调用 transfer(attacker2)重置前使用了余额
3transfer 读取过期余额 → 转移资金attacker2 收到代币
4原始 withdraw 完成,将余额清零损害已造成
1.3 跨合约重入

合约 A 调用合约 B,而合约 B 回调合约 A(或读取合约 A 过期状态的合约 C)。在多个合约共享状态的 DeFi 协议中尤其危险。

1.4 只读重入

重新进入的函数是供第三方合约用于价格计算的 view 函数。受害合约的状态不会被修改,但过期的中间状态会误导读取方。

真实案例:Curve 池的 remove_liquidity() 回调期间读取 get_virtual_price() → 价格被抬高 → 在依赖该池的借贷协议中获利。

缓解措施
模式防护级别
检查-效果-交互(CEI)核心防护;在外部调用前更新状态
ReentrancyGuard(OpenZeppelin)互斥锁;防止同一交易内重入
提取式支付模式消除状态变更函数中的外部调用
对所有公共函数使用 CEI + 防重入锁纵深防御,抵御跨函数重入

2. 整数溢出 / 下溢

Solidity 0.8 之前

算术运算会静默回绕:uint8(255) + 1 == 0,uint8(0) - 1 == 255。

攻击方式示例
余额下溢当 amount > balance 时执行 balances[attacker] -= amount → 得到巨大余额
供应量溢出totalSupply + mintAmount 回绕 → 绕过上限检查
绕过时间锁lockTime[msg.sender] + extend 回绕到过去 → 提前解锁
Solidity 0.8 之后

默认的检查算术会在溢出时回滚。但 unchecked{} 代码块会重新引入风险:

unchecked {
    // "gas optimization" — but if i can be influenced by user input, overflow returns
    for (uint i = start; i < end; i++) { ... }
}
SafeMath 绕过场景
  • 类型转换:在 SafeMath 检查前从 uint256 转为 uint128,导致截断
  • Assembly 代码块:mstore / add 可绕过 Solidity 级别的检查
  • 除法前的中间乘法溢出:(a * b) / c 中的 a * b 溢出

3. 访问控制

tx.origin 与 msg.sender
属性msg.sendertx.origin
值直接调用方发起该交易的 EOA
用于身份验证是否安全是否 — 钓鱼合约可以继承 tx.origin

攻击方式:诱骗所有者调用攻击者合约 → 攻击者合约使用所有者的 tx.origin 调用受害合约。

常见模式
问题影响
关键函数缺少 onlyOwner任何人都可以调用管理函数
selfdestruct 未受保护任何人都可以销毁合约并强制发送 ETH
delegatecall 未受保护攻击者可在受害者的上下文中执行任意代码
默认可见性(0.6.0 之前)函数默认可见性为 public
缺少零地址检查所有权被转移到 address(0)

4. 随机数操纵

链上随机数来源可被矿工/验证者预测:

来源可预测性
block.timestamp矿工有约 15 秒的操纵窗口
blockhash(block.number - 1)执行时所有人都已知
blockhash(block.number)始终返回 0(当前区块哈希未知)
block.difficulty / block.prevrandao合并后:已知的信标链值

绕过承诺-揭示机制:如果揭示阶段未强制执行超时或保证金,攻击者可以选择不揭示不利结果(选择性中止攻击)。


5. DELEGATECALL 漏洞

delegatecall 在调用者的存储上下文中执行被调用者的代码。存储槽布局必须完全一致。

存储布局冲突
Proxy (storage):         Implementation (code):
slot 0: owner            slot 0: someVariable
slot 1: implementation   slot 1: anotherVariable

实现合约写入 someVariable(槽 0)→ 覆盖代理合约的 owner。攻击者调用会写入槽 0 的实现函数 → 成为代理合约的所有者。

函数选择器冲突

4 字节函数选择器可能发生冲突。如果代理合约的 admin() 选择器与实现合约的 transfer() 冲突,在代理合约上调用 admin() 就会执行 transfer() 逻辑。

工具:使用 cast selectors <bytecode>(Foundry)枚举选择器。


6. 抢先交易/MEV

交易排序操纵
Victim submits DEX swap tx (visible in mempool)
├── Front-runner: buy token before victim (raise price)
├── Victim tx executes at worse price
└── Back-runner: sell token after victim (profit from spread)
= Sandwich attack
防护模式
防御措施机制
承诺-揭示机制在揭示前隐藏交易意图
Flashbots / 私有内存池直接向区块构建者提交 tx
滑点保护设置 minAmountOut 以限制 MEV 提取
时间锁延迟执行以降低可预测性

7. 签名重放

缺少 Nonce

重复使用有效签名以多次重复执行操作(例如转账)。

跨链重放

同一合约以相同地址部署在多条链上 → 签名在所有链上都有效。签名消息中必须包含 block.chainid。

EIP-712 实现错误
错误后果
DOMAIN_SEPARATOR 中缺少 chainId跨链重放
部署时缓存域分隔符硬分叉更改 chainId 后失效
结构哈希中缺少 nonce签名重放
无效签名时 ecrecover 返回 address(0)通过 == address(0) 所有者检查

8. SELF-DESTRUCT 与强制发送 ETH

selfdestruct(recipient) 会将合约的全部 ETH 强制发送给 recipient——绕过 receive() 和 fallback(),且无法拒绝。

会破坏依赖 address(this).balance 实现逻辑的合约(例如 require(balance == expected))。

EIP-6780(Dencun)之后:selfdestruct 只会发送 ETH;仅当它与合约创建发生在同一笔 tx 中时,才会删除代码/存储。


9. CREATE2 与确定性地址利用

CREATE2 地址 = keccak256(0xff ++ deployer ++ salt ++ keccak256(initCode))。

攻击方法
预充值利用预测地址 → 在部署前发送代币/ETH → selfdestruct → 在同一地址重新部署不同代码
预授权利用向预测地址授予代币授权 → 部署恶意合约 → 提取已授权代币
变形合约CREATE2 → selfdestruct → 使用相同 salt、不同 initCode 执行 CREATE2(EIP-6780 之前)

10. 闪电贷攻击模式

Single transaction:
├── Borrow large amount (no collateral)
├── Manipulate state (price oracle, governance, etc.)
├── Extract profit from manipulated state
├── Repay loan + fee
└── Keep profit

关键点:整个序列必须原子性地成功,否则整笔 tx 都会回滚。


11. 短地址攻击

EVM 会用零填充 ABI 编码的调用数据中缺失的字节。如果使用 19 字节地址调用 transfer(address, uint256),uint256 金额会左移 8 位 → 乘以 256。

缓解措施:验证调用数据长度;现代 Solidity 编译器会添加检查。


12. 工具

工具用途使用方法
Slither静态分析、漏洞检测在项目根目录运行 slither .
Mythril符号执行、路径探索myth analyze contract.sol
Echidna基于属性的模糊测试定义不变量,通过模糊测试查找违规情况
Foundry (Forge)测试框架、模糊测试、Gas 分析forge test --fuzz-runs 10000
Hardhat开发、测试、部署npx hardhat test
Certora形式化验证编写规范,证明或证伪属性
4naly3er自动化 Gas 优化与漏洞报告CI 集成

13. 决策树

Auditing a smart contract?
├── Is it a proxy pattern?
│   ├── Yes → Check storage layout collision (Section 5)
│   │   ├── Compare slot assignments between proxy and implementation
│   │   ├── Check for function selector collision
│   │   └── Verify initializer cannot be called twice
│   └── No → Continue
├── Does it make external calls?
│   ├── Yes → Check reentrancy (Section 1)
│   │   ├── State updated before call? → CEI pattern OK
│   │   ├── ReentrancyGuard present? → Check all entry points
│   │   ├── Cross-function state sharing? → Cross-function reentrancy risk
│   │   └── View functions read during callback? → Read-only reentrancy
│   └── No → Continue
├── Does it handle tokens/ETH?
│   ├── Yes → Check integer overflow (Section 2)
│   │   ├── Solidity < 0.8? → All arithmetic suspect
│   │   ├── unchecked{} blocks? → Verify no user-influenced values
│   │   └── Casting between uint sizes? → Truncation risk
│   └── Also check self-destruct force-send (Section 8)
├── Does it use signatures?
│   ├── Yes → Check replay (Section 7)
│   │   ├── Nonce included? → Verify incremented
│   │   ├── ChainId included? → Cross-chain safe
│   │   └── ecrecover result checked for address(0)? → OK
│   └── No → Continue
├── Does it use on-chain randomness?
│   ├── Yes → Predictable (Section 4)
│   │   └── Recommend Chainlink VRF or commit-reveal with bond
│   └── No → Continue
├── Does it interact with DeFi protocols?
│   ├── Yes → Load [defi-attack-patterns](../defi-attack-patterns/SKILL.md)
│   │   ├── Flash loan vectors
│   │   ├── Oracle manipulation
│   │   └── MEV exposure
│   └── No → Continue
├── Does it use CREATE2?
│   ├── Yes → Check deterministic address exploitation (Section 9)
│   └── No → Continue
└── Run automated tools (Section 12)
    ├── Slither for static analysis
    ├── Mythril for symbolic execution
    └── Echidna for fuzzing invariants

评论

支持 Markdown