Sui.

Publicación

Comparte tu conocimiento.

article banner.
harry phan.
Jun 30, 2025
Artículo

Una guía para hackers para conquistar Move CTF 2025 (semana 1)

En cuanto a la seguridad de la cadena de bloques, no hay mejor campo de entrenamiento que el desafío Capture The Flag (CTF).

Los desarrolladores forjan sus habilidades en el fragor de la competencia amistosa. El desafío Move CTF 2025 de la primera semana en la cadena de bloques Sui es un ejemplo perfecto diseñado para poner a prueba tu comprensión del lenguaje Move.

Pasaremos de configurar el entorno a analizar las defensas del contrato inteligente, elaborar un script de resolución y, por último, ejecutar la transacción que revele el codiciado premio: CTF {LetSmovectF_week1}.

Tanto si eres un novato en Move como si eres un investigador de seguridad experimentado, este tutorial te proporcionará una guía práctica para mejorar tus habilidades. ¡Vamos a sumergirnos!

Paso 1: Reconocimiento: entender el objetivo

Antes de escribir una sola línea de código, nuestro primer trabajo es entender el campo de batalla. El desafío se centra en un único módulo de Moveweek1::challenge, que necesitamos analizar.

public struct Challenge has key {
    id: UID,
    secret: String,
    current_score: u64,
    round_hash: vector<u8>,
    finish: u64,
}

Nuestra misión, en pocas palabras, es llamar con éxito a la get_flagfunción de entrada. Una llamada exitosa activará una FlagEventtransmisión de nuestra victoria y nuestra bandera a toda la cadena.

Sin embargo, la get_flagfunción está protegida por una serie de cinco comprobaciones de validación distintas. Para tener éxito, debemos satisfacerlos todos en una sola transacción.

Paso 2: Deconstruir las defensas: los cinco obstáculos

get_flagLa assert!función es la de una fortaleza, y sus murallas están hechas de imponencias. Vamos a desglosar cada uno de estos obstáculos uno por uno.

Obstáculo 1: La verificación de la puntuación

La tarea: Calcular el hash sha3_256 de la cadena challenge.secret actual. La puntuación correcta es un número u64 derivado de los 4 primeros bytes de ese hash (siguiendo un orden de bytes grande).

El concepto: Se trata de un desafío criptográfico básico para garantizar que puedes realizar correctamente el hash y la manipulación de bytes, tal como exige el contrato.

Obstáculo 2: El control de conjeturas (un acertijo para demostrar que tienes trabajo)

La tarea: encontrar una suposición (un vector de bytes, que actúa como un no) de forma que, al sumar la conjetura y el secreto (sha3_256 (guess || secret)), los dos primeros bytes del hash resultante coincidan con los dos primeros bytes del challenge.round_hash.

El concepto: se trata de un mecanismo clásico de prueba de trabajo (PoW). No se puede resolver matemáticamente; se requiere un esfuerzo computacional («fuerza bruta») para encontrar una suposición válida, lo que demuestra que has gastado recursos para resolverla.

Obstáculo 3: La comprobación de hash_input (vinculación de identidad)

La tarea: proporcionar un hash que combine correctamente el secreto y tu github_id personal. El truco consiste en que primero hay que serializar el secreto mediante la serialización canónica binaria (bcs: :to_bytes). Esto es diferente a usar solo sus bytes sin procesar, ya que BCS antepone los datos con su longitud. El cálculo final es sha3_256 (bcs (secret) || github_id).

El concepto: Esta verificación vincula tu solución con tu identificador único, lo que evita que otros simplemente vuelvan a reproducir tu transacción exitosa. El uso del BCS es un patrón habitual en Move y es un detalle clave a tener en cuenta.

Obstáculos 4 y 5: Comprobar la semilla y el número mágico

La tarea: Estos dos parámetros están vinculados.

la semilla se calcula como longitud (secreta) * 2.

magic_number se calcula como (puntuación% 1000) + semilla.

El concepto: Se trata de acertijos de lógica que ponen a prueba tu habilidad para leer el código de Move con atención y replicar sus sencillas operaciones aritméticas.

Paso 3: Configurar su kit de herramientas: entorno e implementación

Con la teoría entendida, es hora de ensuciarnos las manos.

Primero, asegúrese de que su CLI de Sui esté instalada y configurada para la red de prueba.

# 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

A continuación, cree y publique el contrato Move en la red de pruebas de Sui. Esto compilará el código y lo implementará, creando el Challengeobjeto compartido inicial con el que necesitamos interactuar.

# Build the project
sui move build

# Publish the contract to the testnet
sui client publish --gas-budget 100000000

Tras una publicación exitosa, la CLI generará los resultados, incluidos dos datos críticos que debe guardar:

ID del paquete: la dirección de tu contrato recién publicado. ID de objeto: la dirección del Challengeobjeto compartido creado por la initfunción.

Paso 4: Falsificar las claves: crear el solucionador de Python

get_flagAhora, necesitamos calcular los argumentos correctos a los que pasarlos. Hacerlo manualmente es tedioso, así que escribiremos un script de Python para automatizarlo. Este script lee la inicialsecret, «LetsMovectF_Week1", y calcula los cinco parámetros necesarios.

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}")

Paso 5: El atraco: invocar a get_flag y capturar el premio Con nuestros parámetros calculados, usaremos otro script de Python para construir y ejecutar el comando final de llamada al cliente sui. Esto automatiza el tedioso proceso de formatear los argumentos de la línea de comandos.

call_get_flag.py (resumen)

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)

Al ejecutar este script, se envía la transacción. Si todos nuestros parámetros son correctos, el resultado mostrará el estado: Éxito y, lo que es más importante, el FlagEvent emitido:

"ParsedJSON": {
  "flag": "CTF{Letsmovectf_week1}",
  "github_id": "qiaopengjun5162",
  "rank": "1",
  "success": true
}

¡Éxito! La bandera está capturada.

Un CTF bien diseñado no termina después de una resolución. Tras la llamada exitosa, la función get_flag usa el objeto Random para generar un nuevo secreto y actualiza el objeto Challenge.

Lo confirmé consultando nuevamente el objeto, encontrando un nuevo secreto y repitiendo el proceso. Mis scripts de resolución eran reutilizables. Solo tenía que actualizar la variable secreta, volver a ejecutar el cálculo y volver a ejecutar la llamada para capturar el indicador por segunda y tercera vez, y así subir de rango.

https://suiscan.xyz/testnet/tx/AzJywLk8zv1Fx5Pc8p4ZrZwFZQ9hmTbtvweo14MVn8fZ

Conclusión: algo más que una bandera

Abordar el desafío de la primera semana de Move CTF 2025 fue una experiencia de aprendizaje increíble. Me obligó a ir más allá de la teoría e interactuar directamente con la cadena de bloques Sui, reforzando conceptos clave como:

Lógica de contratos inteligentes: es fundamental leer y comprender detenidamente el código de Move.

La criptografía en la práctica: aplicar el hash (sha3_256) y comprender la manipulación de bytes.

Mecánica en cadena: la importancia de los formatos de serialización específicos, como el BCS.

Automatización: uso de scripts para hacer que las tareas complejas y repetibles sean manejables.

Este desafío es un testimonio del poder del aprendizaje práctico. Si quieres desarrollar tus habilidades en el desarrollo y la seguridad de la cadena de bloques, te recomiendo encarecidamente que te lances al

  • Sui
2
Cuota
Comentarios
.

Sui is a Layer 1 protocol blockchain designed as the first internet-scale programmable blockchain platform.

610Publicaciones1335Respuestas
Sui.X.Peera.

Gana tu parte de 1000 Sui

Gana puntos de reputación y obtén recompensas por ayudar a crecer a la comunidad de Sui.

Campaña de RecompensasJulio