【picoCTF】Rust fixme 2 - 所有権と参照のエラーを修正してXOR復号

問題概要

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

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

問題文

picoCTF Rust fixme 2

解説

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


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

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

展開されたディレクトリに移動し、fixme2/src/main.rs の内容を確認します。
$ cd fixme2
$ cat src/main.rs

use xor_cryptor::XORCryptor;

fn decrypt(encrypted_buffer:Vec<u8>, borrowed_string: &String){ // How do we pass values to a function that we want to change?

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

  // Editing our borrowed value
  borrowed_string.push_str("PARTY FOUL! Here is your flag: ");

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

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


fn main() {
  // Encrypted flag values
  let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1", "61", "25", "0d", "c4", "60", "f2", "12", "a0", "18", "03", "51", "03", "36", "05", "0e", "f9", "42", "5b"];

  // 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();

  let party_foul = String::from("Using memory unsafe languages is a: "); // Is this variable changeable?
  decrypt(encrypted_buffer, &party_foul); // Is this the correct way to pass a value to a function so that it can be changed?
}
この main.rs は「借用(borrow)と可変参照(mutable reference)」を修正して、フラグを表示できるようにする問題です。

まずは一度 cargo run を実行して、コンパイルエラーになることを確認します。
$ cargo run

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

コード中のコメントが、そのまま修正ポイントになっています。


修正ポイント1: 変更したい値は &String ではなく &mut String

borrowed_string.push_str(...) のように文字列を変更するには、引数として 可変参照 を受け取る必要があります。

元のコード

fn decrypt(encrypted_buffer: Vec<u8>, borrowed_string: &String)

修正後

fn decrypt(encrypted_buffer: Vec<u8>, borrowed_string: &mut String)

修正ポイント2: 呼び出し側も mut + &mut にする

関数に &mut String を渡すには、呼び出し側の変数も mut で宣言し、&mut を付けて渡します。

元のコード

let party_foul = String::from("Using memory unsafe languages is a: ");
decrypt(encrypted_buffer, &party_foul);

修正後

let mut party_foul = String::from("Using memory unsafe languages is a: ");
decrypt(encrypted_buffer, &mut party_foul);

修正ポイント3: return; はこのままでOK

decrypt() の戻り値は ()(何も返さない)なので、失敗時は return; で関数を抜ければOKです。

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

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

fn decrypt(encrypted_buffer: Vec<u8>, borrowed_string: &mut String) {
  // Key for decryption
  let key = String::from("CSUCKS");

  // Editing our borrowed value
  borrowed_string.push_str("PARTY FOUL! Here is your flag: ");

  // 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);
  borrowed_string.push_str(&String::from_utf8_lossy(&decrypted_buffer));
  println!("{}", borrowed_string);
}

fn main() {
  // Encrypted flag values
  let hex_values = [
    "41", "30", "20", "63", "4a", "45", "54", "76", "01", "1c", "7e", "59", "63", "e1",
    "61", "25", "0d", "c4", "60", "f2", "12", "a0", "18", "03", "51", "03", "36", "05",
    "0e", "f9", "42", "5b",
  ];

  // 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();

  let mut party_foul = String::from("Using memory unsafe languages is a: ");
  decrypt(encrypted_buffer, &mut party_foul);
}

実行します。

$ cd fixme2
$ cargo run

出力例

Using memory unsafe languages is a: PARTY FOUL! Here is your flag: picoCTF{xxxxx}

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


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

tar

tar -xvf fixme2.tar.gz

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

cargo run

cargo run

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


まとめ

picoCTFの「Rust fixme 2」問題では、関数に渡した文字列を変更できるように 可変参照(&mut を使うのがポイントでした。

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

  • 関数内で値を変更したいなら、引数は &T ではなく &mut T
  • 呼び出し側も let mut ... で宣言し、&mut を付けて渡す

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

NEXT
次におすすめ

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

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