picoCTF Writeup: Rust Fixme 3 - Stepping into Unsafe Rust

picoCTFRustMemory SafetyUnsafe RustCTF Writeup

picoCTF Writeup: Rust Fixme 3 - Stepping into Unsafe Rust

tar -xzvf filename.tar.gz

  • -x (eXtract): Tells tar to extract the files from the archive.
  • -z (gZip): Tells tar to decompress the archive using gzip (since it is a .gz file).
  • -v (Verbose): Optional, but highly recommended. It lists all the files on your screen as they are being extracted so you can see the progress.
  • -f (File): Specifies the filename of the archive. Note: This flag must always be the last one right before the filename.
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);
}
}

So I don't know Rust : ( But I run the program : )


error[E0133]: call to unsafe function `std::slice::from_raw_parts` is unsafe and requires unsafe function or block
  --> src/main.rs:31:31
   |
31 | ...lice = std::slice::from_raw_parts(decrypted_ptr, decrypted_len);
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
   |
   = note: consult the function's documentation for information on how to avoid undefined behavior

I asked Gemini and ...

Gemini said

This is a classic Rust security feature in action! The compiler is doing exactly what it was designed to do: stopping you from interacting with raw memory without explicit permission.

In languages like C or C++, you can freely read from any memory pointer, which often leads to buffer overflows or out-of-bounds read vulnerabilities. Rust prevents this by default. The function std::slice::from_raw_parts takes a raw pointer (decrypted_ptr) and a length (decrypted_len) and turns it into a usable Rust slice. However, the Rust compiler cannot mathematically guarantee that the pointer is actually valid or that the length is accurate.

To fix this, you have to take manual responsibility for the memory safety by wrapping the call in an unsafe block.

let slice = unsafe {
    std::slice::from_raw_parts(decrypted_ptr, decrypted_len)
};

And then...

┌──(nirmal㉿NirmalsFLOWX16)-[~/CTF/RustFixme_3/fixme3]
└─$ cargo run
   Compiling rust_proj v0.1.0 (/home/nirmal/CTF/RustFixme_3/fixme3)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/rust_proj`
Using memory unsafe languages is a: PARTY FOUL! Here is your flag: picoCTF{n0w_y0uv3_f1x3d_1h3m_411}

So in the end I did learn something about Rust though. XD