feat: starting the TCP client proof of concept
This commit is contained in:
parent
0ef459bcfe
commit
25948a17f4
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -1296,10 +1296,12 @@ dependencies = [
|
|||||||
name = "tcp-test"
|
name = "tcp-test"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"nl-sys",
|
"nl-sys",
|
||||||
"pcap-sys",
|
"pcap-sys",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1412,6 +1414,20 @@ dependencies = [
|
|||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@ -34,6 +34,13 @@ script = [
|
|||||||
"docker-compose run --entrypoint=setcap build cap_net_raw=eip /workspaces/sparse/target/debug/tcp-test"
|
"docker-compose run --entrypoint=setcap build cap_net_raw=eip /workspaces/sparse/target/debug/tcp-test"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[tasks.run-tcp-test]
|
||||||
|
workspace = false
|
||||||
|
dependencies = ["tcp-test"]
|
||||||
|
script = [
|
||||||
|
"docker-compose run tcptest_client /workspaces/sparse/target/debug/tcp-test $(/sbin/ip a show docker0 | awk -F'[ \t/]*' '/inet / { print $3 }')"
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.fmt]
|
[tasks.fmt]
|
||||||
command = "cargo"
|
command = "cargo"
|
||||||
args = ["fmt"]
|
args = ["fmt"]
|
||||||
|
|||||||
@ -44,3 +44,9 @@ services:
|
|||||||
expose:
|
expose:
|
||||||
- "54248/udp"
|
- "54248/udp"
|
||||||
command: /workspaces/sparse/target/debug/ex-revshell-server
|
command: /workspaces/sparse/target/debug/ex-revshell-server
|
||||||
|
|
||||||
|
tcptest_client:
|
||||||
|
image: alpine
|
||||||
|
volumes:
|
||||||
|
- ./target:/workspaces/sparse/target
|
||||||
|
command: /workspaces/sparse/target/debug/tcp-target
|
||||||
|
|||||||
@ -146,7 +146,13 @@ impl<'a> IPv4Pkt<'a> {
|
|||||||
|
|
||||||
pub fn get_layer4_packet(&self) -> error::Result<Layer4Pkt<'a>> {
|
pub fn get_layer4_packet(&self) -> error::Result<Layer4Pkt<'a>> {
|
||||||
match self.protocol() {
|
match self.protocol() {
|
||||||
17 => Ok(Layer4Pkt::UDP(UDPPkt {
|
// ICMP
|
||||||
|
0x1 => Err(error::Error::UnknownPacketType(0x1)),
|
||||||
|
// TCP
|
||||||
|
0x6 => Ok(Layer4Pkt::TCP(TCPPkt {
|
||||||
|
data: &self.data[(self.header_len() as usize)..],
|
||||||
|
})),
|
||||||
|
0x11 => Ok(Layer4Pkt::UDP(UDPPkt {
|
||||||
data: &self.data[(self.header_len() as usize)..],
|
data: &self.data[(self.header_len() as usize)..],
|
||||||
})),
|
})),
|
||||||
p => Err(error::Error::UnknownPacketType(p.into())),
|
p => Err(error::Error::UnknownPacketType(p.into())),
|
||||||
@ -156,12 +162,14 @@ impl<'a> IPv4Pkt<'a> {
|
|||||||
|
|
||||||
pub enum Layer4Pkt<'a> {
|
pub enum Layer4Pkt<'a> {
|
||||||
UDP(UDPPkt<'a>),
|
UDP(UDPPkt<'a>),
|
||||||
|
TCP(TCPPkt<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Layer4Pkt<'a> {
|
impl<'a> Layer4Pkt<'a> {
|
||||||
pub fn len(&self) -> u16 {
|
pub fn len(&self) -> u16 {
|
||||||
match self {
|
match self {
|
||||||
Layer4Pkt::UDP(pkt) => pkt.len(),
|
Layer4Pkt::UDP(pkt) => pkt.len(),
|
||||||
|
Layer4Pkt::TCP(_) => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +210,88 @@ impl<'a> UDPPkt<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TCPPkt<'a> {
|
||||||
|
data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TCPPkt<'a> {
|
||||||
|
pub fn len(&self) -> u8 {
|
||||||
|
(self.data[12] >> 4) * 4
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn srcport(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[0..2].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dstport(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seqnumber(&self) -> u32 {
|
||||||
|
u32::from_be_bytes(self.data[4..8].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acknumber(&self) -> u32 {
|
||||||
|
u32::from_be_bytes(self.data[8..12].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flags(&self) -> u8 {
|
||||||
|
self.data[13]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cwr(&self) -> bool {
|
||||||
|
self.flags() & 0x80 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ece(&self) -> bool {
|
||||||
|
self.flags() & 0x40 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urg(&self) -> bool {
|
||||||
|
self.flags() & 0x20 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ack(&self) -> bool {
|
||||||
|
self.flags() & 0x10 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn psh(&self) -> bool {
|
||||||
|
self.flags() & 0x08 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rst(&self) -> bool {
|
||||||
|
self.flags() & 0x04 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syn(&self) -> bool {
|
||||||
|
self.flags() & 0x02 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fin(&self) -> bool {
|
||||||
|
self.flags() & 0x01 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[14..16].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checksum(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[16..18].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urgent_ptr(&self) -> u16 {
|
||||||
|
u16::from_be_bytes(self.data[18..20].try_into().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(&self) -> &[u8] {
|
||||||
|
&self.data[20..(self.len() as usize)]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &[u8] {
|
||||||
|
&self.data[(self.len() as usize)..]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EthernetPacket {
|
pub struct EthernetPacket {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
@ -252,7 +342,8 @@ impl IPv4Packet {
|
|||||||
|
|
||||||
let ttl: u8 = 64;
|
let ttl: u8 = 64;
|
||||||
let protocol: u8 = match packet {
|
let protocol: u8 = match packet {
|
||||||
Layer4Packet::UDP(_) => 17,
|
Layer4Packet::UDP(_) => 0x11,
|
||||||
|
Layer4Packet::TCP(_) => 0x6,
|
||||||
};
|
};
|
||||||
|
|
||||||
let source_upper = u16::from_be_bytes(source.octets()[0..2].try_into().unwrap());
|
let source_upper = u16::from_be_bytes(source.octets()[0..2].try_into().unwrap());
|
||||||
@ -290,6 +381,7 @@ impl IPv4Packet {
|
|||||||
&dest.octets(),
|
&dest.octets(),
|
||||||
match packet {
|
match packet {
|
||||||
Layer4Packet::UDP(pkt) => &*pkt.data,
|
Layer4Packet::UDP(pkt) => &*pkt.data,
|
||||||
|
Layer4Packet::TCP(pkt) => &*pkt.data,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
@ -306,12 +398,14 @@ impl IPv4Packet {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Layer4Packet {
|
pub enum Layer4Packet {
|
||||||
UDP(UDPPacket),
|
UDP(UDPPacket),
|
||||||
|
TCP(TCPPacket),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layer4Packet {
|
impl Layer4Packet {
|
||||||
pub fn pkt(&'_ self) -> Layer4Pkt<'_> {
|
pub fn pkt(&'_ self) -> Layer4Pkt<'_> {
|
||||||
match self {
|
match self {
|
||||||
Layer4Packet::UDP(pkt) => Layer4Pkt::UDP(pkt.pkt()),
|
Layer4Packet::UDP(pkt) => Layer4Pkt::UDP(pkt.pkt()),
|
||||||
|
Layer4Packet::TCP(pkt) => Layer4Pkt::TCP(pkt.pkt()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,3 +439,98 @@ impl UDPPacket {
|
|||||||
UDPPkt { data: &self.data }
|
UDPPkt { data: &self.data }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TCPPacket {
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCPPacket {
|
||||||
|
#[inline]
|
||||||
|
pub fn pkt(&'_ self) -> TCPPkt<'_> {
|
||||||
|
TCPPkt { data: &self.data }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TCPPacketBuilder {
|
||||||
|
srcport: u16,
|
||||||
|
dstport: u16,
|
||||||
|
seqnumber: u32,
|
||||||
|
acknumber: u32,
|
||||||
|
flags: u8,
|
||||||
|
window: u16,
|
||||||
|
urgent_ptr: u16,
|
||||||
|
options: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! declare_flag {
|
||||||
|
($name:ident, $offset:expr) => {
|
||||||
|
pub fn $name(mut self, enable: bool) -> Self {
|
||||||
|
if enable {
|
||||||
|
self.flags |= $offset
|
||||||
|
} else {
|
||||||
|
self.flags &= (0xFF ^ $offset)
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TCPPacketBuilder {
|
||||||
|
pub fn srcport(mut self, port: u16) -> Self {
|
||||||
|
self.srcport = port;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dstport(mut self, port: u16) -> Self {
|
||||||
|
self.dstport = port;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn seqnumber(mut self, num: u32) -> Self {
|
||||||
|
self.seqnumber = num;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acknumber(mut self, num: u32) -> Self {
|
||||||
|
self.acknumber = num;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_flag!(cwr, 0x80);
|
||||||
|
declare_flag!(ece, 0x40);
|
||||||
|
declare_flag!(urg, 0x20);
|
||||||
|
declare_flag!(ack, 0x10);
|
||||||
|
declare_flag!(psh, 0x08);
|
||||||
|
declare_flag!(rst, 0x04);
|
||||||
|
declare_flag!(syn, 0x02);
|
||||||
|
declare_flag!(fin, 0x01);
|
||||||
|
|
||||||
|
pub fn window(mut self, window: u16) -> Self {
|
||||||
|
self.window = window;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urgent_ptr(mut self, ptr: u16) -> Self {
|
||||||
|
self.urgent_ptr = ptr;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn options(mut self, opts: Vec<u8>) -> Self {
|
||||||
|
self.options = opts;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build<I1, I2>(self, srcip: I1, dstip: I2, data: Vec<u8>) -> TCPPacket
|
||||||
|
where
|
||||||
|
I1: Into<Ipv4Addr>,
|
||||||
|
I2: Into<Ipv4Addr>,
|
||||||
|
{
|
||||||
|
let source = srcip.into();
|
||||||
|
let dest = dstip.into();
|
||||||
|
|
||||||
|
let protocol = &[0x00u8, 0x06u8];
|
||||||
|
|
||||||
|
let tcp_length = data.len() + self.options.len() + 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
edition = "2021"
|
||||||
@ -10,3 +10,5 @@ pcap-sys = { path = "../../pcap-sys" }
|
|||||||
nl-sys = { path = "../../nl-sys" }
|
nl-sys = { path = "../../nl-sys" }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
tokio = { version = "1.32.0", features = ["full"] }
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
|
anyhow = "1.0.75"
|
||||||
|
tokio-stream = { version = "0.1.14", features = ["full"] }
|
||||||
|
|||||||
@ -1,4 +1,44 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
|
||||||
|
use nl_sys::{netlink, route};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> anyhow::Result<()> {
|
||||||
println!("Hello, world!");
|
let ip = std::env::args()
|
||||||
|
.skip(1)
|
||||||
|
.next()
|
||||||
|
.ok_or(anyhow!("could not get target IP"))?
|
||||||
|
.parse::<Ipv4Addr>()?;
|
||||||
|
|
||||||
|
let (ifname, src_mac, dst_mac, srcip) = {
|
||||||
|
let socket = netlink::Socket::new()?;
|
||||||
|
|
||||||
|
let routes = socket.get_routes()?;
|
||||||
|
let neighs = socket.get_neigh()?;
|
||||||
|
let links = socket.get_links()?;
|
||||||
|
let addrs = socket.get_addrs()?;
|
||||||
|
|
||||||
|
let (ifname, srcip, srcmac, dstmac) =
|
||||||
|
route::get_macs_and_src_for_ip(&addrs, &routes, &neighs, &links, ip)
|
||||||
|
.ok_or(anyhow!("unable to find a route to the IP"))?;
|
||||||
|
|
||||||
|
(ifname, srcip, srcmac, dstmac)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut interface = pcap_sys::new_aggregate_interface(false)?;
|
||||||
|
|
||||||
|
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 tcp port 54249", true, None)?;
|
||||||
|
|
||||||
|
interface.prune(|_, interface| interface.datalink() != pcap_sys::consts::DLT_EN10MB);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,9 @@ server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|||||||
server.bind(("0.0.0.0", 54248))
|
server.bind(("0.0.0.0", 54248))
|
||||||
server.listen(32)
|
server.listen(32)
|
||||||
|
|
||||||
|
while True:
|
||||||
client, addr = server.accept()
|
client, addr = server.accept()
|
||||||
|
print('Got connection')
|
||||||
with client:
|
with client:
|
||||||
print(client.recv(24))
|
print(client.recv(24))
|
||||||
client.sendall(b"pong")
|
client.sendall(b"pong")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user