#![deny(unsafe_code)]
use std::io;
use clap::{
crate_authors, crate_description, crate_name, crate_version, value_parser, Arg, ArgAction,
Command,
};
use clap_complete::{generate, Generator, Shell};
fn build_cli() -> Command {
Command::new(crate_name!())
.arg_required_else_help(true)
.author(crate_authors!())
.about(crate_description!())
.version(crate_version!())
.long_about(HELP)
.subcommand(Command::new("uaf").about("Run the use-after-free bug"))
.subcommand(
Command::new("bo").about("Run the buffer overflow exploit. Optionally take a shower"),
)
.subcommand(
Command::new("transition")
.about("Safely transmute A to B")
.subcommand(Command::new("boy-to-girl").about("Safely transmute a Boy to a Girl"))
.subcommand(Command::new("girl-to-boy").about("Safely transmute a Girl to a Boy")),
)
.subcommand(Command::new("segfault").about("Segfault yourself"))
.subcommand(
Command::new("completions")
.about("Return shell completions")
.arg(
Arg::new("shell")
.action(ArgAction::Set)
.value_parser(value_parser!(Shell))
.required(true),
),
)
.help_template(TEMPLATE)
}
fn main() {
let mut command = build_cli();
let matches = build_cli().clone().get_matches();
let subcommand = matches.subcommand().unwrap();
match subcommand.0 {
"uaf" => cve_rs::use_after_free(),
"segfault" => cve_rs::segfault(),
"bo" => cve_rs::buffer_overflow().unwrap(),
"transition" => {
let command = subcommand.1.subcommand().expect("Invalid transition").0;
transmute_demo(command).unwrap()
}
"completions" => print_completions(
subcommand.1.get_one::<Shell>("shell").copied().unwrap(),
&mut command,
),
_ => unreachable!(),
}
}
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}
const TEMPLATE: &str = "\
{before-help}{name} {version}
{author-with-newline}{about-with-newline}
{usage-heading} {usage}
{all-args}{after-help}
";
const HELP: &str = r"
cve-rs: Blazingly fast memory vulnerabilities, written in 100% safe rust.
This is a demo of the bugs implemented by cve-rs.
cve-rs exploits a soundness hole in lifetimes that lets us cast any lifetime to 'static, allowing us to create dangling references.
See: https://github.com/rust-lang/rust/issues/25860
This program is open-source! View the source for all these exploits here: https://github.com/Speykious/cve-rs
";
#[repr(C)]
#[derive(Debug)]
struct Boy {
age: u32,
name: String,
github_username: String,
}
#[repr(C)]
#[derive(Debug)]
struct Girl {
age: u32,
name: String,
github_username: String,
}
fn transmute_demo(command: &str) -> std::io::Result<()> {
use std::io::Write as _;
let stdin = std::io::stdin();
let mut stdout = std::io::stdout();
let mut input_buf = String::new();
match command {
"boy-to-girl" => stdout.write_all(b"Creating a Boy struct\n")?,
"girl-to-boy" => stdout.write_all(b"Creating a Girl struct\n")?,
_ => unreachable!(),
}
let age = {
stdout.write_all(b"Enter age: ")?;
stdout.flush()?;
stdin.read_line(&mut input_buf)?;
match input_buf.trim().parse() {
Ok(age) => age,
Err(_) => panic!("Invalid age"),
}
};
let name = {
stdout.write_all(b"Enter name: ")?;
stdout.flush()?;
input_buf.clear();
stdin.read_line(&mut input_buf)?;
input_buf.trim().to_owned()
};
let github_username = {
stdout.write_all(b"Enter github username: ")?;
stdout.flush()?;
input_buf.clear();
stdin.read_line(&mut input_buf)?;
input_buf.trim().to_owned()
};
match command {
"boy-to-girl" => {
let boy: Boy = Boy {
age,
name,
github_username,
};
println!("Before transmute: {boy:?}");
let girl: Girl = cve_rs::transmute(boy);
println!("After transmute: {girl:?}");
}
"girl-to-boy" => {
let girl: Girl = Girl {
age,
name,
github_username,
};
println!("Before transmute: {girl:?}");
let boy: Boy = cve_rs::transmute(girl);
println!("After transmute: {boy:?}");
}
_ => unreachable!(),
}
Ok(())
}