公链安全之比特币任意盗币漏洞浅析(CVE-2010-5141)
字数 1393 2025-08-29 08:31:41

比特币任意盗币漏洞分析(CVE-2010-5141)教学文档

一、背景知识

1. UTXO模型

1.1 账户模型与UTXO模型的区别

  • 账户模型:数据结构为"账号=>余额",转账操作简单(A-200,B+200)
    • 代表系统:银行系统、以太坊等
  • UTXO模型:比特币特有模型,无直接"账号=>余额"结构
    • 代表系统:比特币

1.2 UTXO转账机制

以A向B转账200为例:

  1. 找到A账号下200余额的来源交易x
  2. 创建新交易y:
    • 输入:交易x
    • 输出:向B转账200的交易y
  3. 标记状态:
    • x交易标记为已花费
    • y交易标记为未花费
  4. 金额规则:
    • 输入输出金额必须相等
    • 如需部分转账,剩余金额需转回自己其他地址

2. 比特币脚本引擎

  • 特性:非图灵完备,提供有限灵活性
  • 功能:实现多重签名、资金冻结等简单功能
  • 执行原理
    • 基于堆栈执行(先进先出)
    • 定义了一系列操作码(OP_CODE)
  • 标准转账流程
    1. 填入执行脚本(Script)
    2. 签名(sig)和公钥(pubKey)压入堆栈
    3. OP_CHECKSIG验证签名
    4. 验证通过压入true,否则压入false

二、漏洞分析(CVE-2010-5141)

1. 漏洞核心

  • 位置:比特币0.3.3版本的script.cpp文件
  • 关键函数VerifySignature
  • 影响:攻击者可盗取任意比特币
  • 实际影响:未被利用,修复迅速

2. 漏洞代码分析

2.1 VerifySignature函数

// 伪代码表示
bool VerifySignature(CTx txFrom, CTx txTo, ...) {
    // 关键调用
    if (!EvalScript(txin.scriptSig + OP_CODESEPARATOR + txout.scriptPubKey, ...)) {
        return false;
    }
    return true;
}
  • txFrom:上一笔交易
  • txTo:当前处理交易
  • EvalScript参数组成:
    1. txin.scriptSig(可控,含签名信息)
    2. OP_CODESEPARATOR(分隔符)
    3. txout.scriptPubKey(不可控,含公钥和OP_CHECKSIG)

2.2 EvalScript函数

  • 返回true条件
    1. 堆栈不为空
    2. 栈顶元素强转为bool后为true(非0)

2.3 OP_CHECKSIG操作码

// 伪代码表示
case OP_CHECKSIG:
    bool fSuccess = CheckSig(...);
    if (fSuccess)
        stack.push(vchTrue); // 非0值
    else
        stack.push(vchFalse); // 0值

2.4 漏洞利用关键 - OP_PUSHDATA4

  • 定义值:78
  • 处理逻辑
    1. 遇到OP_PUSHDATA4时,指针右移78+4=82位
    2. 其中78位数据被压入栈
  • 攻击方式
    • txin.scriptSig中注入OP_PUSHDATA4操作码
    • 导致后续的公钥信息和OP_CHECKSIG指令被"吃掉"作为参数入栈
    • 最终判断:
      • 堆栈不为空
      • 栈顶元素非0
    • 绕过验证,返回true

三、漏洞修复方案

修复版本

比特币0.3.8

修复方法

// 修复后伪代码
bool VerifySignature(...) {
    // 将scriptSig和scriptPubkey分开执行
    if (!EvalScript(txin.scriptSig, ...) || 
        !EvalScript(txout.scriptPubKey, ...)) {
        return false;
    }
    return true;
}
  • 核心改进:分离执行scriptSigscriptPubkey
  • 效果scriptSig中的恶意操作码无法影响scriptPubkey的执行

四、教学总结

1. 漏洞要点

  1. 根本原因:脚本拼接执行导致注入攻击
  2. 关键操作码:OP_PUSHDATA4的异常处理
  3. 利用条件:控制输入脚本(txin.scriptSig)

2. 对公链开发的启示

  1. 脚本/虚拟机安全:需严格隔离用户输入与系统脚本
  2. 操作码设计:需考虑边界情况和异常处理
  3. 验证机制:关键验证步骤应有独立完整的执行环境

3. 研究难点

  1. 历史资料稀少
  2. 早期版本编译环境搭建困难
  3. 私链测试环境缺乏

五、参考资源

  1. 比特币StackExchange关于该漏洞的讨论
  2. 比特币0.3.3和0.3.8源码对比
比特币任意盗币漏洞分析(CVE-2010-5141)教学文档 一、背景知识 1. UTXO模型 1.1 账户模型与UTXO模型的区别 账户模型 :数据结构为"账号=>余额",转账操作简单(A-200,B+200) 代表系统:银行系统、以太坊等 UTXO模型 :比特币特有模型,无直接"账号=>余额"结构 代表系统:比特币 1.2 UTXO转账机制 以A向B转账200为例: 找到A账号下200余额的来源交易x 创建新交易y: 输入:交易x 输出:向B转账200的交易y 标记状态: x交易标记为已花费 y交易标记为未花费 金额规则: 输入输出金额必须相等 如需部分转账,剩余金额需转回自己其他地址 2. 比特币脚本引擎 特性 :非图灵完备,提供有限灵活性 功能 :实现多重签名、资金冻结等简单功能 执行原理 : 基于堆栈执行(先进先出) 定义了一系列操作码(OP_ CODE) 标准转账流程 : 填入执行脚本(Script) 签名(sig)和公钥(pubKey)压入堆栈 OP_ CHECKSIG验证签名 验证通过压入true,否则压入false 二、漏洞分析(CVE-2010-5141) 1. 漏洞核心 位置 :比特币0.3.3版本的 script.cpp 文件 关键函数 : VerifySignature 影响 :攻击者可盗取任意比特币 实际影响 :未被利用,修复迅速 2. 漏洞代码分析 2.1 VerifySignature函数 txFrom :上一笔交易 txTo :当前处理交易 EvalScript 参数组成: txin.scriptSig (可控,含签名信息) OP_CODESEPARATOR (分隔符) txout.scriptPubKey (不可控,含公钥和OP_ CHECKSIG) 2.2 EvalScript函数 返回true条件 : 堆栈不为空 栈顶元素强转为bool后为true(非0) 2.3 OP_ CHECKSIG操作码 2.4 漏洞利用关键 - OP_ PUSHDATA4 定义值 :78 处理逻辑 : 遇到OP_ PUSHDATA4时,指针右移78+4=82位 其中78位数据被压入栈 攻击方式 : 在 txin.scriptSig 中注入OP_ PUSHDATA4操作码 导致后续的公钥信息和OP_ CHECKSIG指令被"吃掉"作为参数入栈 最终判断: 堆栈不为空 栈顶元素非0 绕过验证,返回true 三、漏洞修复方案 修复版本 比特币0.3.8 修复方法 核心改进 :分离执行 scriptSig 和 scriptPubkey 效果 : scriptSig 中的恶意操作码无法影响 scriptPubkey 的执行 四、教学总结 1. 漏洞要点 根本原因 :脚本拼接执行导致注入攻击 关键操作码 :OP_ PUSHDATA4的异常处理 利用条件 :控制输入脚本(txin.scriptSig) 2. 对公链开发的启示 脚本/虚拟机安全 :需严格隔离用户输入与系统脚本 操作码设计 :需考虑边界情况和异常处理 验证机制 :关键验证步骤应有独立完整的执行环境 3. 研究难点 历史资料稀少 早期版本编译环境搭建困难 私链测试环境缺乏 五、参考资源 比特币StackExchange关于该漏洞的讨论 比特币0.3.3和0.3.8源码对比