MoveCtf
说在前头:作者是move菜鸟,有问题望大家指正
参加平台:https://platform.cyclens.tech/activity/1
根据week1.move
的代码提示,调用get_flag
函数
调用后提交交易哈希 (Transaction Hash)和合约返回的 FLAG (Contract Returned FLAG)即可完成任务
我是写了一个solve_week1的合约来调用get_flag
Sui Cli官方文档https://docs.sui.io/references/cli
brew install sui
使用homebrew包管理器安装 Sui(MacOS)sui client new-address ed25519
使用 ED25519 签名方案创建一个新地址sui client faucet
获取gas(交易手续费),命令行输入后需自行去网站手动领取,sui client gas
查询是否领取到gaspublic entry fun get_flag(
seed: u64,
score: u64,
rand: &Random,
github_id: String,
hash_input: vector<u8>,
magic_number: u64,
challenge: &mut Challenge,
guess: vector<u8>,
ctx: &mut TxContext
)
可以看到我们需要ctx除外的8个参数才能调用get_flag
sui client publish
把week1部署到链上ObjectId
,后续会用到,也可以在终端找到
let secret = b"Letsmovectf_week1"
;
所以后续的&challenge.secret
都是“Letsmovectf_week1”这串字符seed
:
let secret_bytes = *string::as_bytes(&challenge.secret);
let secret_len = vector::length(&secret_bytes);
assert!(seed == secret_len * 2, EINVALID_SEED);
string::as_bytes(&challenge.secret)
: 这个函数会把 challenge.secret
字符串转换成一个字节数组L
e
t
~ e
k
1
vector::length(&secret_bytes)
获取数组元素的个数=17
可知 seed == secret_let 2 = 17 2 = 34score
参数是否等于计算出的 expected_score
assert!()
是Move语言中的一个断言语句,如果条件为假,程序就会立即中止执行(回滚交易),并返回指定的错误代码。如果为真,程序正常运行(后续出现不再赘述)score
:let secret_hash = sha3_256(*string::as_bytes(&challenge.secret));
let expected_score = (
((*vector::borrow(&secret_hash, 0) as u64) << 24) |
((*vector::borrow(&secret_hash, 1) as u64) << 16) |
((*vector::borrow(&secret_hash, 2) as u64) << 8) |
(*vector::borrow(&secret_hash, 3) as u64));
assert!(score == expected_score, EINVALID_SCORE);
secret
进行 SHA3-256 哈希运算,并将结果存储在 secret_hash
变量中vector::borrow(&secret_hash, 0)
: 会从 secret_hash
字节数组中获取索引为 0 的字节
<< 24
:将字节进行左移位操作,左移24位
|
: 这是一个按位或操作。左移后的四个字节值通过按位或操作组合在一起。因为这些字节被移动到了不同的位置,所以按位或操作会将它们有效地拼接成一个 u64
整数。secret_hash[0] = 0xAB //举例
secret_hash[1] = 0xCD
secret_hash[2] = 0xEF
secret_hash[3] = 0x12
expected_score = ((0xAB << 24) | // 0xAB000000
(0xCD << 16) | // 0x00CD0000
(0xEF << 8) | // 0x0000EF00
(0x12)) // 0x00000012
expected_score = 0xABCDEF12
rand
:0x8github_id
:启动环境后可得hash_input
:let mut bcs_input = bcs::to_bytes(&challenge.secret);
vector::append(&mut bcs_input, *string::as_bytes(&github_id));
let expected_hash = sha3_256(bcs_input);
assert!(hash_input == expected_hash, EINVALID_HASH);
challenge.secret
通过 BCS 格式序列化成字节,并将这些字节存储在一个名为 bcs_input
的可变字节数组中。
BSC是"Binary Canonical Serialization"(二进制规范序列化)的缩写github_id
字符串转换成的字节数组,追加到 bcs_input
字节数组的末尾bsc_input
进行sha3_256哈希计算,将结果储存在中expected_hash
magic_number
:let expected_magic = challenge.current_score % 1000 + seed;
assert!(magic_number == expected_magic, EINVALID_MAGIC);
seed
进行一些运算没什么好说的
challenge
:前面提到的ObjectId
guess
:let mut guess_data = guess
vector::append(&mut guess_data, *string::as_bytes(&challenge.secret));
let random = sha3_256(guess_data);
let prefix_length = 2;
assert!(compare_hash_prefix(&random, &challenge.round_hash, prefix_length), EINVALID_GUESS_HASH);
fun compare_hash_prefix(hash1: &vector<u8>, hash2: &vector<u8>, n: u64): bool {
if (vector::length(hash1) < n || vector::length(hash2) < n) {
return false
};
let mut i = 0;
while (i < n) {
if (*vector::borrow(hash1, i) != *vector::borrow(hash2, i)) {
return false
};
i = i + 1;
};
true
}
该段代码是将guess
和challenge.secret
进行sha3_256加密运算,并两个字符串加密后的哈希值与prefix_length = 2
传入compare_hash_prefix
,将两个哈希值的前缀‘prefix_length’(2)位字符进行比较,若两位都相同,程序继续运行
sui move new solve_week1
创建一个新项目move.toml
中导入week1的合约
[dependencies]
week1 = { local = "../week1" }
才能在solve_week1里调用week1的module
可以与上一部分对应着看,大部分代码都可以直接cv week1.move
的
其实是我懒得写
module solve_week1::solve;
use std::bcs;
use std::hash::sha3_256;
use std::string::{Self, String};
use std::vector;
use sui::random::Random;
use sui::tx_context::TxContext;
use week1::challenge::{Self, Challenge};
public entry fun solve_get_flag(
challenge: &mut Challenge,
rand: &Random,
ctx: &mut TxContext
) {
//seed
let seed = 34; // length of "Letsmovectf_week1" is 17, so 17 * 2 = 34
//score
let secret = b"Letsmovectf_week1";
let secret_hash = sha3_256(secret);
let score = (
((*vector::borrow(&secret_hash, 0) as u64) << 24) |
((*vector::borrow(&secret_hash, 1) as u64) << 16) |
((*vector::borrow(&secret_hash, 2) as u64) << 8) |
(*vector::borrow(&secret_hash, 3) as u64),
);
//github_id
let github_id = string::utf8(b"12cecd31-3002-4557-a8a1-da79ab1e4e5f");
//hash_input
let mut bcs_input = bcs::to_bytes(&string::utf8(secret));
vector::append(&mut bcs_input, *string::bytes(&github_id));
let hash_input = sha3_256(bcs_input);
//magic_number
let magic_number = score % 1000 + seed;
//guess
let guess = b"83419";
challenge::get_flag(
seed,
score,
rand,
github_id,
hash_input,
magic_number,
challenge,
guess,
ctx,
);
}
sui client publish
将solve_week1部署上链
新合约只需要两个args就可以调用
sui client call --package <packageID> --module solve --function solve_get_flag --args "<challengeID>" "0x8"
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!