Rust fixme2 writeup


Rust fixme2 について

前回のRust fixme1 writeupからの
自分の復習も兼ねて、この問題の writeup (解説) を書きます。
問題はこちらです。

Writeup

問題文は以下の通りです。

Rust fixme2

この問題も前の問題と同様、問題文の冒頭に赤色ハッチングで示されているとおり、 picoCTF 内の webshell を使って解くことはできません。
自分で Rust の環境を構築し、フラグを探す必要があります。
私は WSL 上で Microsoft 公式の Devcontainer を利用して解いています。

エラーの修正

今回のエラーは変数の所有権と参照に関するエラーです。
エラー修正前後のコードは以下の通りです。

修正前のコード

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 mutkey = 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?
}

修正後のコード

use xor_cryptor::XORCryptor;

fn decrypt(encrypted_buffer: Vec<u8>, borrowed_string: &mut 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 mut party_foul = String::from("Using memory unsafe languages is a: "); // Is this variable changeable?
    decrypt(encrypted_buffer, &mut party_foul); // Is this the correct way to pass a value to a function so that it can be changed?
}
別解
use xor_cryptor::XORCryptor;

fn decrypt(encrypted_buffer: Vec<u8>, mut borrowed_string: 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 borrowed_string; // 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);
    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: "); // Is this variable changeable?
    party_foul = decrypt(encrypted_buffer, party_foul); // Is this the correct way to pass a value to a function so that it can be changed?
}

ポインターで引数を渡すのではなく、String型を直接引数として渡して処理する方法です。
この方法では String 型の所有権が decrypt 関数に移動するため、 main 関数内の party_foul 変数は decrypt 関数に渡す際にムーブされます。

修正内容

エラーは borrowed_string という変数の所有権と参照に関するエラーです。
String 型を結合させるために push_str を用いています。
push_str メソッドは呼び出し元の String はミュータブルでなければならず、これがエラーの原因です。

よって変更箇所は以下の3箇所になります。

  1. decrypt 関数の引数 borrowed_string&String から &mut String に変更する
  2. main 関数内の party_foul 変数を let party_foul から let mut party_foul に変更する
  3. main 関数内の decrypt 関数の呼び出しの引数を &party_foul から &mut party_foul に変更する

以上の処置を行うことにより、フラグ picoCTF{4r3_y0u_h4v1n5_fun_y31?} を取得できます。