Rust fixme 3
Goal: the third Rust project in the set. Everything the first two taught is already applied correctly, and it still refuses to compile.
Approach
Section titled “Approach”Same opening as before: fetch, unpack, read src/main.rs. The toolchain went on the box during the first challenge, so cargo is already there.
┌──(Idan@Kali)-[~/Rust_fixme_3]└─> wget https://challenge-files.picoctf.net/c_verbal_sleep/dcdaf491b35c1d0f5075e9583edbbb7aaea1dffb6ad32bc000e4d87b5200ff7b/fixme3.tar.gz┌──(Idan@Kali)-[~/Rust_fixme_3]└─> gunzip fixme3.tar.gz┌──(Idan@Kali)-[~/Rust_fixme_3]└─> tar -xf fixme3.tarThe file is longer than the previous two and, unusually for this set, already correct in every way they taught: decrypt takes a &mut String, main hands it &mut party_foul, and the binding is declared let mut. What is different is the decryption body. Rather than reading the Vec<u8> that decrypt_vec hands back, it pulls a raw pointer and a length out of that vector and rebuilds a slice from the pair, with a comment block above it pointing at the Rust book’s chapter on unsafe code.
That comment is not decoration, it is the hint, and what it points at is directly below it. The unsafe { and its closing } are commented out, and the statements they were meant to wrap are not.
// 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)); // }With the braces commented out the body is still live, it is simply no longer inside an unsafe block, so std::slice::from_raw_parts is called from ordinary safe code. cargo build rejects exactly that one line as E0133: the call is unsafe and requires an unsafe function or block. Deleting the two // is the entire fix.
┌──(Idan@Kali)-[~/Rust_fixme_3/fixme3]└─> cargo run Compiling rust_proj v0.1.0 Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s Running `target/debug/rust_proj`Using memory unsafe languages is a: PARTY FOUL! Here is your flag: <flag>picoCTF{n0w_y0uv3_f1x3d_1h3m_411} Why it works
Section titled “Why it works”unsafe does not switch anything off. The borrow checker, the type system and the ownership rules all still apply inside the block. What the keyword unlocks is a short list of five abilities the compiler cannot verify on its own, one of which is calling a function marked unsafe. from_raw_parts is on that list because nothing in its signature can prove the pointer is valid for the length given, correctly aligned, and not going to be mutated while the slice is alive. Those are obligations, and unsafe is where the author accepts them.
The joke of this particular file is that the obligations are already met and always were. The pointer and the length come from a vector that stays alive for the whole block, and the slice being rebuilt is the same one &decrypted_buffer would have handed over for free. So the compiler’s objection here is bookkeeping rather than substance: the code was sound the entire time, it just was not allowed to say so.