Rust fixme2 writeup
Rust fixme2 について
前回のRust fixme1 writeupからの
自分の復習も兼ねて、この問題の writeup (解説) を書きます。
問題はこちらです。
Writeup
問題文は以下の通りです。

この問題も前の問題と同様、問題文の冒頭に赤色ハッチングで示されているとおり、 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箇所になります。
decrypt関数の引数borrowed_stringを&Stringから&mut Stringに変更するmain関数内のparty_foul変数をlet party_foulからlet mut party_foulに変更するmain関数内のdecrypt関数の呼び出しの引数を&party_foulから&mut party_foulに変更する
以上の処置を行うことにより、フラグ picoCTF{4r3_y0u_h4v1n5_fun_y31?} を取得できます。