-
-
Save rexim/38c176fe4669ef83db69aca9909d7b7f to your computer and use it in GitHub Desktop.
// The Most Memory Safe Buffer Overflow in Rust! | |
// | |
// Consider all the code below under Public Domain | |
// | |
// How to build: | |
// $ rustc main.rs | |
// | |
// Wrong password: | |
// $ printf "hello\n" | ./main | |
// | |
// Right password: | |
// $ printf "password\n" | ./main | |
// | |
// Universal password: | |
// $ printf "aaaaaaaaaaaaaa\0aaaaaaaaaaaaaa\0" | ./main | |
// | |
// Support Rust Recovery Foundation: https://rustrecoveryfoundation.neocities.org/ | |
use std::io::{BufRead, Write}; | |
const BUF_CAP: usize = 15; | |
type Ptr = usize; | |
fn alloc_buffer(mem: &mut Vec::<char>, size: usize) -> Ptr { | |
let result = mem.len(); | |
for _ in 0..size { | |
mem.push(' ') | |
} | |
result | |
} | |
fn alloc_str(mem: &mut Vec<char>, s: &str) -> Ptr { | |
let result = mem.len(); | |
for c in s.chars() { | |
mem.push(c) | |
} | |
mem.push('\0'); | |
result | |
} | |
fn read_line_into_buffer(input: &mut impl BufRead, mem: &mut Vec<char>, buf: Ptr) { | |
let mut s = String::new(); | |
let n = input.read_line(&mut s).unwrap(); | |
for (i, c) in s.chars().enumerate() { | |
mem[buf + i] = c; | |
} | |
if mem[buf + n - 1] == '\n' { | |
mem[buf + n - 1] = '\0' | |
} else { | |
mem[buf + n] = '\0'; | |
} | |
} | |
fn streq(mem: &Vec<char>, mut s1: Ptr, mut s2: Ptr) -> bool { | |
while mem[s1] != '\0' && mem[s2] != '\0' { | |
if mem[s1] != mem[s2] { | |
return false; | |
} | |
s1 += 1; | |
s2 += 1; | |
} | |
mem[s1] == '\0' && mem[s2] == '\0' | |
} | |
fn main() { | |
let mut mem = Vec::<char>::new(); | |
let buffer = alloc_buffer(&mut mem, BUF_CAP); | |
let password = alloc_str(&mut mem, "password"); | |
alloc_buffer(&mut mem, BUF_CAP); | |
print!("Password: "); | |
std::io::stdout().flush().unwrap(); | |
read_line_into_buffer(&mut std::io::stdin().lock(), &mut mem, buffer); | |
if streq(&mem, buffer, password) { | |
println!("Access Granted!") | |
} else { | |
println!("Access Denied!") | |
} | |
} |
found it from the mention in your brainfuck video, excellent shitpost
It's not really buffer overflow though, is it? That's just an ordinary buffer reuse, you can do that in any language >.>
It's not really buffer overflow though, is it? That's just an ordinary buffer reuse, you can do that in any language >.>
It's an overflow of a logically allocated sub-buffer. It leads to the same kinds of bugs and vulnerabilities.
Of course, it's kinda like doing things the hard wrong way on purpose, but it goes to show that Rust merely makes it harder to have buffer overflows, but it can't stop you if you're brave enough.
It's not really a buffer overflow. It just overwrite a 'logical' aliased variable as any other logical error. It doesn't enable attacks like return address overwriting.
In summary, it can only corrupt the data in the same Vector (in practice you'll never split a Vector's storage into two halves delibrately for "Raw Ptr" uses as in this example, you definitely will put the data receiving buffer into a standalone Vector), any tries to write to the range beyond this Vector will cause out-of-bound panic of Rust, and no need to mention the heap layout is mostly randomized, its impossible to affect adjacent data structures on heap, so this vulnerability is extremely hard to be exploited.
This example merely shows how hard deliberately implementing a buffer overflow in Rust is.
60 SLOC is hardly difficult for "deliberately" doing anything.
It's not really a buffer overflow. It just overwrite a 'logical' aliased variable as any other logical error. It doesn't enable attacks like return address overwriting.
Actually it does, but you gotta really try, see https://boulderbugle.com/overwritting-return-address-in-rust-you-8s92wy28
@j-hc because they are and no amount of mockery will change this fact.
@rexim istg is that j-hc guy an idiot or something? how can u not know indices and pointers are literally the same thing? like has he never declared an index to be restrict
?? has he never dereferenced an index?? hes probably never even heard of index arithmetic 😤😤
istg the state of rust fanboys denying basic reality is laughable 😂😂😂💯💯
@psprojectC I personally wouldn't say they are "literally the same thing". Rather they are similar in some of the problems they introduce, but if you prefer to believe what you've just said, sure. That's your choice.
@rexim theyre really not. everyone here knows an index doesnt exist without a pointer. which makes it even stranger why when ure sarcastically told "indices are just lyke pointers" u actually deny it
@psprojectC They are just lyke pointers, but they are not literally the same thing. Did the gist trigger you to the point where you feel like you need to argue over the definitions of such basic words? (:
@rexim what thing from this gist would i be triggered over? im not sure i understand, could u please explain?
I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a length
field in your LinkedList
class whose get_length
is supposed to be O(1)
.
Here is my summary of what might go wrong in this example:
let n = // some number not known in compile time;
// We define `buf_a` to be the first 3 bytes of `v`. Other bytes are considered something else.
let v: Vec<u8> = vec![0, 0, 0, 20, 20, 20];
// No! This is not actually an element of `buf_a`, we just accessed something outside of `buf_a`!
let element_of_buf_a = v[3];
// No! We might get a index out of bound error!
let element_of_v = v[n];
// But the compiler is fine with this code and does not even warn us!
@rexim Do you think this minifies this example? I am new to Rust, so I might be wrong.
My roommate coming from C thought list[index]
might actually cause SegFault
. So for anyone not familiar with Rust, list[index]
is simply list.get(index).unwrap()
, which basically is:
if index < list.length() {
return list.get_without_check(index); // the real get_without_check is actually marked unsafe.
} else {
WTFYouAreDoingYourIndexIsCriminalYouAssHole();
}
I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a
length
field in yourLinkedList
class whoseget_length
is supposed to beO(1)
. Here is my summary of what might go wrong in this example:let n = // some number not known in compile time; // We define `buf_a` to be the first 3 bytes of `v`. Other bytes are considered something else. let v: Vec<u8> = vec![0, 0, 0, 20, 20, 20]; // No! This is not actually an element of `buf_a`, we just accessed something outside of `buf_a`! let element_of_buf_a = v[3]; // No! We might get a index out of bound error! let element_of_v = v[n]; // But the compiler is fine with this code and does not even warn us!@rexim Do you think this minifies this example? I am new to Rust, so I might be wrong.
Indices in Rust specifically don't have a concept of ownership attached to them with all the corresponding consequences which you should always keep in mind.
My roommate coming from C thought
list[index]
might actually causeSegFault
. So for anyone not familiar with Rust,list[index]
is simplylist.get(index).unwrap()
, which basically is:if index < list.length() { return list.get_without_check(index); // the real get_without_check is actually marked unsafe. } else { WTFYouAreDoingYourIndexIsCriminalYouAssHole(); }
Here is the thing about C... Depending on the situation list[index]
may NOT segfault even when index >= list.length()
, which is way worse ;)
I see there are two problems, but for one of them, instead of 'buffer overflow', I think it's more like not having a
length
field in yourLinkedList
class whoseget_length
is supposed to beO(1)
. Here is my summary of what might go wrong in this example:let n = // some number not known in compile time; // We define `buf_a` to be the first 3 bytes of `v`. Other bytes are considered something else. let v: Vec<u8> = vec![0, 0, 0, 20, 20, 20]; // No! This is not actually an element of `buf_a`, we just accessed something outside of `buf_a`! let element_of_buf_a = v[3]; // No! We might get a index out of bound error! let element_of_v = v[n]; // But the compiler is fine with this code and does not even warn us!@rexim Do you think this minifies this example? I am new to Rust, so I might be wrong.
Indices in Rust specifically don't have a concept of ownership attached to them with all the corresponding consequences which you should always keep in mind.
Hmm... I am not sure what you mean, are you saying that you can not move an element out of Vec
if it is not Copy
and semantically vec[index]
actually mean copy the element out? Or that the traits backing the []
syntax(i.e. Index
IndexMut
) only gives &T
and &mut T
but not T
? Or something about index
type itself (usize
, Range
, ...)?
I will be very grateful if you can elaborate since I am still learning Rust.
@TinusgragLin yes, Index and IndexMut do in fact provide &T and &mut T.
People on the Internet say that this does not prove that Rust is memory unsafe. I have no idea why they think that this is to prove unsafety of Rust. I explicitly said that this is The Most Memory Safe Buffer Overflow in Rust. Read the description carefully.