Created
November 18, 2019 00:09
-
-
Save rust-play/70b7b28b690e0020f52695b9e086f5c2 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This PoC has two different exploit methods; you can compile one or | |
// the other. | |
// Since Rust Playground doesn't let you set arbitrary features or | |
// other --cfg options, here is a silly macro. Change `method1` to | |
// `method2` to try method 2. | |
macro_rules! lol_cfg { | |
(method1 => { $($t:tt)* }) => ($($t)*); | |
//^^^^^^^ Change this! | |
($x:ident => { $($t:tt)* }) => (); | |
} | |
// The only non-standard-library imports are a convenience function | |
// and macro from `futures`. | |
extern crate futures; | |
use futures::task::noop_waker; | |
use futures::pending; | |
// Standard library imports | |
use std::cell::Cell; | |
use std::future::Future; | |
use std::mem; | |
use std::pin::Pin; | |
#[allow(unused_imports)] | |
use std::ops::{Deref, DerefMut}; | |
use std::task::Context; | |
// Trait implemented by 'before' and 'after' objects, so that clone() | |
// or deref_mut() on a mutable or immutable (respectively) reference | |
// to trait object can switch from one to the other. | |
trait Dubious<'a> { | |
// function implemented for 'before' object: | |
fn get_evil_reference(&self) -> &'a mut (dyn Dubious<'a> + 'a); | |
// function implemented for 'after' object: | |
fn as_future(self: Pin<&mut Self>) -> Pin<&mut dyn Future<Output=()>>; | |
} | |
lol_cfg!(method1 => { | |
// Evil DerefMut implementation (method 1). | |
impl<'a> DerefMut for &'a (dyn Dubious<'a> + 'a) { | |
fn deref_mut(&mut self) -> &mut (dyn Dubious<'a> + 'a) { | |
self.get_evil_reference() | |
} | |
} | |
}); | |
lol_cfg!(method2 => { | |
// Evil Clone implementation (method 2). | |
impl<'a> Clone for &'a mut (dyn Dubious<'a> + 'a) { | |
fn clone(&self) -> Self { | |
self.get_evil_reference() | |
} | |
} | |
}); | |
// The 'before' object that will be passed into clone()/deref_mut(). | |
// It contains a mutable reference which will be moved out and | |
// returned. | |
// - For method 1, we implement the DerefMut trait (taking `&mut | |
// self`) on an immutable reference, so we get `&mut &Wrapper`. | |
// - For method 2, we implement the Clone trait (taking `&self`) on a | |
// mutable reference, so we get `&&mut Wrapper`. | |
// Either way, we need interior mutability to move from the field. | |
struct Wrapper<'a>(Cell<Option<&'a mut (dyn Dubious<'a> + 'a)>>); | |
impl<'a> Wrapper<'a> { | |
fn new(p: &'a mut (dyn Dubious<'a> + 'a)) -> Self { | |
Wrapper(Cell::new(Some(p))) | |
} | |
} | |
impl<'a> Dubious<'a> for Wrapper<'a> { | |
fn get_evil_reference(&self) -> &'a mut (dyn Dubious<'a> + 'a) { | |
self.0.replace(None).unwrap() | |
} | |
fn as_future(self: Pin<&mut Self>) -> Pin<&mut dyn Future<Output=()>> { | |
panic!() | |
} | |
} | |
// The 'after' object that will come out of clone(): a future from an | |
// `async` block. Actually, we can't name that type, so we just | |
// blanket implement for all Futures. | |
impl<'a, MyFuture: Future<Output=()>> Dubious<'a> for MyFuture { | |
fn get_evil_reference(&self) -> &'a mut (dyn Dubious<'a> + 'a) { | |
panic!() | |
} | |
fn as_future(self: Pin<&mut Self>) -> Pin<&mut dyn Future<Output=()>> { | |
self | |
} | |
} | |
// Helper that makes the demonstration of 'use-after-free' more | |
// consistent. The exploit lets us move an already-started future; | |
// using this enum we can fill the original location with known bytes | |
// so that it deterministically crashes. | |
enum UAF<MyFuture: Future<Output=()>> { | |
Future(MyFuture), | |
Dummy([u8; 256]), | |
} | |
pub fn go_inner<MyFuture: Future<Output=()>>(p_myfuture: MyFuture) { | |
let waker = noop_waker(); | |
let mut cx = Context::from_waker(&waker); | |
let mut uaf: UAF<MyFuture> = UAF::Future(p_myfuture); | |
{ | |
// Get a reference to the future within the enum. | |
let p_myfuture: &mut MyFuture = match &mut uaf { | |
UAF::Future(p_myfuture) => p_myfuture, | |
_ => panic!() | |
}; | |
lol_cfg!(method1 => {{ | |
// Method 1: | |
let wrapper = Wrapper::new(p_myfuture); | |
let p_wrapper: Pin<&Wrapper<'_>> = Pin::new(&wrapper); | |
// Convert to pinned immutable reference to trait object. | |
let mut p_wrapper_as_dubious: Pin<&dyn Dubious<'_>> = | |
p_wrapper.as_ref(); | |
// Now call as_mut() on the Pin, which will call our | |
// custom deref_mut() impl on the immutable reference. | |
let p_myfuture_as_dubious: Pin<&mut dyn Dubious<'_>> = | |
p_wrapper_as_dubious.as_mut(); | |
// `p_myfuture_as_dubious` points to the MyFuture. Downcast... | |
let p_myfuture_as_future: Pin<&mut dyn Future<Output=()>> | |
= p_myfuture_as_dubious.as_future(); | |
// ...and poll. | |
let _ = p_myfuture_as_future.poll(&mut cx); | |
}}); | |
lol_cfg!(method2 => {{ | |
// Method 2: | |
let mut wrapper = Wrapper::new(p_myfuture); | |
let mut p_wrapper: Pin<&mut Wrapper<'_>> = Pin::new(&mut wrapper); | |
// Convert to pinned mutable reference to trait object. | |
let p_wrapper_as_dubious: Pin<&mut dyn Dubious<'_>> | |
= p_wrapper.as_mut(); | |
// Now call clone() on the Pin, which will call our custom | |
// clone() impl on the mutable reference. | |
let p_myfuture_as_dubious: Pin<&mut dyn Dubious<'_>> = | |
p_wrapper_as_dubious.clone(); | |
// (The following is identical to method 1; needed to copy+paste due to scoping:) | |
// `p_myfuture_as_dubious` points to the MyFuture. Downcast... | |
let p_myfuture_as_future: Pin<&mut dyn Future<Output=()>> | |
= p_myfuture_as_dubious.as_future(); | |
// ...and poll. | |
let _ = p_myfuture_as_future.poll(&mut cx); | |
}}); | |
} | |
{ | |
// Move the MyFuture; fill the original location with 0x41 bytes. | |
let p_myfuture = match mem::replace(&mut uaf, UAF::Dummy([0x41; 256])) { | |
UAF::Future(p_myfuture) => p_myfuture, | |
_ => panic!() | |
}; | |
// Trigger the crash. This is just a normal pin -> poll | |
// sequence, which is only dangerous because the async fn was | |
// already started before being moved. | |
let mut pin_box_mf: Pin<Box<MyFuture>> = Box::pin(p_myfuture); | |
let _ = pin_box_mf.as_mut().poll(&mut cx); | |
} | |
} | |
fn main() { | |
go_inner(async { | |
let integer = 42; | |
let string = "hello"; | |
let p_integer = &integer; | |
let p_string = &string; | |
loop { | |
println!("hi"); | |
println!(" integer={:x}", *p_integer); | |
println!(" string={}", *p_string); | |
// Yield to caller: | |
pending!(); | |
} | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment