smart-contract-vulnerabilities

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SKILL: Smart Contract Vulnerabilities — Expert Attack Playbook

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

AI LOAD INSTRUCTION: Expert smart contract audit techniques. Covers reentrancy (single, cross-function, cross-contract, read-only), integer overflow, access control, delegatecall, randomness manipulation, flash loans, signature replay, front-running/MEV, and CREATE2 exploitation. Base models miss subtle cross-contract reentrancy and storage layout collisions in proxy patterns.
AI加载说明:专业智能合约审计技术,涵盖重入(单函数、跨函数、跨合约、只读)、整数溢出、访问控制、delegatecall、随机数操纵、闪电贷、签名重放、抢先交易/MEV以及CREATE2漏洞利用。基础模型容易遗漏跨合约重入和代理模式中的存储布局冲突等细微问题。

0. RELATED ROUTING

0. 相关路由

  • defi-attack-patterns when the vulnerability is part of a DeFi protocol exploit (flash loans, oracle manipulation, governance attacks)
  • deserialization-insecure when the target is off-chain infrastructure deserializing blockchain data
  • DeFi攻击模式 适用于漏洞属于DeFi协议利用的场景(闪电贷、预言机操纵、治理攻击)
  • 不安全反序列化 适用于目标为反序列化区块链数据的链下基础设施场景

Advanced Reference

高级参考

Also load SOLIDITY_VULN_PATTERNS.md when you need:
  • Side-by-side vulnerable vs fixed code patterns for each vulnerability class
  • Gas optimization traps that introduce vulnerabilities
  • Proxy pattern storage collision examples with slot calculations

如需以下内容,请同时加载SOLIDITY_VULN_PATTERNS.md
  • 各类漏洞对应的易受攻击代码与修复代码的对照模式
  • 会引入漏洞的Gas优化陷阱
  • 附带插槽计算的代理模式存储冲突示例

1. REENTRANCY

1. 重入

The most iconic smart contract vulnerability. External calls transfer execution control; if state is not updated before the call, the callee can re-enter.
最典型的智能合约漏洞。外部调用会转移执行控制权;如果调用前未更新状态,被调用方可以重新进入合约。

1.1 Classic Reentrancy (Single-Function)

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
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 Cross-Function Reentrancy

1.2 跨函数重入

Two functions share state; attacker re-enters a different function during callback:
StepExecutionState
1Call
withdraw()
→ external call
balance still positive
2Attacker fallback calls
transfer(attacker2)
balance used before reset
3
transfer
reads stale balance → moves funds
attacker2 receives tokens
4Original
withdraw
completes, zeroes balance
damage done
两个函数共享状态;攻击者在回调期间重新进入另一个函数:
步骤执行过程状态
1调用
withdraw()
→ 触发外部调用
余额仍为正
2攻击者 fallback 函数调用
transfer(attacker2)
余额在重置前被使用
3
transfer
读取过期余额 → 转移资金
attacker2收到代币
4原始
withdraw
执行完成,将余额置零
损害已经造成

1.3 Cross-Contract Reentrancy

1.3 跨合约重入

Contract A calls Contract B, which calls back into Contract A (or Contract C that reads A's stale state). Especially dangerous in DeFi protocols where multiple contracts share state.
合约A调用合约B,合约B回调合约A(或者调用读取A过期状态的合约C)。在多合约共享状态的DeFi协议中风险极高。

1.4 Read-Only Reentrancy

1.4 只读重入

The re-entered function is a
view
function used by a third-party contract for price calculation. No state modification in the victim, but the stale intermediate state misleads the reader.
Real-world: Curve pool
get_virtual_price()
read during
remove_liquidity()
callback → inflated price → profit on dependent lending protocol.
被重入的函数是第三方合约用于价格计算的
view
函数。受害合约没有状态修改,但中间过期状态会误导读取方。
真实案例:Curve池
get_virtual_price()
remove_liquidity()
回调期间被读取 → 价格被高估 → 依赖该池的借贷协议产生损失。

Mitigations

修复方案

PatternProtection Level
Checks-Effects-Interactions (CEI)Core defense; update state before external call
ReentrancyGuard
(OpenZeppelin)
Mutex lock; prevents same-tx re-entry
Pull payment patternEliminate external calls in state-changing functions
CEI + guard on all public functionsDefense-in-depth against cross-function

模式防护等级
检查-效果-交互(CEI)核心防御;外部调用前先更新状态
ReentrancyGuard
(OpenZeppelin)
互斥锁;防止同一交易内重入
拉取支付模式消除状态变更函数中的外部调用
所有公开函数同时使用CEI+防护锁针对跨函数重入的纵深防御

2. INTEGER OVERFLOW / UNDERFLOW

2. 整数溢出/下溢

Pre-Solidity 0.8

Solidity 0.8之前版本

Arithmetic silently wraps:
uint8(255) + 1 == 0
,
uint8(0) - 1 == 255
.
AttackExample
Balance underflow
balances[attacker] -= amount
when amount > balance → huge balance
Supply overflow
totalSupply + mintAmount
wraps → bypass cap checks
Timelock bypass
lockTime[msg.sender] + extend
wraps to past → early unlock
算术运算会静默绕回:
uint8(255) + 1 == 0
uint8(0) - 1 == 255
攻击类型示例
余额下溢当amount大于余额时执行
balances[attacker] -= amount
→ 获得巨额余额
供应溢出
totalSupply + mintAmount
绕回 → 绕过总量上限检查
时间锁绕过
lockTime[msg.sender] + extend
绕回为过去时间 → 提前解锁

Post-Solidity 0.8

Solidity 0.8及之后版本

Default checked arithmetic reverts on overflow. But
unchecked{}
blocks reintroduce risk:
solidity
unchecked {
    // "gas optimization" — but if i can be influenced by user input, overflow returns
    for (uint i = start; i < end; i++) { ... }
}
默认开启算术检查,溢出时会回滚交易。但
unchecked{}
块会重新引入风险:
solidity
unchecked {
    // 「Gas优化」——但如果i可被用户输入影响,溢出风险会重新出现
    for (uint i = start; i < end; i++) { ... }
}

SafeMath Bypass Scenarios

SafeMath绕过场景

  • Casting:
    uint256
    uint128
    truncation before SafeMath check
  • Assembly blocks:
    mstore
    /
    add
    bypass Solidity-level checks
  • Intermediate multiplication overflow before division:
    (a * b) / c
    where
    a * b
    overflows

  • 类型转换:SafeMath检查前将
    uint256
    截断为
    uint128
  • 汇编块:
    mstore
    /
    add
    等操作绕过Solidity层面检查
  • 除法前的中间乘法溢出:
    (a * b) / c
    a * b
    先溢出

3. ACCESS CONTROL

3. 访问控制

tx.origin vs msg.sender

tx.origin vs msg.sender

Property
msg.sender
tx.origin
ValueImmediate callerEOA that initiated the tx
Safe for authYesNo — phishing contract can inherit tx.origin
Attack: trick owner into calling attacker contract → attacker contract calls victim with owner's
tx.origin
.
属性
msg.sender
tx.origin
直接调用方发起交易的EOA地址
是否适合用于鉴权 — 钓鱼合约可以继承tx.origin
攻击方式:诱导所有者调用攻击者合约 → 攻击者合约以所有者的
tx.origin
调用受害合约。

Common Patterns

常见问题

IssueImpact
Missing
onlyOwner
on critical functions
Anyone can call admin functions
Unprotected
selfdestruct
Anyone can destroy the contract, force-send ETH
Unprotected
delegatecall
Attacker executes arbitrary code in victim's context
Default visibility (pre-0.6.0)Functions default to
public
Missing zero-address checksOwnership transferred to
address(0)

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

4. RANDOMNESS MANIPULATION

4. 随机数操纵

On-chain randomness sources are predictable to miners/validators:
SourcePredictability
block.timestamp
Miner has ~15s window to manipulate
blockhash(block.number - 1)
Known to all at execution time
blockhash(block.number)
Always returns 0 (current block hash unknown)
block.difficulty
/
block.prevrandao
Post-merge: known beacon chain value
Commit-reveal bypass: If reveal phase doesn't enforce timeout or bond, attacker can choose not to reveal unfavorable outcomes (selective abort attack).

链上随机数来源对矿工/验证者是可预测的:
来源可预测性
block.timestamp
矿工有约15秒的操纵窗口
blockhash(block.number - 1)
执行时对所有节点可见
blockhash(block.number)
永远返回0(当前区块哈希未知)
block.difficulty
/
block.prevrandao
合并后:已知的信标链值
提交-揭示方案绕过:如果揭示阶段没有强制超时或保证金,攻击者可以选择不揭示不利结果(选择性中止攻击)。

5. DELEGATECALL VULNERABILITIES

5. DELEGATECALL漏洞

delegatecall
executes callee's code in caller's storage context. Storage slot layout must match exactly.
delegatecall
在调用方的存储上下文中执行被调用方的代码,存储插槽布局必须完全匹配。

Storage Layout Collision

存储布局冲突

Proxy (storage):         Implementation (code):
slot 0: owner            slot 0: someVariable
slot 1: implementation   slot 1: anotherVariable
Implementation writes to
someVariable
(slot 0) → overwrites proxy's
owner
. Attacker calls implementation function that writes slot 0 → becomes proxy owner.
代理(存储):         实现合约(代码):
slot 0: owner            slot 0: someVariable
slot 1: implementation   slot 1: anotherVariable
实现合约写入
someVariable
(插槽0)→ 覆盖代理的
owner
。攻击者调用实现合约中写入插槽0的函数 → 成为代理所有者。

Function Selector Collision

函数选择器冲突

4-byte function selectors can collide. If proxy's
admin()
selector collides with implementation's
transfer()
, calling
admin()
on the proxy executes
transfer()
logic.
Tool:
cast selectors <bytecode>
(Foundry) to enumerate selectors.

4字节函数选择器可能发生冲突。如果代理的
admin()
选择器与实现合约的
transfer()
冲突,调用代理的
admin()
会执行
transfer()
逻辑。
工具:
cast selectors <bytecode>
(Foundry)可枚举选择器。

6. FRONT-RUNNING / MEV

6. 抢先交易/MEV

Transaction Ordering Manipulation

交易顺序操纵

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
受害者提交DEX兑换交易(在内存池中可见)
├── 抢先交易者:在受害者交易前买入代币(推高价格)
├── 受害者交易以更差的价格执行
└── 后跑交易者:在受害者交易后卖出代币(赚取差价)
= 三明治攻击

Protection Patterns

防护模式

DefenseMechanism
Commit-revealHide transaction intent until reveal
Flashbots / private mempoolSubmit tx directly to block builder
Slippage protectionSet
minAmountOut
to limit MEV extraction
Time-lockDelay execution to reduce predictability

防御方案机制
提交-揭示直到揭示阶段才公开交易意图
Flashbots/私有内存池直接向区块构建者提交交易
滑点保护设置
minAmountOut
限制MEV提取
时间锁延迟执行降低可预测性

7. SIGNATURE REPLAY

7. 签名重放

Missing Nonce

缺少Nonce

Reuse a valid signature to repeat the action (e.g., transfer) multiple times.
重复使用有效签名多次执行同一操作(例如转账)。

Cross-Chain Replay

跨链重放

Same contract deployed on multiple chains with same address → signature valid on all chains. Must include
block.chainid
in signed message.
同一合约在多条链上部署相同地址 → 签名在所有链上都有效。必须在签名消息中包含
block.chainid

EIP-712 Implementation Errors

EIP-712实现错误

ErrorConsequence
Missing
DOMAIN_SEPARATOR
with chainId
Cross-chain replay
Domain separator cached at deployBreaks after hard fork changing chainId
Missing nonce in struct hashSignature replay
ecrecover
returns
address(0)
on invalid sig
Passes
== address(0)
owner check

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

8. SELF-DESTRUCT & FORCE-SEND ETH

8. SELF-DESTRUCT与强制发送ETH

selfdestruct(recipient)
force-sends all contract ETH to recipient — bypasses
receive()
and
fallback()
, cannot be rejected.
Breaks contracts that rely on
address(this).balance
for logic (e.g.,
require(balance == expected)
).
Post-EIP-6780 (Dencun):
selfdestruct
only sends ETH; code/storage deletion only if called in same tx as creation.

selfdestruct(recipient)
会将合约所有ETH强制发送给接收方——绕过
receive()
fallback()
,无法被拒绝。
会破坏依赖
address(this).balance
做逻辑判断的合约(例如
require(balance == expected)
)。
EIP-6780(Dencun升级)后:
selfdestruct
仅发送ETH;仅在与部署同交易中调用时才会删除代码/存储。

9. CREATE2 & DETERMINISTIC ADDRESS EXPLOITATION

9. CREATE2与确定性地址漏洞利用

CREATE2
address =
keccak256(0xff ++ deployer ++ salt ++ keccak256(initCode))
.
AttackMethod
Pre-fund exploitationPredict address → send tokens/ETH before deployment →
selfdestruct
→ redeploy different code at same address
Pre-approve exploitationPredicted address gets token approvals → deploy malicious contract → drain approved tokens
Metamorphic contracts
CREATE2
selfdestruct
CREATE2
with same salt but different
initCode
(pre-EIP-6780)

CREATE2
地址 =
keccak256(0xff ++ deployer ++ salt ++ keccak256(initCode))
攻击方式
预注资漏洞利用预测地址 → 部署前发送代币/ETH → 执行
selfdestruct
→ 在同一地址重新部署不同代码
预授权漏洞利用预测地址获得代币授权 → 部署恶意合约 → 转走授权代币
变形合约
CREATE2
selfdestruct
→ 用相同salt不同
initCode
再次执行
CREATE2
(EIP-6780之前)

10. FLASH LOAN ATTACK PATTERNS

10. 闪电贷攻击模式

Single transaction:
├── Borrow large amount (no collateral)
├── Manipulate state (price oracle, governance, etc.)
├── Extract profit from manipulated state
├── Repay loan + fee
└── Keep profit
Key: entire sequence must succeed atomically or the whole tx reverts.

单交易流程:
├── 无抵押借入大额资金
├── 操纵状态(价格预言机、治理等)
├── 从被操纵的状态中提取利润
├── 偿还贷款+手续费
└── 剩余利润归攻击者所有
关键:整个序列必须原子性成功,否则整个交易回滚。

11. SHORT ADDRESS ATTACK

11. 短地址攻击

EVM pads missing bytes in ABI-encoded calldata with zeros. If
transfer(address, uint256)
is called with a 19-byte address, the uint256 amount shifts left by 8 bits → multiplied by 256.
Mitigation: validate calldata length; modern Solidity compilers add checks.

EVM会将ABI编码调用数据中缺失的字节补零。如果调用
transfer(address, uint256)
时传入19字节地址,uint256金额会左移8位 → 放大256倍。
修复方案:验证调用数据长度;现代Solidity编译器已内置检查。

12. TOOLS

12. 工具

ToolPurposeUsage
SlitherStatic analysis, vulnerability detection
slither .
in project root
MythrilSymbolic execution, path exploration
myth analyze contract.sol
EchidnaProperty-based fuzzingDefine invariants, fuzz for violations
Foundry (Forge)Test framework, fuzzing, gas analysis
forge test --fuzz-runs 10000
HardhatDevelopment, testing, deployment
npx hardhat test
CertoraFormal verificationWrite specs, prove/disprove properties
4naly3erAutomated gas optimization + vuln reportCI integration

工具用途用法
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. DECISION TREE

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
审计智能合约?
├── 是否是代理模式?
│   ├── 是 → 检查存储布局冲突(第5节)
│   │   ├── 对比代理和实现合约的插槽分配
│   │   ├── 检查函数选择器冲突
│   │   └── 验证初始化函数不能被重复调用
│   └── 否 → 继续
├── 是否有外部调用?
│   ├── 是 → 检查重入(第1节)
│   │   ├── 调用前是否更新状态? → CEI模式合规
│   │   ├── 是否有ReentrancyGuard? → 检查所有入口点
│   │   ├── 是否有跨函数状态共享? → 跨函数重入风险
│   │   └── 回调期间是否有view函数被读取? → 只读重入风险
│   └── 否 → 继续
├── 是否处理代币/ETH?
│   ├── 是 → 检查整数溢出(第2节)
│   │   ├── Solidity版本 < 0.8? → 所有算术运算都有风险
│   │   ├── 是否有unchecked{}块? → 确认没有用户可控值
│   │   └── 是否有uint类型之间的转换? → 截断风险
│   └── 同时检查self-destruct强制发送ETH(第8节)
├── 是否使用签名?
│   ├── 是 → 检查重放(第7节)
│   │   ├── 是否包含Nonce? → 验证会递增
│   │   ├── 是否包含ChainId? → 跨链安全
│   │   └── ecrecover结果是否检查非零地址? → 合规
│   └── 否 → 继续
├── 是否使用链上随机数?
│   ├── 是 → 可预测(第4节)
│   │   └── 推荐使用Chainlink VRF或带保证金的提交-揭示方案
│   └── 否 → 继续
├── 是否与DeFi协议交互?
│   ├── 是 → 加载[DeFi攻击模式](../defi-attack-patterns/SKILL.md)
│   │   ├── 闪电贷攻击向量
│   │   ├── 预言机操纵
│   │   └── MEV暴露风险
│   └── 否 → 继续
├── 是否使用CREATE2?
│   ├── 是 → 检查确定性地址漏洞利用(第9节)
│   └── 否 → 继续
└── 运行自动化工具(第12节)
    ├── Slither做静态分析
    ├── Mythril做符号执行
    └── Echidna做不变量模糊测试