diff --git a/utils/noaslr/Cargo.toml b/utils/noaslr/Cargo.toml new file mode 100644 index 0000000000..ad6c5a6587 --- /dev/null +++ b/utils/noaslr/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +resolver = "2" +members = [ + "noaslr", + "demo", + "libnoaslr" +] diff --git a/utils/noaslr/Makefile.toml b/utils/noaslr/Makefile.toml new file mode 100644 index 0000000000..25f7859eaa --- /dev/null +++ b/utils/noaslr/Makefile.toml @@ -0,0 +1,89 @@ +[config] +default_to_workspace = false + +[env] +PROFILE="dev" +BUILD_DIR="${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/debug" + +[env.release] +PROFILE="release" +BUILD_DIR="${CARGO_MAKE_CRATE_TARGET_DIRECTORY}/release" + +[tasks.clean] +command = "cargo" +args = ["clean"] + +[tasks.format] +install_crate = "rustfmt" +command = "cargo" +args = ["fmt", "--", "--emit=files"] + +[tasks.demo] +dependencies = ["format", "clippy"] +command = "cargo" +args = [ + "build", + "-p", "demo", + "--profile", "${PROFILE}", +] + +[tasks.run_demo] +dependencies = ["demo"] +command = "cargo" +args = [ + "run", + "-p", "demo", +] + +[tasks.build] +dependencies = ["format", "clippy"] +command = "cargo" +args = [ + "build", + "-p", "noaslr", + "--profile", "${PROFILE}", +] + +[tasks.buildlib] +dependencies = ["format", "clippy"] +command = "cargo" +args = [ + "build", + "-p", "libnoaslr", + "--profile", "${PROFILE}", +] + +[tasks.run] +command = "cargo" +dependencies = [ "demo" ] +env = { "ZZZ_TEST_ZZZ" = "ZZZ TEST ZZZ"} +args = [ + "run", + "-p", "noaslr", + "--profile", "${PROFILE}", + "--", + "${BUILD_DIR}/demo", + "--", + "-f", + "/proc/self/maps", + "--", + "test" +] + +[tasks.runlib] +command = "cargo" +dependencies = [ "demo", "buildlib" ] +env = { "LD_PRELOAD" = "${BUILD_DIR}/libnoaslr.so", "ZZZ_TEST_ZZZ" = "ZZZ TEST ZZZ"} +args = [ + "run", + "-p", "demo", + "--profile", "${PROFILE}", + "--", + "-f", + "/proc/self/maps", + "--", + "test" +] + +[tasks.all] +dependencies = ["demo", "build", "buildlib"] diff --git a/utils/noaslr/README.md b/utils/noaslr/README.md new file mode 100644 index 0000000000..739631b888 --- /dev/null +++ b/utils/noaslr/README.md @@ -0,0 +1,118 @@ +# NOASLR +`noaslr` is a launcher for running applications with ASLR disabled without having +to disable the feature system-wide. + +`libnoaslr` is a dynamic shared object which can be injected into an application +at startup using `LD_PRELOAD` which will cause it to disable ASLR. + +Also included is `demo` an application which reads and prints the contents of the +file passed as its single argument. By passing `/proc/self/maps` as its argument +it can be observed that the application loads at the same default address each +time it is run. + +# Test +## App +``` +$ cargo make run +``` +## Library + +``` +$ cargo make runlib +``` + +# Example +## App +``` +$ ./target/debug/noaslr ./target/debug/demo -- /proc/self/maps +``` +## Library + +``` +$ LD_PRELOAD=target/debug/libnoaslr.so ./target/debug/demo /proc/self/maps +``` + +## Output +... + +555555554000-55555556d000 r--p 00000000 fd:03 78381550 /home/jon/git/LibAFL/utils/noaslr/target/debug/demo +55555556d000-5555556a1000 r-xp 00019000 fd:03 78381550 /home/jon/git/LibAFL/utils/noaslr/target/debug/demo +5555556a1000-5555556ee000 r--p 0014d000 fd:03 78381550 /home/jon/git/LibAFL/utils/noaslr/target/debug/demo +5555556ee000-5555556fb000 r--p 00199000 fd:03 78381550 /home/jon/git/LibAFL/utils/noaslr/target/debug/demo +5555556fb000-5555556fc000 rw-p 001a6000 fd:03 78381550 /home/jon/git/LibAFL/utils/noaslr/target/debug/demo +5555556fc000-55555571d000 rw-p 00000000 00:00 0 [heap] +7ffff7d74000-7ffff7d76000 rw-p 00000000 00:00 0 +7ffff7d76000-7ffff7d98000 r--p 00000000 fd:03 9972607 /usr/lib/x86_64-linux-gnu/libc-2.31.so +7ffff7d98000-7ffff7f10000 r-xp 00022000 fd:03 9972607 /usr/lib/x86_64-linux-gnu/libc-2.31.so +7ffff7f10000-7ffff7f5e000 r--p 0019a000 fd:03 9972607 /usr/lib/x86_64-linux-gnu/libc-2.31.so +7ffff7f5e000-7ffff7f62000 r--p 001e7000 fd:03 9972607 /usr/lib/x86_64-linux-gnu/libc-2.31.so +7ffff7f62000-7ffff7f64000 rw-p 001eb000 fd:03 9972607 /usr/lib/x86_64-linux-gnu/libc-2.31.so +7ffff7f64000-7ffff7f68000 rw-p 00000000 00:00 0 +7ffff7f68000-7ffff7f69000 r--p 00000000 fd:03 9972611 /usr/lib/x86_64-linux-gnu/libdl-2.31.so +7ffff7f69000-7ffff7f6b000 r-xp 00001000 fd:03 9972611 /usr/lib/x86_64-linux-gnu/libdl-2.31.so +7ffff7f6b000-7ffff7f6c000 r--p 00003000 fd:03 9972611 /usr/lib/x86_64-linux-gnu/libdl-2.31.so +7ffff7f6c000-7ffff7f6d000 r--p 00003000 fd:03 9972611 /usr/lib/x86_64-linux-gnu/libdl-2.31.so +7ffff7f6d000-7ffff7f6e000 rw-p 00004000 fd:03 9972611 /usr/lib/x86_64-linux-gnu/libdl-2.31.so +7ffff7f6e000-7ffff7f74000 r--p 00000000 fd:03 9972655 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so +7ffff7f74000-7ffff7f85000 r-xp 00006000 fd:03 9972655 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so +7ffff7f85000-7ffff7f8b000 r--p 00017000 fd:03 9972655 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so +7ffff7f8b000-7ffff7f8c000 r--p 0001c000 fd:03 9972655 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so +7ffff7f8c000-7ffff7f8d000 rw-p 0001d000 fd:03 9972655 /usr/lib/x86_64-linux-gnu/libpthread-2.31.so +7ffff7f8d000-7ffff7f91000 rw-p 00000000 00:00 0 +7ffff7f91000-7ffff7f94000 r--p 00000000 fd:03 9961835 /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 +7ffff7f94000-7ffff7fa6000 r-xp 00003000 fd:03 9961835 /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 +7ffff7fa6000-7ffff7faa000 r--p 00015000 fd:03 9961835 /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 +7ffff7faa000-7ffff7fab000 r--p 00018000 fd:03 9961835 /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 +7ffff7fab000-7ffff7fac000 rw-p 00019000 fd:03 9961835 /usr/lib/x86_64-linux-gnu/libgcc_s.so.1 +7ffff7fac000-7ffff7fae000 rw-p 00000000 00:00 0 +7ffff7fc8000-7ffff7fc9000 ---p 00000000 00:00 0 +7ffff7fc9000-7ffff7fcb000 rw-p 00000000 00:00 0 +7ffff7fcb000-7ffff7fce000 r--p 00000000 00:00 0 [vvar] +7ffff7fce000-7ffff7fcf000 r-xp 00000000 00:00 0 [vdso] +7ffff7fcf000-7ffff7fd0000 r--p 00000000 fd:03 9972533 /usr/lib/x86_64-linux-gnu/ld-2.31.so +7ffff7fd0000-7ffff7ff3000 r-xp 00001000 fd:03 9972533 /usr/lib/x86_64-linux-gnu/ld-2.31.so +7ffff7ff3000-7ffff7ffb000 r--p 00024000 fd:03 9972533 /usr/lib/x86_64-linux-gnu/ld-2.31.so +7ffff7ffc000-7ffff7ffd000 r--p 0002c000 fd:03 9972533 /usr/lib/x86_64-linux-gnu/ld-2.31.so +7ffff7ffd000-7ffff7ffe000 rw-p 0002d000 fd:03 9972533 /usr/lib/x86_64-linux-gnu/ld-2.31.so +7ffff7ffe000-7ffff7fff000 rw-p 00000000 00:00 0 +7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack] +ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] + +``` + +# About +## APP +`noaslr` does the following: +* Uses the `personality` API to set the flag `ADDR_NO_RANDOMIZE`. +* Uses the `execve` API to launch the child application. + +## Lib +`libnoasl` does the following: +* Uses rusts `ctor` crate to define a function as `__attribute__((constructor))` +* Uses the `personality` API to determine if the flag `ADDR_NO_RANDOMIZE` is set +* If the flag is set, the constructor returns, otherwise... +* Uses the `personality` API again to set the flag +* Reads the program arguments from `/proc/self/cmdline` +* Reads the program environment variables from `/proc/self/environ` +* Uses the `execvpe` API to re-launch the application + +# Usage +``` +Tool launching applications with ASLR disabled + +Usage: noaslr [-- ...] + +Arguments: + + Name of the application to launch + + [ARGS]... + Arguments passed to the target + +Options: + -h, --help + Print help (see a summary with '-h') + + -V, --version + Print version +``` diff --git a/utils/noaslr/demo/Cargo.toml b/utils/noaslr/demo/Cargo.toml new file mode 100644 index 0000000000..cde2b834d3 --- /dev/null +++ b/utils/noaslr/demo/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "demo" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +vergen = { version = "8.1.1", features = ["build", "cargo", "git", "gitcl", "rustc", "si"] } + +[dependencies] +anyhow = { version = "1.0.71", default-features = false } +clap = { version = "4.2.0", default-features = false, features = ["derive", "string", "std", "help"] } +readonly = { version = "0.2.8", default-features = false } diff --git a/utils/noaslr/demo/build.rs b/utils/noaslr/demo/build.rs new file mode 100644 index 0000000000..b8c56e8d82 --- /dev/null +++ b/utils/noaslr/demo/build.rs @@ -0,0 +1,13 @@ +use {std::error::Error, vergen::EmitBuilder}; + +fn main() -> Result<(), Box> { + EmitBuilder::builder() + .all_build() + .all_cargo() + .all_git() + .all_rustc() + .all_sysinfo() + .emit()?; + + Ok(()) +} diff --git a/utils/noaslr/demo/src/args.rs b/utils/noaslr/demo/src/args.rs new file mode 100644 index 0000000000..b77397af73 --- /dev/null +++ b/utils/noaslr/demo/src/args.rs @@ -0,0 +1,45 @@ +use clap::{builder::Str, Parser}; + +#[derive(Default)] +pub struct Version; + +impl From for Str { + fn from(_: Version) -> Str { + let version = [ + ("Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP")), + ("Describe:", env!("VERGEN_GIT_DESCRIBE")), + ("Commit SHA:", env!("VERGEN_GIT_SHA")), + ("Commit Date:", env!("VERGEN_RUSTC_COMMIT_DATE")), + ("Commit Branch:", env!("VERGEN_GIT_BRANCH")), + ("Rustc Version:", env!("VERGEN_RUSTC_SEMVER")), + ("Rustc Channel:", env!("VERGEN_RUSTC_CHANNEL")), + ("Rustc Host Triple:", env!("VERGEN_RUSTC_HOST_TRIPLE")), + ("Rustc Commit SHA:", env!("VERGEN_RUSTC_COMMIT_HASH")), + ("Cargo Target Triple", env!("VERGEN_CARGO_TARGET_TRIPLE")), + ] + .iter() + .map(|(k, v)| format!("{k:25}: {v}\n")) + .collect::(); + + format!("\n{version:}").into() + } +} + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +#[command( + version = Version::default(), + about = "demo", + long_about = "Application to demonstrate noaslr utility" +)] +#[readonly::make] +pub struct Args { + #[arg(short, long, help = "Unused flag")] + pub flag: bool, + + #[arg(help = "Name of the file to read")] + pub file: String, + + #[arg(last = true, value_parser, value_delimiter = ' ', num_args = 1.., help = "Arguments passed to the target")] + args: Vec, +} diff --git a/utils/noaslr/demo/src/main.rs b/utils/noaslr/demo/src/main.rs new file mode 100644 index 0000000000..9155c9d189 --- /dev/null +++ b/utils/noaslr/demo/src/main.rs @@ -0,0 +1,34 @@ +mod args; + +use { + crate::args::Args, + anyhow::{anyhow, Result}, + clap::Parser, + std::{ + env, + fs::File, + io::{BufRead, BufReader}, + }, +}; + +fn main() -> Result<()> { + let args = Args::parse(); + for (i, a) in env::args().enumerate() { + println!("ARG - {i:3}: {a}"); + } + for (i, (k, v)) in env::vars().enumerate() { + println!("ENV {i:3}: {k} = {v}"); + } + + let file = File::open(&args.file).map_err(|e| anyhow!("Failed to open maps: {e:}"))?; + let lines = BufReader::new(file).lines(); + + for line in lines { + println!( + "{}", + line.map_err(|e| anyhow!("Failed to read line: {e:}"))? + ); + } + + Ok(()) +} diff --git a/utils/noaslr/libnoaslr/Cargo.toml b/utils/noaslr/libnoaslr/Cargo.toml new file mode 100644 index 0000000000..b2bf2f022a --- /dev/null +++ b/utils/noaslr/libnoaslr/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "libnoaslr" +version = "0.1.0" +edition = "2021" + +[lib] +name = "noaslr" +path = "src/lib.rs" +crate-type = ["dylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = { version = "1.0.71", default-features = false } +ctor = { version = "0.2.2", default-features = false } +nix = { version = "0.26.2", default-features = false, features = ["process", "personality"] } diff --git a/utils/noaslr/libnoaslr/src/lib.rs b/utils/noaslr/libnoaslr/src/lib.rs new file mode 100644 index 0000000000..bc76063413 --- /dev/null +++ b/utils/noaslr/libnoaslr/src/lib.rs @@ -0,0 +1,42 @@ +use { + anyhow::{anyhow, Result}, + ctor::ctor, + nix::{ + sys::{personality, personality::Persona}, + unistd::execvpe, + }, + std::{ffi::CString, fs::File, io::Read}, +}; + +fn read_null_lines(path: &str) -> Result> { + let mut file = File::open(path).map_err(|e| anyhow!("Failed to open maps: {e:}"))?; + let mut data = String::new(); + file.read_to_string(&mut data) + .map_err(|e| anyhow!("Failed to read command line: {e:}"))?; + data.split('\0') + .map(|s| s.to_string()) + .filter(|s| !s.is_empty()) + .map(|x| CString::new(x).map_err(|e| anyhow!("Failed to read argument: {e:}"))) + .collect::>>() +} + +fn libnoaslr() -> Result<()> { + let mut persona = personality::get().map_err(|e| anyhow!("Failed to get personality: {e:}"))?; + if (persona & Persona::ADDR_NO_RANDOMIZE) == Persona::ADDR_NO_RANDOMIZE { + return Ok(()); + } + + persona |= Persona::ADDR_NO_RANDOMIZE; + personality::set(persona).map_err(|e| anyhow!("Failed to set personality: {e:}"))?; + + let args = read_null_lines("/proc/self/cmdline")?; + let env = read_null_lines("/proc/self/environ")?; + + execvpe(&args[0], &args, &env).map_err(|e| anyhow!("Failed to exceve: {e:}"))?; + Ok(()) +} + +#[ctor] +fn init() { + libnoaslr().unwrap(); +} diff --git a/utils/noaslr/noaslr/Cargo.toml b/utils/noaslr/noaslr/Cargo.toml new file mode 100644 index 0000000000..3c75eacebe --- /dev/null +++ b/utils/noaslr/noaslr/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "noaslr" +version = "0.1.0" +edition = "2021" + +[build-dependencies] +vergen = { version = "8.1.1", features = ["build", "cargo", "git", "gitcl", "rustc", "si"] } + +[dependencies] +anyhow = { version = "1.0.71", default-features = false } +clap = { version = "4.2.0", default-features = false, features = ["derive", "string", "std", "help", "derive", "error-context", "usage"] } +log = { version = "0.4.19", default-features = false } +nix = { version = "0.26.2", default-features = false, features = ["process", "personality"] } +readonly = { version = "0.2.8", default-features = false } +simplelog = { version = "0.12.1", default-features = false } diff --git a/utils/noaslr/noaslr/build.rs b/utils/noaslr/noaslr/build.rs new file mode 100644 index 0000000000..b8c56e8d82 --- /dev/null +++ b/utils/noaslr/noaslr/build.rs @@ -0,0 +1,13 @@ +use {std::error::Error, vergen::EmitBuilder}; + +fn main() -> Result<(), Box> { + EmitBuilder::builder() + .all_build() + .all_cargo() + .all_git() + .all_rustc() + .all_sysinfo() + .emit()?; + + Ok(()) +} diff --git a/utils/noaslr/noaslr/src/args.rs b/utils/noaslr/noaslr/src/args.rs new file mode 100644 index 0000000000..541927ba7d --- /dev/null +++ b/utils/noaslr/noaslr/src/args.rs @@ -0,0 +1,54 @@ +use { + clap::{builder::Str, Parser}, + std::iter, +}; + +#[derive(Default)] +pub struct Version; + +impl From for Str { + fn from(_: Version) -> Str { + let version = [ + ("Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP")), + ("Describe:", env!("VERGEN_GIT_DESCRIBE")), + ("Commit SHA:", env!("VERGEN_GIT_SHA")), + ("Commit Date:", env!("VERGEN_RUSTC_COMMIT_DATE")), + ("Commit Branch:", env!("VERGEN_GIT_BRANCH")), + ("Rustc Version:", env!("VERGEN_RUSTC_SEMVER")), + ("Rustc Channel:", env!("VERGEN_RUSTC_CHANNEL")), + ("Rustc Host Triple:", env!("VERGEN_RUSTC_HOST_TRIPLE")), + ("Rustc Commit SHA:", env!("VERGEN_RUSTC_COMMIT_HASH")), + ("Cargo Target Triple", env!("VERGEN_CARGO_TARGET_TRIPLE")), + ] + .iter() + .map(|(k, v)| format!("{k:25}: {v}\n")) + .collect::(); + + format!("\n{version:}").into() + } +} + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +#[command( + version = Version::default(), + about = "nosalr", + long_about = "Tool launching applications with ASLR disabled" +)] +#[readonly::make] +pub struct Args { + #[arg(help = "Name of the application to launch")] + program: String, + + #[arg(last = true, value_parser, value_delimiter = ' ', num_args = 1.., help = "Arguments passed to the target")] + args: Vec, +} + +impl Args { + pub fn argv(&self) -> Vec { + iter::once(&self.program) + .chain(self.args.iter()) + .cloned() + .collect::>() + } +} diff --git a/utils/noaslr/noaslr/src/main.rs b/utils/noaslr/noaslr/src/main.rs new file mode 100644 index 0000000000..8ce0c08966 --- /dev/null +++ b/utils/noaslr/noaslr/src/main.rs @@ -0,0 +1,28 @@ +mod args; + +use { + crate::args::Args, + anyhow::{anyhow, Result}, + clap::Parser, + nix::{ + sys::{personality, personality::Persona}, + unistd::execvp, + }, + std::ffi::CString, +}; + +fn main() -> Result<()> { + let args = Args::parse(); + let mut persona = personality::get().map_err(|e| anyhow!("Failed to get personality: {e:}"))?; + persona |= Persona::ADDR_NO_RANDOMIZE; + personality::set(persona).map_err(|e| anyhow!("Failed to set personality: {e:}"))?; + + let cargs = args + .argv() + .iter() + .map(|x| CString::new(x.clone()).map_err(|e| anyhow!("Failed to read argument: {e:}"))) + .collect::>>()?; + + execvp(&cargs[0], &cargs).map_err(|e| anyhow!("Failed to exceve: {e:}"))?; + Ok(()) +}