Пост
Поделитесь своими знаниями.
Руководство для хакеров по освоению Move CTF 2025 (неделя 1)
В сфере безопасности блокчейна нет лучшей учебной площадки, чем соревнование Capture The Flag (CTF).
Разработчики совершенствуют свои навыки в условиях дружеской конкуренции. Конкурс Move CTF 2025 Week 1 на блокчейне Sui — прекрасный пример, призванный проверить ваше понимание языка Move.
Мы перейдем от настройки среды к анализу защит смарт-контракта, составлению сценария решателя и, наконец, выполнению транзакции, в результате которой будет разыгран желанный приз — CTF {letSmovectF_Week1}.
Независимо от того, являетесь ли вы новичком в Move или опытным исследователем в области безопасности, это пошаговое руководство послужит практическим руководством по совершенствованию ваших навыков. Давайте погрузимся в процесс!
Шаг 1. Разведка — понимание цели
Прежде чем написать хоть одну строчку кода, наша первая задача — понять поле битвы. Задача сосредоточена на одном модуле Moveweek1::challenge
, который нам нужно проанализировать.
public struct Challenge has key {
id: UID,
secret: String,
current_score: u64,
round_hash: vector<u8>,
finish: u64,
}
Проще говоря, наша миссия заключается в успешном вызове get_flag
функции ввода. Успешный звонок вызовет FlagEvent
сообщение о нашей победе и флаге на всю сеть.
Однако get_flag
функция защищена серией из пяти отдельных валидационных проверок. Чтобы добиться успеха, мы должны выполнить все из них за одну транзакцию.
Шаг 2: Разрушение защитных сооружений — пять препятствий
get_flag``assert!
Функция — это крепость, стены которой построены из веток. Давайте разберемся с каждым из этих препятствий по очереди.
Препятствие 1: проверка очков
Задача: вычислить хэш sha3_256 текущей строки challenge.secret. Точный результат — это число u64, полученное из первых 4 байтов этого хэша (в порядке байтов с обратным порядком байтов).
Концепция: это базовая криптографическая задача, призванная обеспечить правильное хеширование и манипулирование байтами в соответствии с требованиями контракта.
Препятствие 2: проверка догадок (головоломка с доказательством работоспособности)
Задание: найдите предположение (вектор байтов, выполняющий роль числительного числа), при котором при сложении предположения и секрета (sha3_256 (guess || secret)) первые два байта полученного хеша совпадали с первыми двумя байтами challenge.round_hash.
Концепция: это классический механизм Proof-of-Work (PoW). Его невозможно решить математически; чтобы найти верное предположение и доказать, что вы потратили на его решение ресурсы, требуются вычислительные усилия («грубая сила»).
Препятствие 3: проверка hash_input (привязка идентификационных данных)
Задача: укажите хэш, в котором правильно сочетаются секрет и ваш личный github_id. Суть в том, что сначала секрет нужно сериализовать с помощью двоичной канонической сериализации (bcs: :to_bytes). Это не то же самое, что просто использовать необработанные байты, поскольку BCS предварительно указывает длину данных. Окончательный расчет — sha3_256 (bcs (секрет) || github_id).
Концепция: эта проверка связывает ваше решение с вашим уникальным идентификатором, не позволяя другим просто воспроизвести вашу успешную транзакцию. Использование BCS — обычная практика в Move и одна из ключевых деталей, на которую следует обратить внимание.
Препятствия 4 и 5: проверка скорости и магических чисел
Задание: эти два параметра связаны между собой.
начальное значение рассчитывается как длина (в секрете) * 2.
magic_number рассчитывается по формуле (оценка% 1000) + семя.
Концепция: это логические головоломки, которые проверяют вашу способность внимательно читать код Move и повторять его простые арифметические операции.
Шаг 3. Настройка набора инструментов — среда и развертывание
Когда теория понята, пришло время испачкать руки.
Во-первых, убедитесь, что интерфейс командной строки Sui установлен и настроен для тестовой сети.
# Check your Sui version
sui --version
> sui 1.50.1-homebrew
# Check your active environment
sui client envs
> testnet
# Check your active address and gas balance
sui client active-address
sui client gas
Затем создайте и опубликуйте контракт Move в тестовой сети Sui. Это позволит скомпилировать код и развернуть его, создав исходный общий Challenge
объект, с которым нам нужно будет взаимодействовать.
# Build the project
sui move build
# Publish the contract to the testnet
sui client publish --gas-budget 100000000
После успешной публикации интерфейс командной строки выведет результаты, включая две важные части информации, которые необходимо сохранить:
Идентификатор пакета: адрес вашего недавно опубликованного контракта.
Идентификатор объекта: адрес общего Challenge
объекта, созданного init
функцией.
Шаг 4: Подделка ключей — создание решателя Python
get_flag
Теперь нам нужно вычислить правильные аргументы для перехода. Делать это вручную утомительно, поэтому мы напишем скрипт на Python для автоматизации. Этот скрипт считывает исходный текст secret
«letSmovectF_Week1» и вычисляет все пять обязательных параметров.
import hashlib
# --- Configuration ---
github_id = b"qiaopengjun5162"
secret = b"Letsmovectf_week1"
# --- 1. Calculate Score ---
hash_bytes = hashlib.sha3_256(secret).digest()
score = int.from_bytes(hash_bytes[:4], 'big')
print(f"✅ Score: {score}")
# --- 2. Solve Proof-of-Work for Guess ---
target_prefix = hash_bytes[:2]
found_guess = None
for i in range(1000000): # Brute-force loop
guess_candidate = f"guess{i}".encode()
combined_data = guess_candidate + secret
random_hash = hashlib.sha3_256(combined_data).digest()
if random_hash[:2] == target_prefix:
found_guess = guess_candidate
print(f"✅ Guess Found: {found_guess.decode()}")
print(f" (as hex): {found_guess.hex()}")
break
# --- 3. Calculate Hash Input (with BCS) ---
# BCS serialization for a string prepends its length as a ULEB128 integer.
# For short strings, this is just a single byte representing the length.
bcs_encoded_secret = bytes([len(secret)]) + secret
bcs_input_data = bcs_encoded_secret + github_id
hash_input = hashlib.sha3_256(bcs_input_data).digest()
print(f"✅ Hash Input (hex): {hash_input.hex()}")
# --- 4 & 5. Calculate Seed and Magic Number ---
secret_len = len(secret)
seed = secret_len * 2
magic_number = (score % 1000) + seed
print(f"✅ Seed: {seed}")
print(f"✅ Magic Number: {magic_number}")
Шаг 5: Ограбление: вызов get_flag и получение приза После вычисления параметров мы воспользуемся другим скриптом Python для создания и выполнения последней команды вызова клиента sui. Это автоматизирует утомительный процесс форматирования аргументов командной строки.
call_get_flag.py (краткое описание)
import subprocess
# --- Configuration (using values from previous script) ---
package_id = "0x804c92e4eef709b83f135d6cc667005ce35d7eccd49384570cbd7b1b40e32434"
challenge_id = "0xd28bc35560711a8b6ca93e2bf8e353fa6e17c15cbc426c48ece1ade9d83ce5ee"
random_id = "0x8" # Default Random object on Sui
gas_budget = "10000000"
# --- Calculated Parameters ---
score = 1478524421
guess = [103, 117, 101, 115, 115, 55, 54, 57, 48, 56] # b'guess76908'
hash_input = [...] # The full byte array from the solver
github_id = "qiaopengjun5162"
magic_number = 455
seed = 34
# --- Construct and Execute the Sui CLI Command ---
command = [
"sui", "client", "call",
"--package", package_id,
"--module", "challenge",
"--function", "get_flag",
"--args",
str(score), str(guess), str(hash_input), f'"{github_id}"',
str(magic_number), str(seed), challenge_id, random_id,
"--gas-budget", gas_budget
]
# Execute the command and print the output
result = subprocess.run(command, capture_output=True, text=True, check=True)
print(result.stdout)
После запуска этого скрипта транзакция отправляется. Если все наши параметры верны, в выходных данных будет указано Status: Success и, самое главное, сгенерированное событие FlagEvent:
"ParsedJSON": {
"flag": "CTF{Letsmovectf_week1}",
"github_id": "qiaopengjun5162",
"rank": "1",
"success": true
}
Успех! Флаг захвачен.
Хорошо продуманный CTF не закончится после одного решения. После успешного вызова функция get_flag использует объект Random для генерации нового секрета и обновляет объект Challenge.
Я подтвердил это, снова запросив объект, найдя новый секрет и повторив процесс. Мои скрипты решателя можно было использовать повторно: мне просто нужно было обновить секретную переменную, перезапустить вычисление и снова выполнить вызов, чтобы во второй и третий раз захватить флаг и подняться по карьерной лестнице.
https://suiscan.xyz/testnet/tx/AzJywLk8zv1Fx5Pc8p4ZrZwFZQ9hmTbtvweo14MVn8fZ
Вывод: больше, чем просто флаг
Решение задачи Move CTF 2025 на первой неделе стало невероятным опытом. Это заставило меня выйти за рамки теории и напрямую заняться блокчейном Sui, укрепив ключевые концепции, такие как:
Логика смарт-контрактов. Внимательное чтение и понимание кода Move имеют первостепенное значение.
Криптография на практике: применение хеширования (sha3_256) и понимание манипуляций с байтами.
Механика цепочки: важность специальных форматов сериализации, таких как BCS.
Автоматизация: использование скриптов для управления сложными повторяемыми задачами.
Эта задача свидетельствует о силе практического обучения. Если вы хотите улучшить свои навыки в области разработки и безопасности блокчейна, я настоятельно рекомендую вам приступить к
- Sui
Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.
Заработай свою долю из 1000 Sui
Зарабатывай очки репутации и получай награды за помощь в развитии сообщества Sui.

- ... SUIBigSneh+1396
- ... SUISuiLover+1333
- ... SUI0xduckmove+1207
- ... SUIThorfin+1202
- ... SUIOwen+970
- ... SUIharry phan+847
- ... SUItheking+742
- Почему BCS требует точного порядка полей для десериализации, когда структуры Move содержат именованные поля?53
- «Ошибки проверки нескольких источников» в публикациях модуля Sui Move — автоматическое устранение ошибок43
- Сбой транзакции Sui: объекты, зарезервированные для другой транзакции25
- Как ограничения возможностей взаимодействуют с динамическими полями в гетерогенных коллекциях?05