实践:签到挑战
题目描述
在本实践环节,你将分析一个 Sui Move 签到合约,通过计算哈希值调用函数,获取隐藏的 flag。flag 是一个格式为 CTF{xxx}
的字符串,将在正确输入时通过事件输出。目标是体验 Move 代码分析和基本 CTF 解题流程。
示例代码
以下是待分析的 Move 合约:
github: chapter_1
module chapter_1::check_in {
use std::string::{Self, String};
use std::bcs;
use std::hash::sha3_256;
use sui::event;
//testnet
//PackageID:0x335297860a807291254b20f8a0dea30d72d5e17d2e6f8058e42d5b9c72f0f0ef
public struct FlagEvent has copy, drop {
sender: address,
flag: String,
success: bool
}
public entry fun get_flag(
flag: vector<u8>,
github_id: String,
ctx: &mut TxContext
) {
let mut bcs_input = bcs::to_bytes(&string::utf8(b"LetsMoveCTF"));
vector::append<u8>(&mut bcs_input, *github_id.as_bytes());
let expected_hash = sha3_256(bcs_input);
if (flag == expected_hash) {
event::emit(FlagEvent {
sender: tx_context::sender(ctx),
flag: string::utf8(b"CTF{WelcomeToMoveCTF}"),
success: true
});
} else {
event::emit(FlagEvent {
sender: tx_context::sender(ctx),
flag: string::utf8(b"Try again!"),
success: false
});
}
}
}
任务目标
阅读代码,理解哈希验证逻辑。
计算正确的 flag 输入并运行代码,获取 flag。
解题思路
1、找到如何获取flag的代码块:
##其中 `flag == expected_hash` 为获取flag的条件
if (flag == expected_hash) {
event::emit(FlagEvent {
sender: tx_context::sender(ctx),
flag: string(b"CTF{WelcomeToMoveCTF}"),
success: true
});
} else {
event::emit(FlagEvent {
sender: tx_context::sender(ctx),
flag: string(b"Try again!"),
success: false
});
}
2、如何满足 flag == expected_hash
条件?
let mut bcs_input = bcs::to_bytes(&string(b"LetsMoveCTF"));
vector::append<u8>(&mut bcs_input, *github_id.as_bytes());
let expected_hash = sha3_256(bcs_input);
代码块中 expected_hash 为 LetsMoveCTF
+ 用户输入的github_id
转换为bytes然后sha3_256 进行编码,所以flag传入也需要是expected_hash的这个结果。
3、进行解题
这里采用的是合约的方式进行解题。
github: solve_chapter_1 首先创建合约:
sui move new solve_chapter_1 && cd solve_chapter_1
修改 Move.toml
文件导入题目合约,如果是本地与题目同目录则添加 local
方式:
[package]
name = "solve_chapter_1"
edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move
# license = "" # e.g., "MIT", "GPL", "Apache 2.0"
# authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"]
[dependencies]
chapter_1 = { local = "../chapter_1" }
# For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`.
# Revision can be a branch, a tag, and a commit hash.
# MyRemotePackage = { git = "https://some.remote/host.git", subdir = "remote/path", rev = "main" }
# For local dependencies use `local = path`. Path is relative to the package root
# Local = { local = "../path/to" }
# To resolve a version conflict and force a specific version for dependency
# override use `override = true`
# Override = { local = "../conflicting/version", override = true }
[addresses]
solve_chapter_1 = "0x0"
# Named addresses will be accessible in Move as `@name`. They're also exported:
# for example, `std = "0x1"` is exported by the Standard Library.
# alice = "0xA11CE"
[dev-dependencies]
# The dev-dependencies section allows overriding dependencies for `--test` and
# `--dev` modes. You can introduce test-only dependencies here.
# Local = { local = "../path/to/dev-build" }
[dev-addresses]
# The dev-addresses section allows overwriting named addresses for the `--test`
# and `--dev` modes.
# alice = "0xB0B"
然后编写解题合约 solve_chapter_1.move
:
module solve_chapter_1::solve{
use chapter_1::check_in::get_flag;
use std::string;
use std::bcs;
use std::hash::sha3_256;
//testnet
//PackageID: 0xef6b4139ec1b0fda23e06c4a30c9e91150b72c38530e4517152e591001c5c433
public entry fun solve_get_flag(ctx: &mut TxContext){
let github_id = string::utf8(b"hoh-zone");
let mut bcs_input = bcs::to_bytes(&string::utf8(b"LetsMoveCTF"));
vector::append<u8>(&mut bcs_input, *github_id.as_bytes());
let flag_hash = sha3_256(bcs_input);
get_flag(flag_hash, github_id, ctx);
}
}
发布合约:
sui client publish
发布成功后调用合约:
sui client call --package 0xef6b4139ec1b0fda23e06c4a30c9e91150b72c38530e4517152e591001c5c433 --module solve --function solve_get_flag
最终结果可以看到终端输出的Events内成功获取flag:
╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Transaction Block Events │
├───────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ┌── │
│ │ EventID: EjQiJnPZRqen1TSSkNiUaRaEQYSmhshJYHctn3uUt1V5:0 │
│ │ PackageID: 0xef6b4139ec1b0fda23e06c4a30c9e91150b72c38530e4517152e591001c5c433 │
│ │ Transaction Module: solve │
│ │ Sender: 0x90abb670800b4015229d30f5d010faef0c347e1d9650c9acebe2c012be7eb724 │
│ │ EventType: 0x335297860a807291254b20f8a0dea30d72d5e17d2e6f8058e42d5b9c72f0f0ef::check_in::FlagEvent │
│ │ ParsedJSON: │
│ │ ┌─────────┬────────────────────────────────────────────────────────────────────┐ │
│ │ │ flag │ CTF{WelcomeToMoveCTF} │ │
│ │ ├─────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ sender │ 0x90abb670800b4015229d30f5d010faef0c347e1d9650c9acebe2c012be7eb724 │ │
│ │ ├─────────┼────────────────────────────────────────────────────────────────────┤ │
│ │ │ success │ true │ │
│ │ └─────────┴────────────────────────────────────────────────────────────────────┘ │
│ └── │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯