以太坊 CREATE2 操作码原理与 CTF 利用:在同一地址反复部署不同合约的攻击技巧(ctf-wiki)

发布时间:2026/9/25 7:47:52

以太坊 CREATE2 操作码原理与 CTF 利用:在同一地址反复部署不同合约的攻击技巧(ctf-wiki) 文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载CREATE2是 EIP-1014 引入的以太坊合约创建操作码它用0xff address salt keccak256(init_code)的确定性哈希取代了依赖nonce的CREATE地址推导使合约地址变得完全可控、可预先计算。在 CTF 区块链题目中这一特性最常见的利用方式是保持init_code不变、让构造函数返回的运行时字节码可变从而在同一个地址上先后部署字节码完全不同的合约绕过各类基于地址或字节码的校验。阅读本篇后你将掌握CREATE与CREATE2的地址推导原理、二者差异、地址预计算脚本以及 2019 Balsn CTF Creativity 题目的完整 Solidity PoC 复现思路。背景为何 CREATE2 会在以太坊与 CTF 中被关注在 CTF-Wiki 的区块链板块中以太坊Ethereum安全是占比最高、也是最常出现的主题见 区块链介绍。智能合约代码一旦上链便难以篡改因此合约部署地址的生成规则直接决定了攻击者能在多大程度上预测或重放合约。以太坊虚拟机EVM为开发者提供了两种创建合约的操作码CREATE最传统的合约创建方式地址由创建者地址与账户nonce决定CREATE2在君士坦丁堡Constantinople硬分叉中引入的新操作码地址由创建者地址、盐值salt与创建代码init_code共同决定完全不再依赖nonce。两者都输出一个新合约的地址但CREATE2让地址生成具有确定性 可预计算的能力这正是其衍生出大量攻击技巧的根源。CREATE 的地址推导nonce 驱动的确定性在理解CREATE2之前需要先理解旧的CREATE操作码。无论使用外部账户EOA部署还是由合约通过CREATE创建子合约新合约的地址都由创建者的账户地址以及该账户的nonce共同决定。以太坊中每个账户都维护着一个nonce字段Ethereum Basics 将其定义为已执行交易总数用来标示该账户发出的交易数量对外部账户而言每发送一笔交易nonce就会1对合约账户而言每通过CREATE创建一个子合约nonce就会1。新合约地址的计算公式为keccak256(rlp.encode(address, nonce))[12:]即将创建者地址与当前nonce做 RLP 编码取keccak256哈希结果的后 20 字节160 位作为新合约地址。由于nonce会随交易或子合约创建次数而递增同一账户两次创建合约得到的地址必然不同而且地址无法在创建之前被精确预知至少不直观。这种不可控性正是CREATE2要解决的痛点。CREATE2 的地址推导EIP-1014 的确定性公式CREATE2摒弃了对nonce的依赖改为对以下三个参数做哈希运算参数含义可控性address合约创建者的地址部署方部署方确定可控salt由调用方显式传入的 32 字节混淆值完全可控init_code合约的创建代码用于部署合约的字节码可控是整个技巧的关键其地址计算公式为keccak256(0xff address salt keccak256(init_code))[12:]公式中0xff是一个固定的单字节前缀用于与CREATE的地址空间以及普通交易地址推导作区分随后拼接创建者地址、salt再拼接init_code本身的keccak256哈希最终对整个结果再取一次keccak256并截取低 160 位后 20 字节作为新合约地址。一个容易忽略的关键细节init_code 与运行时字节码计算地址时使用的最后一个参数并非合约最终存储的运行时字节码runtime code而是创建代码init code。创建代码是用来创建合约的那段代码它在合约创建交易执行期间运行执行完毕后通过RETURN0xf3返回一段数据这段数据才是写入链上的运行时字节码参见 Ethereum Opcodes 中RETURN的语义return memory[offset:offsetlength]。这一细节带来了极具攻击性的推论只要init_code保持不变无论构造函数最终返回什么运行时字节码计算出的地址都完全一致。于是攻击者可以控制构造函数返回任意字节码同时保持init_code固定从而在同一个地址上反复部署、覆盖出完全不同行为的合约。CREATE2允许合约在部署后被重新更改的特性天然存在潜在安全问题这也是 EIP-1014 社区讨论Potential Security Implications of CREATE2所关注的重点。在 CTF 中该技巧通常被用作在同一地址部署不同合约以绕过不同校验的通用手段。实战案例2019 Balsn CTF Creativity 的 CREATE2 巧用下面以 2019 Balsn CTF 的 Creativity 题目基于其官方 Write-up 提供的 PoC为例完整演示如何借助CREATE2在同一个地址上先后部署两个完全不同的合约。核心 PoCDeployer 与 Dumper 两个合约PoC 使用 Solidity^0.5.10包含两个合约Deployer负责执行CREATE2部署Dumper则充当始终不变的init_code它唯一的职责就是在构造函数阶段把调用者传入的任意字节码倾倒return出来作为最终部署的运行时字节码。pragma solidity ^0.5.10; contract Deployer { bytes public deployBytecode; address public deployedAddr; function deploy(bytes memory code) public { deployBytecode code; address a; // Compile Dumper to get this bytecode bytes memory dumperBytecode hex6080604052348015600f57600080fd5b50600033905060608173ffffffffffffffffffffffffffffffffffffffff166331d191666040518163ffffffff1660e01b815260040160006040518083038186803b158015605c57600080fd5b505afa158015606f573d6000803e3d6000fd5b505050506040513d6000823e3d601f19601f820116820180604052506020811015609857600080fd5b81019080805164010000000081111560af57600080fd5b8281019050602081018481111560c457600080fd5b815185600182028301116401000000008211171560e057600080fd5b50509291905050509050805160208201f3fe; assembly { a : create2(callvalue, add(0x20, dumperBytecode), mload(dumperBytecode), 0x9453) } deployedAddr a; } } contract Dumper { constructor() public { Deployer dp Deployer(msg.sender); bytes memory bytecode dp.deployBytecode(); assembly { return (add(bytecode, 0x20), mload(bytecode)) } } }逐段拆解这个 PoC 的攻击链路deploy(code)是唯一的对外入口调用方把希望部署的任意合约字节码作为code参数传入。Deployer先将code存入公共状态变量deployBytecode后面Dumper会读取它再通过内联汇编执行CREATE2。init_code恒定dumperBytecode硬编码了编译后的Dumper合约字节码也就是说无论传入什么codeCREATE2使用的init_code始终是同一段字节码salt固定为0x9453部署方地址固定为Deployer合约地址。三者不变 ⇒ 计算出的目标地址不变。Dumper构造函数偷梁换柱Dumper的构造函数在创建阶段执行它会拿到msg.sender即Deployer合约调用deployBytecode()读出刚才传入的code然后用内联汇编的return(add(bytecode, 0x20), mload(bytecode))把这整段code作为运行时字节码返回给 EVM。需要注意汇编参数的内存布局mload(bytecode)读取的是bytecode指针处的长度字段add(bytecode, 0x20)则跳过长度字段指向数据区起始位置二者恰好构成RETURN所需的(offset, length)参数——这也与 Ethereum Opcodes 中RETURN的栈语义完全一致。效果同一个地址任意合约对调用者而言效果是惊人的每次调用deploy(code)实际参与地址计算的init_code都是同一个dumperBytecode加上固定的部署方地址与salt因此deploy(code)部署出来的合约最终总是落在同一个地址上而该地址上最终存储的运行时字节码则是本次调用传入的code。也就是说第一次deploy(codeA)→ 地址 X 上部署的是codeA第二次deploy(codeB)→地址 X 上部署的变成了codeB无论传入什么合约代码都能精确投递到同一个地址。这正好实现了本文开头所说的在同一次 CTF 挑战中用同一个地址绕过不同校验的攻击模式题目校验某地址上的合约行为时攻击者可以先在该地址部署满足校验 A 的合约再重新部署满足校验 B 的合约。地址预计算getAddress 函数由于CREATE2的地址完全确定攻击者在部署前就能算出目标地址。题目给出的已知条件是Deployer合约地址为0x99Ed0b4646a5F4Ee0877B8341E9629e4BF30c281配合固定的dumperBytecode与salt可以预先算出实际部署合约的地址为0x4315DBef1aC19251d54b075d29Bcc4E81F1e3C73。地址预计算的 Solidity 实现如下其计算逻辑与keccak256(0xff address salt keccak256(init_code))[12:]一一对应function getAddress(address addr, bytes memory bytecode, uint salt) public view returns (address) { bytes32 hash keccak256( abi.encodePacked( bytes1(0xff), addr, salt, keccak256(bytecode) ) ); // NOTE: cast last 20 bytes of hash to address return address(uint160(uint256(hash))); }实现细节解读abi.encodePacked将0xff、创建者地址、salt与keccak256(init_code)紧凑拼接保证不引入任何填充字节与 EVM 内部计算逻辑完全一致bytes1(0xff)对应公式中的固定前缀最后通过uint160(uint256(hash))截取哈希的低 20 字节并转换为address类型。实际复现时可以先用getAddress验证预计算地址0x4315DBef1aC19251d54b075d29Bcc4E81F1e3C73再分别在deploy中传入两段不同的字节码观察同一地址上先后部署出两个不同合约。部署过程实测截图下面的两张图来自本题的复现过程直观展示了同一个地址上先后部署两个不同合约的完整链路。两张图中部署方DEPLOYER地址均为0x99Ed0b4646a5F4Ee0877B8341E9629e4BF30c281但传入的待部署字节码参数不同第一张为0x33ff第二张为完整合约字节码两次部署计算出的合约地址却都是0x4315DBef1aC19251d54b075d29Bcc4E81F1e3C73——这正是CREATE2地址确定性在实战中的直接体现。实战中的攻击套路与防御视角为什么攻击者偏爱 CREATE2从 CTF 出题与解题两个角度看CREATE2的核心价值在于地址可预计算部署前即可获知目标地址便于配合其他合约预先设置白名单、授权等依赖地址的逻辑地址可复用同一地址可以承载完全不同的代码逻辑从而绕过地址 A 已经存在/不可修改之类的假设init_code与运行时字节码解耦只要控制好构造函数返回的数据就能让地址计算与最终代码脱钩。对应的防御注意事项合约逻辑中若把某地址上已部署的代码视为不可变信任根需要意识到该地址可能被CREATE2重新部署出恶意代码即所谓的合约可以自我替换风险审计中应特别关注项目方是否使用了固定salt 可变init_code的模式以及构造函数是否会返回来自外部输入的字节码在编写依赖合约地址做权限控制的系统时应显式验证部署方身份与部署时间而不是只依赖地址本身。相关 CTF 题目下列题目均与CREATE2技巧相关适合作为练习题目附件可参考 CTF-Wiki 的 challenges 仓库赛事题目名称Balsn 2019CreativityQWB 2020EasyAssembly其中 Balsn 2019 Creativity 正是上文 PoC 的出处QWB 2020 EasyAssembly 则将CREATE2与汇编/字节码构造技巧结合进一步考验对 EVM 执行细节的把控。参考EIP-1014: Skinny CREATE2CREATE2操作码的正式规范定义了地址计算公式与引入背景充分利用 CREATE2社区对CREATE2各类用法的系统梳理Balsn CTF 2019 - Creativity本题的官方 Write-up 与 PoC 原始出处。如需回顾相关基础知识可继续阅读 Ethereum Basics账户、nonce、交易与合约创建机制以及 Ethereum OpcodesRETURN、CALL等关键指令的栈语义。赞分享文档网络安全教程【免费下载链接】ctf-wikiCome and join us, we need you!项目地址https://gitcode.com/gh_mirrors/ct/ctf-wiki点击查看免费下载相关推荐Envoy 静态配置详解用 static_resources 搭建一个可运行的 HTTP 反向代理Envoy 静态配置详解用 static_resources 搭建一个可运行的 HTTP 反向代理 本文以 Envoy 官方快速上手指南中的「静态配置」文档为文档网络安全教程WTF Solidity 极简入门25. CREATE2 操作码——在合约部署前预计算合约地址WTF Solidity 极简入门25. CREATE2 操作码——在合约部署前预计算合约地址 CREATE2 操作码允许开发者在不实际部署合约的情况下通过示例工程区块链教程Wasp 邮件功能中的 Dummy Provider仅限开发使用的邮件发送器与生产构建限制Wasp 邮件功能中的 Dummy Provider仅限开发使用的邮件发送器与生产构建限制 在 Wasp一个面向全栈 Web 应用的声明式框架中 ema文档网络安全教程上一篇推荐开源项目Zulip Mobile - 跨平台的高效聊天客户端下一篇nginx-rtmp-win32性能优化提升Windows平台流媒体服务效率创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/25 7:42:51

Mac打印机连接故障排查与CUPS底层原理详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 11:13:03

LLM Agent驱动的开源代码审查CLI工具

1. 项目概述:这不是又一个代码审查工具,而是一次开发协作范式的重构 “open-code-review”这个名字乍看平平无奇,但拆开来看——open、code、review——三个词背后藏着当前软件工程最真实的痛点:代码审查(Code Review…

2026/9/24 20:24:47

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/25 0:02:35

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:02:35

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:02:35

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑