Move共学-TASK8完成 MoveCTF Lets Move挑战

本文描述了MoveCTF “Lets move” 挑战和提交加密证明求解的过程,为区块链密码学验证提供实践经验,助参与者深刻理解哈希函数与验证逻辑,掌握 BCS 序列化和随机数生成在智能合约交互中的运用,提升区块链开发能力。

需求

  • 完成 CLI 调用学习
  • 理解合约交互传值
  • 完成 Move CTF Lets Move

一、任务指南

  • 合约部署地址: 0x097a3833b6b5c62ca6ad10f0509dffdadff7ce31e1d86e63e884a14860cedc0f
  • Challenge Object: 0x19e76ca504c5a5fa5e214a45fca6c058171ba333f6da897b82731094504d5ab9
  • random: 0x8
  • github id: 填写自己的github id

1.1 lets_move源码分析

题目源码: Move CTF Lets Move

1.1.1 常量与结构体定义

  • EPROOF const EPROOF: u64 = 0; 常量表示证明检查失败时的错误代码。

  • Flag 结构体

    // flag结构体
    public struct Flag has copy, drop {
        sender: address,              // 提交证明的地址
        flag: bool,                        // 布尔值,说明挑战是否成功
        ture_num: u64,                // 用于跟踪挑战成功的次数
        github_id: String             // 表示提交证明的GitHub Id
    }
  • Challenge 结构体

    // Challenge 结构体
    public struct Challenge has key {
        id: UID,                            // 挑战的唯一标识符 (`UID`)
        str: String,                       // 一个字符串,可能表示挑战的描述或名称
        difficulity: u64,                // 挑战难度的级别
        ture_num: u64                // 跟踪该挑战完成的次数
    }

1.2 函数 init

    // 初始化一个新的 `Challenge` 对象
    // 使用 `object::new(ctx)` 创建唯一 ID,字符串为   `"LetsMoveCTF"`,难度为 `3`。
    fun init(ctx: &mut TxContext) {
        let flag_str = Challenge {
            id: object::new(ctx),
            str: string(b"LetsMoveCTF"),
            difficulity: 3,
            ture_num: 0,
        };
        share_object(flag_str);        // 共享这个对象
    }

1.3 函数 get_flag

    // get_flag 方法
    entry fun get_flag(
        proof: vector<u8>,    //  `u8` 类型的向量,表示某种加密证明(可能是挑战的解决方案)。
        github_id: String,        // 提交证明的用户的 GitHub ID 字符串
        challenge: &mut Challenge,   // 指向一个 `Challenge` 对象
        rand: &Random,                     // 随机数
        ctx: &mut TxContext               // 事务上下文,用于与 Sui 区块链进行交互。
    ) {
        // 将 `proof`、发送者的地址以及挑战对象合并成一个 `full_proof` 向量。
        let mut full_proof: vector<u8> = vector::empty<u8>();
        vector::append<u8>(&mut full_proof, proof);
        vector::append<u8>(&mut full_proof, tx_context::sender(ctx).to_bytes());
        vector::append<u8>(&mut full_proof, bcs::to_bytes(challenge));

        // 对 `full_proof` 进行 SHA3-256 哈希运算。
        let hash: vector<u8> = hash::sha3_256(full_proof);

        // 循环遍历哈希值,计算 `prefix_sum` 
        let mut prefix_sum: u32 = 0;
        let mut i: u64 = 0;
        while (i < challenge.difficulity) {
            prefix_sum = prefix_sum + (*vector::borrow(&hash, i) as u32);
            i = i + 1;
        };

       // 目标是检查哈希的前 `difficulity` 字节的总和是否为 `0`
        assert!(prefix_sum == 0, EPROOF);

        // 检查通过,生成一个新的随机字符串,并更新挑战的状态。
        challenge.str = getRandomString(rand, ctx);
        challenge.ture_num = challenge.ture_num + 1;

        // 通过 `event::emit` 发出事件,记录挑战完成的详细信息
        event::emit(Flag {
            sender: tx_context::sender(ctx),
            flag: true,
            ture_num: challenge.ture_num,
            github_id
        });
    }

这个MoveCTF挑战的主要步骤 ① 将 proof、发送者的地址以及挑战对象合并成一个 full_proof 向量。 ② 对 full_proof 进行 SHA3-256 哈希运算。 ③ 循环遍历哈希值,计算 prefix_sum ④ 目标是检查哈希的前 difficulity 字节的总和是否为 0, 失败抛异常错误码 ⑤ 检查通过,生成一个新的随机字符串,并更新挑战的状态。 ⑥ 最后,通过 event::emit 发出事件,记录挑战完成的详细信息

1.4 函数 getRandomString

使用随机生成器生成一个长度在4到 30之间的随机字符串。

    // 使用随机生成器生成一个长度在 `4` 到 `30` 之间的随机字符串。
    fun getRandomString(rand: &Random, ctx: &mut TxContext): String {
        let mut gen = random::new_generator(rand, ctx);

        let mut str_len = random::generate_u8_in_range(&mut gen, 4, 30);

        let mut rand: vector<u8> = b"";
        while (str_len != 0) {
            let rand_num = random::generate_u8_in_range(&mut gen, 34, 126);
            vector::push_back(&mut rand, rand_num);
            str_len = str_len - 1;
        };

        string(rand)
    }
}

1.2 解题的思路

通过阅读源码,这个挑战是需要我们寻找寻找一个符合特定条件的“proof”(证明)。具体来说,它会生成一个 8 字节的随机 proof,并通过 SHA3-256 哈希算法计算出一个哈希值,然后检查哈希的前 difficulty 个字节的和是否为零。

        let mut full_proof: vector<u8> = vector::empty<u8>();
        vector::append<u8>(&mut full_proof, proof);
        vector::append<u8>(&mut full_proof, tx_context::sender(ctx).to_bytes());
        vector::append<u8>(&mut full_proof, bcs::to_bytes(challenge));

代码中的 sender是明确的,我们需要找到bcs::to_bytes(challenge)的值和proof证明。 那么如何获取挑战对象哈希序列化对象结构体的值?然后通过逆向分析计算出对应的 proof证明。

1.2.1 获取Challenge对象的序列化数组

因为Challenge对象在测试网上,我们可以通过一个合约的函数,来获取challenge_bytes

module get_challenge::get_challenge {
    use lets_move::lets_move::Challenge;
    use sui::bcs;
    use sui::event;

    public struct ChallengeByte has copy, drop {
        challenge_bytes: vector<u8>
    }

    public entry fun get_challenge(challenge: &Challenge) {
        let challenge_bytes = bcs::to_bytes(challenge);
        event::emit(ChallengeByte { challenge_bytes });
    }
}
  1. 发布到测试环境 命令:sui client publish --gas-budget 100000000 --skip-fetch-latest-git-deps --skip-dependency-verification
    PS D:\data\web3\letsmove-ctf\src\02_lets_move\get_challenge> sui client publish --gas-budget 100000000 --skip-fetch-latest-git-deps --skip-dependency-verification 
    [warn] Client/Server api version mismatch, client api version : 1.37.1, server api version : 1.38.2
    INCLUDING DEPENDENCY lets_move
    INCLUDING DEPENDENCY Sui
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING get_challenge
    Skipping dependency verification
    Transaction Digest: CgRJQB6Arw68728a3YMmFBX7JuEJPq7KdyqhRfxMBmvA
  2. sui 浏览器查询获取challenge_bytes 通过交易摘要获取challenge_bytes的值:

image.png [25, 231, 108, 165, 4, 197, 165, 250, 94, 33, 74, 69, 252, 166, 192, 88, 23, 27, 163, 51, 246, 218, 137, 123, 130, 115, 16, 148, 80, 77, 90, 185, 5, 99, 89, 97, 92, 126, 3, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0]

1.2.2 计算proof证明

import hashlib
import random

def sha3_256(data: bytes) -> bytes:
    """计算 SHA3-256 哈希"""
    return hashlib.sha3_256(data).digest()

def find_proof(sender: bytes, challenge: bytes, difficulty: int = 3) -> list:
    """寻找满足条件的 proof 并返回字节数组"""
    while True:
        # 随机生成 8 字节的 proof
        proof = random.randbytes(8)

        # 构建 full_proof = proof + sender + challenge
        full_proof = proof + sender + challenge

        # 计算 SHA3-256 哈希
        hash_result = sha3_256(full_proof)

        # 检查前 difficulty 个字节的和是否为 0
        if sum(hash_result[:difficulty]) == 0:
            print(f"Proof found (byte array): {[b for b in proof]}")
            return [b for b in proof]

# 将 sender 转换为字节数组(16 进制字符串转为 bytes)
sender_hex = "1546f533333b358a8edddd38a8e8967583883e82ca7de604d5bba15e20e493d2"
sender_bytes = bytes.fromhex(sender_hex)

# 使用提供的 challenge_bytes
challenge_bytes = bytes(
    [25, 231, 108, 165, 4, 197, 165, 250, 94, 33, 74, 69, 252, 166, 192, 88, 23, 27, 163, 51, 246, 218, 137, 123, 130,
     115, 16, 148, 80, 77, 90, 185, 5, 99, 89, 97, 92, 126, 3, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0])

# 计算 proof 并输出为字节数组
proof = find_proof(sender_bytes, challenge_bytes)
print(f"Calculated proof as byte array: {proof}")

核心逻辑,find_proof 函数

<!--StartFragment-->

  • 该函数尝试通过不断生成随机的 8 字节 proof,并与 senderchallenge 一起构建 full_proof,然后计算其 SHA3-256 哈希值。

  • 计算哈希后,它检查哈希结果前 difficulty 个字节的和是否为 0。如果条件满足(即 sum(hash_result[:difficulty]) == 0),表示找到了符合条件的 proof,函数将其返回。

  • random.randbytes(8) 用于生成一个随机的 8 字节 proof。

<!--EndFragment-->

<!--StartFragment-->

1.2.3 用Sui CLI执行get_flag函数

用Cli命令调用get_flag函数,验证 proof证明是否正确。

PS D:\data\web3\letsmove> sui client call --package 0x097a3833b6b5c62ca6ad10f0509dffdadff7ce31e1d86e63e884a14860cedc0f --module lets_move --function get_flag --args '[56, 171, 212, 27, 54, 49, 33, 217]' LeonDev1024 0x19e76ca504c5a5fa5e214a45fca6c058171ba333f6da897b82731094504d5ab9 0x8 --gas-budget 100000000
[warn] Client/Server api version mismatch, client api version : 1.37.1, server api version : 1.38.2
Transaction Digest: 2y7NhUaRJ3mJDenJSJPBTQWVfFkawsTBo6AdHLGPWAf4

二、总结

  1. 学习收获

    • 掌握了Move合约的结构与核心特性。
    • 深入理解了哈希函数与挑战验证逻辑。
    • 学会通过BCS序列化与随机数生成进行智能合约交互。
  2. 解题关键

    • 理解Challenge对象的序列化形式。
    • 动态计算符合条件的proof。
  3. 适用场景

    本次挑战为区块链开发中的密码学验证提供了实践经验,可应用于密码学协议验证、区块链挑战任务开发等领域。

<!--EndFragment-->

本次Move共学系列笔记

Move共学-TASK4完成游戏的上链部署

Move共学-TASK5实现一个最简单的Swap

Move共学-TASK6用Sui SDK和Navi SDK完成一个自定义的PTB模块

Move共学-TASK7完成 MoveCTF Check in挑战

三、官方资源

  • Sui 官方文档:Sui 的官方网站提供了全面且权威的文档,涵盖了 Sui 区块链的基本概念、架构、Move 语言的语法、特性,以及在 Sui 上开发智能合约的详细指南等内容,这是深入理解 Sui Move 的基础资料,网址为<https://docs.sui.io/> 。
  • MoveCTF 2024 官方网站:由 MoveBit 主办、Sui 独家赞助的 MoveCTF 2024 竞赛网站,提供了竞赛的具体信息、规则、题目示例等,可通过<https://movectf2024.movebit.xyz/>访问

关注《HOH水分子》公众号,我们将持续分享和制作变成语言教程,让大家对编程产生化学反应。

<!--StartFragment-->

b4AQIzN06730e45415811.webp

<!--EndFragment-->

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
LeonDev1024
LeonDev1024
0x98dE...1DB4
江湖只有他的大名,没有他的介绍。