3、智能合约安全威胁模型:常见攻击面分类与威胁建模方法
智能合约的安全审计不能停留在“找漏洞”的被动层面,而应上升为“系统化识别风险”的主动工程。威胁建模(Threat Modeling)正是将安全从代码细节提升到架构与业务逻辑层面的核心方法。本章将围绕常见攻击面分类、威胁建模流程,以及STRIDE与攻击树两种经典方法在合约审计中的落地应用展开,帮助审计人员建立“先建模、后审计”的思维框架。
3.1 常见攻击面分类:从漏洞模式到审计关注点
智能合约的攻击面与传统Web应用有显著差异。由于区块链的不可篡改性和执行环境的确定性,攻击者往往利用合约的外部调用机制、数值计算边界、权限管理缺陷以及资源消耗特性发起攻击。审计人员必须对以下五类攻击面建立系统认知。
3.1.1 重入攻击(Reentrancy)
核心原理:当合约A调用外部合约B时,控制权暂时转移给B。若B在回调中再次调用A的未完成函数,就会形成重入。经典的DAO攻击(2016年)导致360万ETH被盗,正是利用withdraw()函数在更新余额之前发起外部调用。
判断方法:审计时需检查所有外部调用(call、delegatecall、transfer、send)是否遵循“先更新状态,再交互”(Checks-Effects-Interactions)模式。同时关注跨函数重入(Cross-function Reentrancy)和只读重入(Read-only Reentrancy)等变体。
ReentrancyGuard修饰器是最直接的防护,但审计时仍需验证其是否覆盖所有可能的重入路径,尤其是_beforeTokenTransfer等钩子函数中的隐式调用。
3.1.2 整数溢出与精度损失
Solidity 0.8.0之前默认不检查溢出,导致uint256加减乘除可能回绕。即使使用SafeMath,精度截断问题依然存在——例如在代币兑换中,amount * rate / 10000的整数除法会丢失余数,攻击者可利用多次小额操作累积误差。
| 漏洞类型 | 典型场景 | 审计关注点 |
|---|---|---|
| 算术溢出 | 余额累加、奖励计算 | 是否使用SafeMath或0.8+内置检查 |
| 精度截断 | 汇率换算、份额分配 | 除法顺序、舍入方向是否有利于攻击者 |
| 下溢 | 用户余额扣减 | 是否先校验余额再执行减法 |
3.1.3 访问控制缺陷
访问控制漏洞往往源于权限校验缺失或角色管理混乱。常见模式包括:onlyOwner修饰器未正确继承、构造函数拼写错误导致无法初始化、tx.origin误用(钓鱼攻击)、以及delegatecall导致的权限提升。
initialize)是否可被任意用户抢先调用(Front-running),以及selfdestruct后合约地址被重新创建时的权限重置问题。
3.1.4 拒绝服务(DoS)
智能合约的DoS攻击不依赖网络流量,而是通过逻辑阻塞或资源耗尽实现。典型场景包括:require条件被永久破坏、循环遍历动态数组导致Gas超限、外部调用失败导致整个交易回滚(如transfer失败阻断退款流程)。
3.1.5 业务逻辑漏洞
这类漏洞最隐蔽,往往需要结合业务场景理解。例如:治理合约中的投票权重计算错误、借贷协议中的清算阈值偏差、NFT盲盒的随机数可预测性等。审计时需将合约代码映射到业务流程图,识别状态机转换中的非法路径。
3.2 威胁建模方法:从代码审计到系统化风险识别
威胁建模的核心价值在于提前发现设计层面的风险,而非仅依赖代码扫描。在智能合约审计中,建议采用以下四步流程:
- 系统分解:明确合约的边界、外部依赖(预言机、其他合约)、数据流和信任边界。
- 威胁识别:基于攻击面分类,枚举所有可能的攻击路径。
- 风险排序:根据攻击难度、影响范围、资产损失进行优先级排序。
- 缓解验证:检查现有防护措施是否覆盖已识别的威胁。
3.2.1 STRIDE在合约审计中的适配
STRIDE(Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege)源自微软,用于系统化威胁分类。在智能合约中,各字母的映射如下:
- S(身份欺骗):攻击者伪装成其他地址调用函数,如未校验
msg.sender的权限。 - T(数据篡改):修改合约状态变量,如操纵价格预言机。
- R(抵赖):用户否认发起过某笔交易,通常依赖链上签名验证。
- I(信息泄露):私有数据被读取,如
private变量实际可被链上读取。 - D(拒绝服务):合约功能被永久或暂时阻塞。
- E(权限提升):从普通用户升级为管理员,如利用
delegatecall漏洞。
3.3 攻击树在合约审计中的应用
攻击树(Attack Tree)以根节点表示攻击者的最终目标,子节点表示达成目标所需的中间步骤,叶节点表示具体攻击动作。这种结构特别适合分析复杂的多步攻击链。
3.3.1 构建攻击树的步骤
- 定义根目标:例如“盗取合约中所有ETH”。
- 分解子目标:需要“获得owner权限”或“绕过提款校验”。
- 细化叶节点:如“利用
delegatecall修改owner”、“构造恶意calldata触发重入”。 - 标注条件:每个节点标注AND/OR关系,以及所需前置条件。
3.3.2 案例拆解:基于攻击树的借贷协议审计
背景与约束:某DeFi借贷协议允许用户抵押ETH借出DAI,清算阈值为150%。审计目标为“攻击者无需抵押即可提取DAI”。
攻击树构建:
根目标:无抵押提取DAI
├── OR 1: 操纵价格预言机
│ ├── AND 1.1: 控制预言机合约
│ │ ├── 1.1.1 利用owner未初始化漏洞
│ │ └── 1.1.2 闪电贷操纵Uniswap价格
│ └── AND 1.2: 利用价格更新延迟
│ └── 1.2.1 在价格更新前提交清算
├── OR 2: 绕过抵押校验
│ ├── 2.1 利用重入漏洞在抵押前提取
│ └── 2.2 利用精度截断使抵押价值计算为0
└── OR 3: 直接修改余额映射
└── 3.1 利用storage碰撞漏洞
分析判断过程:审计团队通过攻击树发现,路径1.1.2(闪电贷操纵预言机)的可行性最高,因为该协议使用Uniswap TWAP但时间窗口过短(仅5分钟)。进一步检查发现,预言机更新函数updatePrice()未设置访问控制,任何用户均可触发。
实际操作步骤:审计团队编写PoC,通过闪电贷借入大量USDC,在Uniswap V3中拉高ETH/USD价格,随后调用updatePrice()提交恶意价格,再以极低抵押率借出DAI。PoC在测试网复现成功。
结果对比:修复前,攻击者可无抵押提取全部DAI;修复后(增加价格更新权限控制、延长TWAP窗口至30分钟),攻击路径被有效阻断。
复盘经验:攻击树的价值在于将“抽象目标”转化为“可验证的测试用例”。审计报告中应附上攻击树图,帮助开发团队理解漏洞间的关联性。
3.4 常见误区与总结复盘
在威胁建模实践中,审计人员常陷入以下误区:
- 误区一:将威胁建模等同于漏洞扫描,忽略设计层面的风险。
- 误区二:STRIDE分类生搬硬套,未结合智能合约的独特执行模型。
- 误区三:攻击树只画不验证,未将叶节点转化为实际测试用例。
- 误区四:过度关注已知漏洞模式,忽视业务逻辑的“合理但危险”路径。