【picoCTF】Rust fixme 1 - 基本的なRust構文エラーを直してXOR復号でフラグ表示

問題概要

picoCTFの「Rust fixme 1」という問題の解説記事です。

  • カテゴリ: General Skills
  • 難易度: Easy

問題文

picoCTF Rust fixme 1

解説

この問題では、与えられたRustコードのコンパイルエラーを修正して、隠されたフラグを表示することが求められます。


ステップ1: ソースコードの確認

まずは、提供されたRustソースコード fixme1.tar.gz を展開し、内容を確認します。
$ tar -xvf fixme1.tar.gz
fixme1/
fixme1/Cargo.toml
fixme1/Cargo.lock
fixme1/src/
fixme1/src/main.rs
展開後、fixme1/src/main.rs ファイルを確認します。
$ cat fixme1/src/main.rs

use xor_cryptor::XORCryptor;

fn main() {
  // Key for decryption
  let key = String::from("CSUCKS") // How do we end statements in Rust?

  // Encrypted flag values
  let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1", "61", "25", "7f", "5a", "60", "50", "11", "38", "1f", "3a", "60", "e9", "62", "20", "0c", "e6", "50", "d3", "35"];

  // Convert the hexadecimal strings to bytes and collect them into a vector
  let encrypted_buffer: Vec<u8> = hex_values.iter()
    .map(|&hex| u8::from_str_radix(hex, 16).unwrap())
    .collect();

  // Create decrpytion object
  let res = XORCryptor::new(&key);
  if res.is_err() {
    ret; // How do we return in rust?
  }
  let xrc = res.unwrap();

  // Decrypt flag and print it out
  let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer);
  println!(
    ":?", // How do we print out a variable in the println function? 
    String::from_utf8_lossy(&decrypted_buffer)
  );
}

この問題は、問題文・コメントの通り「Rustの文法を直してね」というタイプのものです。 まずは一度 cargo run して、コンパイルエラーになることを確認します。
$ cd fixme1
$ cargo run

ステップ2: コンパイルエラーを修正

コード中にある「How do we ... ?」のコメントが、そのまま修正ポイントを示しています。


修正ポイント1: 文末のセミコロン(;

Rustでは、let などの 文(statement) は基本的に末尾に ; が必要です。
以下の行は ; が無いのでエラーになります。
let key = String::from("CSUCKS")

修正後

let key = String::from("CSUCKS");

修正ポイント2: retreturn

Rustに ret というキーワードはありません。 関数から抜けるには return を使います。
今回 main() は戻り値が ()(何も返さない)なので、失敗時は単に return; でOKです。

修正後

if res.is_err() {
  return;
}
なお、よりRustらしく書くなら match / if let / ? なども使えますが、ここでは最小修正で進めます。

修正ポイント3: println! のフォーマット文字列

Rustの println! は、Pythonのf-stringのように「フォーマット指定子」を使います。
  • {}: 表示用(Display)
  • {:?}: デバッグ表示(Debug)
元のコードは ":?" となっており、{} が無いので正しく表示できません。
今回 String::from_utf8_lossy(&decrypted_buffer) は文字列として表示したいので、{} を使うのが分かりやすいです。

修正後

println!(
  "{}",
  String::from_utf8_lossy(&decrypted_buffer)
);

ステップ3: 修正後のコードで実行

上の修正点を反映すると、main.rs は次のようになります。
use xor_cryptor::XORCryptor;

fn main() {
  // Key for decryption
  let key = String::from("CSUCKS");

  // Encrypted flag values
  let hex_values = [
    "41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1",
    "61", "25", "7f", "5a", "60", "50", "11", "38", "1f", "3a", "60", "e9", "62", "20",
    "0c", "e6", "50", "d3", "35",
  ];

  // Convert the hexadecimal strings to bytes and collect them into a vector
  let encrypted_buffer: Vec<u8> = hex_values
    .iter()
    .map(|&hex| u8::from_str_radix(hex, 16).unwrap())
    .collect();

  // Create decrpytion object
  let res = XORCryptor::new(&key);
  if res.is_err() {
    return;
  }
  let xrc = res.unwrap();

  // Decrypt flag and print it out
  let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer);
  println!("{}", String::from_utf8_lossy(&decrypted_buffer));
}

実行します。

$ cd fixme1
$ cargo run

出力例

picoCTF{xxxxx}

※フラグはマスクしています。


使用したコマンドの軽い解説

tar

tar -xvf fixme1.tar.gz

配布されたアーカイブを展開するコマンドです。

cargo run

cargo run

Rustのビルド&実行をまとめて行います。 コンパイルエラーもここで確認できます。


まとめ

picoCTFの「Rust fixme 1」問題では、Rustの基本文法(セミコロン、returnprintln! のフォーマット)を直すことで、復号処理が動きフラグを取得できました。

▼ポイントは以下の通りです。

  • Rustの文は基本的に ; で終える
  • 関数から抜けるなら return;
  • println!"{}""{:?}" などのフォーマット指定子を使う

閲覧ありがとうございました!

NEXT
次におすすめ

【picoCTF】Rust fixme 3 - ライフタイムと可変参照を整理してXOR復号

カテゴリ: General Skills難易度: Easy#picoCTF
次の記事へ →
同じカテゴリ/難易度/picoCTFでの表示順が近い記事を優先しておすすめしています。