問題概要
picoCTFの「Rust fixme 3」という問題の解説記事です。
- カテゴリ: General Skills
- 難易度: Easy
問題文
解説
この問題では、与えられたRustコードのコンパイルエラーを修正して、隠されたフラグを表示することが求められます。
ステップ1: ソースコードの確認
まずは、提供されたRustソースコード
fixme3.tar.gz を展開し、内容を確認します。$ tar -xvf fixme3.tar.gz
fixme3/
fixme3/Cargo.toml
fixme3/Cargo.lock
fixme3/src/
fixme3/src/main.rs
展開されたディレクトリに移動し、
fixme3/src/main.rs の内容を確認します。$ cd fixme3
$ cat src/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 decryption object
let res = XORCryptor::new(&key);
if res.is_err() {
return;
}
let xrc = res.unwrap();
// Did you know you have to do "unsafe operations in Rust?
// https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
// Even though we have these memory safe languages, sometimes we need to do things outside of the rules
// This is where unsafe rust comes in, something that is important to know about in order to keep things in perspective
// unsafe {
// Decrypt the flag operations
let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer);
// Creating a pointer
let decrypted_ptr = decrypted_buffer.as_ptr();
let decrypted_len = decrypted_buffer.len();
// Unsafe operation: calling an unsafe function that dereferences a raw pointer
let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len);
borrowed_string.push_str(&String::from_utf8_lossy(decrypted_slice));
// }
println!("{}", borrowed_string);
}
fn main() {
// Encrypted flag values
let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "12", "90", "7e", "53", "63", "e1", "01", "35", "7e", "59", "60", "f6", "03", "86", "7f", "56", "41", "29", "30", "6f", "08", "c3", "61", "f9", "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();
let mut party_foul = String::from("Using memory unsafe languages is a: ");
decrypt(encrypted_buffer, &mut party_foul);
}
この
main.rs は「unsafeブロック(unsafe { ... })」がテーマの問題で、コメントの通り unsafe操作を正しく囲めていない ためにコンパイルエラーになります。まずは一度
cargo run を実行して、コンパイルエラーになることを確認します。$ cargo run
ステップ2: コンパイルエラーを修正
今回の主なエラーは、次の行です。
let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len);
std::slice::from_raw_parts は raw pointer(生ポインタ)を dereference してスライスを作る ため、Rustでは unsafe な関数として扱われます。
ところが、このコードは unsafe { ... } がコメントアウトされているため、
call to unsafe function is unsafe and requires unsafe function or block のようなエラーになります。修正ポイント: unsafeブロックを復活させる
最小修正として、コメントアウトされている
unsafe { と } を元に戻します。元のコード(コメントアウトされている)
// unsafe {
...
// }
修正後
unsafe {
// Decrypt the flag operations
let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer);
// Creating a pointer
let decrypted_ptr = decrypted_buffer.as_ptr();
let decrypted_len = decrypted_buffer.len();
// Unsafe operation: calling an unsafe function that dereferences a raw pointer
let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len);
borrowed_string.push_str(&String::from_utf8_lossy(decrypted_slice));
}
ステップ3: 修正後のコードで実行
修正後の
main.rs は次のようになります(unsafe を有効化)。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 decryption object
let res = XORCryptor::new(&key);
if res.is_err() {
return;
}
let xrc = res.unwrap();
unsafe {
// Decrypt the flag operations
let decrypted_buffer = xrc.decrypt_vec(encrypted_buffer);
// Creating a pointer
let decrypted_ptr = decrypted_buffer.as_ptr();
let decrypted_len = decrypted_buffer.len();
// Unsafe operation: calling an unsafe function that dereferences a raw pointer
let decrypted_slice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len);
borrowed_string.push_str(&String::from_utf8_lossy(decrypted_slice));
}
println!("{}", borrowed_string);
}
fn main() {
// Encrypted flag values
let hex_values = ["41", "30", "20", "63", "4a", "45", "54", "76", "12", "90", "7e", "53", "63", "e1", "01", "35", "7e", "59", "60", "f6", "03", "86", "7f", "56", "41", "29", "30", "6f", "08", "c3", "61", "f9", "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();
let mut party_foul = String::from("Using memory unsafe languages is a: ");
decrypt(encrypted_buffer, &mut party_foul);
}
実行します。
$ cd fixme3
$ cargo run
出力例
Using memory unsafe languages is a: PARTY FOUL! Here is your flag: picoCTF{xxxxx}
※フラグはマスクしています。
使用したコマンドの軽い解説
tar
tar -xvf fixme3.tar.gz
配布されたアーカイブを展開するコマンドです。
cargo run
cargo run
Rustのビルド&実行をまとめて行います。 コンパイルエラーもここで確認できます。
まとめ
picoCTFの「Rust fixme 3」問題では、raw pointer を扱う処理(
std::slice::from_raw_parts)が unsafe操作 である点がポイントでした。▼ポイントは以下の通りです。
from_raw_partsのような操作はunsafe { ... }で囲う必要があるunsafeは「危険なことを許可する」宣言なので、必要最小限の範囲に留める
閲覧ありがとうございました!
NEXT
次におすすめ
読み終わったら、そのまま次へ
【picoCTF】Time Machine - Git履歴をたどってフラグを復元
カテゴリ: General Skills難易度: Easy#picoCTF
次の記事へ →
同じカテゴリ/難易度/picoCTFでの表示順が近い記事を優先しておすすめしています。