From a229345409a423d54bd719ac58eb9b6c80864e49 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 31 Aug 2023 10:55:46 +0200 Subject: [PATCH 01/17] refactor: Removed dependencies, major refactor --- Cargo.toml | 22 ++++++++++++++++------ src/main.rs | 31 ++++++++++++++----------------- src/markov/mutations.rs | 2 +- src/mqtt/mod.rs | 5 +++-- src/network.rs | 1 + src/packet_pool.rs | 2 -- src/process_monitor/mod.rs | 16 +++++++++++++++- src/runtime/mod.rs | 13 ++++++++----- 8 files changed, 58 insertions(+), 34 deletions(-) create mode 100644 src/network.rs diff --git a/Cargo.toml b/Cargo.toml index fedd9f4..7497022 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,24 +7,34 @@ readme = "README.md" edition = "2021" [dependencies] +# Convenient error handling color-eyre = "0.6.2" +# Logging tracing = "0.1.37" tracing-subscriber = "0.3.17" -dotenvy = "0.15.7" +# Futures tokio = { version = "1.32.0", features = ["full"] } -markov = "1.1.0" -rand = "0.8.5" +futures = "0.3.28" +# Hex en/decoding hex = "0.4.3" +# MQTT Packet generation mqtt-protocol = "0.11.2" +# Random number generation with xoshiro for faster PRNG +rand = "0.8.5" rand_xoshiro = "0.6.0" +# Command line interface clap = { version = "4.3.24", features = ["derive"] } +# For serialization serde = { version = "1.0.186", features = ["derive"] } toml = "0.7.6" -futures = "0.3.28" -tokio-uring = "0.4.0" -console-subscriber = "0.1.10" +# For serialization of raw bytes serde_with = {version="3.1.0", features = ["hex"]} +# Tokio Console Support +console-subscriber = "0.1.10" +# For Websocket support +tokio-tungstenite = "0.20.0" + [profile.release] debug = true codegen-units = 1 diff --git a/src/main.rs b/src/main.rs index 7fdc5c8..d84060d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,44 +18,42 @@ //! - S2: Inject/Delete/Mutate the current chain or go to SEND //! - SEND: Send the current chain and either go to Sf or S2 //! Once they get to S2 they behave the same way. -use std::cmp::min; -use std::collections::BTreeMap; -use std::fmt::Display; -use std::path::Path; -use std::sync::Arc; -// TODO: Pick a mqtt packet generation/decoding library that is customizable for the purpose of this project and also supports v3,v4 and v5. -// FIXME: Fix ranges... -// TODO: crtl_c handling -// TODO: Try fuzzing a basic mongoose server? -// TODO: Fuzz mosquitto compiled with sanitizers use crate::markov::MAX_PACKETS; use crate::mqtt::test_connection; use crate::process_monitor::start_supervised_process; use crate::runtime::{iterations_tracker, run_thread}; -use clap::{Parser, Subcommand}; +use clap::{Parser, Subcommand, ValueEnum}; use futures::future::join_all; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; use serde_with::formats::CommaSeparator; use serde_with::serde_as; use serde_with::StringWithSeparator; +use std::cmp::min; +use std::collections::BTreeMap; +use std::fmt::Display; +use std::path::Path; use std::str::FromStr; +use std::sync::Arc; use tokio::fs::File; use tokio::io::AsyncReadExt; use tokio::net::TcpStream; use tokio::sync::mpsc::channel as mpsc_channel; use tokio::sync::RwLock; -use tokio::time::sleep; use tokio::{fs, task}; use tracing::{debug, info, trace}; mod markov; pub mod mqtt; +mod network; mod packet_pool; mod process_monitor; mod runtime; - -// TODO: All threads should also dump their last packets for fast replaying +// TODO: Clean up main +// TODO: Pick a mqtt packet generation/decoding library that is customizable for the purpose of this project and also supports v3,v4 and v5. +// TODO: Try fuzzing a basic mongoose server? +// TODO: Fuzz mosquitto compiled with sanitizers +// TODO: Support TLS, WS and QUIC #[serde_as] #[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] pub struct Packets { @@ -122,7 +120,7 @@ struct Cli { #[arg(short, long)] broker_command: String, // TODO: Make the timeout configurable - #[arg(short, long, default_value = "200")] + #[arg(long, default_value = "200")] timeout: u64, } @@ -149,7 +147,6 @@ struct SeedAndIterations { async fn main() -> color_eyre::Result<()> { console_subscriber::init(); color_eyre::install()?; - dotenvy::dotenv().ok(); let cli = Cli::parse(); let packet_queue = Arc::new(RwLock::new( PacketQueue::read_from_file("./packet_pool.toml").await?, @@ -157,7 +154,7 @@ async fn main() -> color_eyre::Result<()> { match &cli.subcommand { SubCommands::Fuzz { threads } => { // The channel used for iteration counting - let (it_sender, mut it_receiver) = mpsc_channel::(*threads as usize); + let (it_sender, it_receiver) = mpsc_channel::(*threads as usize); // This receiver is necessary to dump the packets once the broker is stopped let (sender, _) = tokio::sync::broadcast::channel(1); let mut subscribers = vec![]; diff --git a/src/markov/mutations.rs b/src/markov/mutations.rs index 58e9b49..7090a46 100644 --- a/src/markov/mutations.rs +++ b/src/markov/mutations.rs @@ -36,7 +36,7 @@ impl Distribution for Standard { fn inject_bof(packet: &mut Vec, rng: &mut Xoshiro256PlusPlus) { let idx = rng.gen_range(0..packet.len()); // To fight big packets - let byte_length = 10000 / packet.len(); + let byte_length = 350 / packet.len(); let mut bytes = vec![0; byte_length]; rng.fill(&mut bytes[..]); packet.splice(idx..idx, bytes); diff --git a/src/mqtt/mod.rs b/src/mqtt/mod.rs index 4265b7a..429451f 100644 --- a/src/mqtt/mod.rs +++ b/src/mqtt/mod.rs @@ -2,9 +2,10 @@ use crate::markov::ByteStream; use crate::{PacketQueue, Packets}; use std::sync::Arc; use std::time::Duration; -use tokio::io::AsyncWriteExt; +use tokio::net::{TcpStream, ToSocketAddrs}; use tokio::sync::RwLock; use tokio::time::timeout; +use tokio_tungstenite::tungstenite::client::IntoClientRequest; use tracing::{debug, info, trace}; // TODO: Maybe we can begin the packet queue with some more interesting packets that triggered bugs in the past from CVEs pub(crate) fn generate_auth_packet() -> Vec { @@ -120,7 +121,7 @@ async fn known_packet( packet_queue: &Arc>, ) -> bool { // TODO: decode the packet and extract user id, payload, topic etc. because those don't matter to see if it is a known packet - let mut queue_lock = packet_queue.read().await; + let queue_lock = packet_queue.read().await; let response_packet = response_packet.to_vec(); if !queue_lock.inner.contains_key(&response_packet) { info!("New behavior discovered, adding it to the queue",); diff --git a/src/network.rs b/src/network.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/network.rs @@ -0,0 +1 @@ + diff --git a/src/packet_pool.rs b/src/packet_pool.rs index 37137ca..6012e99 100644 --- a/src/packet_pool.rs +++ b/src/packet_pool.rs @@ -43,8 +43,6 @@ const OTHER_MOSQUITTO_CVE: &[&[u8]; MAX_PACKETS] = &[ ]; mod tests { use super::*; - use crate::{PacketQueue, Packets}; - use std::fs::write; #[test] fn nanomq_bug() { diff --git a/src/process_monitor/mod.rs b/src/process_monitor/mod.rs index a74447e..a2a293e 100644 --- a/src/process_monitor/mod.rs +++ b/src/process_monitor/mod.rs @@ -1,7 +1,9 @@ use std::time::Duration; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; +use tokio::signal; use tokio::sync::broadcast::Sender; +use tokio::sync::oneshot::channel; use tokio::time::{sleep, timeout}; use tracing::{debug, info}; @@ -23,9 +25,17 @@ pub async fn start_supervised_process( assert!(child.id().is_some()); debug!("Started broker process"); // No broker should take longer than 2 seconds to start. But we could make this configurable. - sleep(tokio::time::Duration::from_secs(2)).await; + sleep(tokio::time::Duration::from_secs(5)).await; + // Buffers for stdout and stderr let mut stdout_reader = BufReader::new(child.stdout.take().unwrap()).lines(); let mut stderr_reader = BufReader::new(child.stderr.take().unwrap()).lines(); + // For handling crtlc + let (tx, mut rx) = channel(); + tokio::spawn(async move { + signal::ctrl_c().await.unwrap(); + info!("Crtl C received, stopping..."); + tx.send(()).expect("Could not send to crtlc_receiver"); + }); tokio::spawn(async move { let mut last_stdout: String = String::new(); let mut last_stderr: String = String::new(); @@ -47,6 +57,10 @@ pub async fn start_supervised_process( info!("Stdout: {:?}", last_stdout); info!("Stderr: {:?}", last_stderr); break; + } else if rx.try_recv().is_ok() { + child.kill().await.unwrap(); + sender.send(()).unwrap(); + break; } } }); diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index ce78e8d..c570ef1 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,4 +1,4 @@ -use crate::markov::StateMachine; +use crate::markov::{ByteStream, StateMachine}; use crate::{PacketQueue, SeedAndIterations}; use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256PlusPlus; @@ -11,7 +11,7 @@ use tokio::sync::mpsc::Sender; use tokio::sync::RwLock; use tokio::time::sleep; use tokio::{fs, task}; -use tracing::{debug, error, info}; +use tracing::*; // TODO: Change address to allow other kinds of Streams /// Runs a task that connects to the broker and fuzzes it @@ -24,7 +24,6 @@ pub(crate) async fn run_thread( it_sender_clone: Sender, ) { let task_handle = task::spawn(async move { - let start_time = std::time::Instant::now(); let mut last_packets = Vec::new(); let mut counter: u64 = 0; let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); @@ -75,7 +74,7 @@ pub(crate) async fn run_thread( } // Dump the packet we crashed on let _ = fs::create_dir("crashes").await; - let res = fs::write( + let _ = fs::write( format!("crashes/crash_{}.txt", seed), format!("{:?}", last_packets), ) @@ -92,7 +91,11 @@ pub async fn iterations_tracker(threads: usize, mut it_receiver: MpscReceiver iteration_buffer[i] = v, + None => break, + } } let sum: u64 = iteration_buffer.iter().sum(); let elapsed = start.elapsed().as_millis(); From c714cc8fbda7cc8db47c58925fc32ab7ae3afaff Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:03:17 +0200 Subject: [PATCH 02/17] refactor: Moved packets into their own file --- Cargo.toml | 13 ++++- src/main.rs | 78 +++------------------------ src/markov/mod.rs | 6 +-- src/markov/mutations.rs | 2 +- src/mqtt/mod.rs | 9 +--- src/network.rs | 108 +++++++++++++++++++++++++++++++++++++ src/packet_pool.rs | 3 +- src/packets.rs | 66 +++++++++++++++++++++++ src/process_monitor/mod.rs | 2 +- src/runtime/mod.rs | 17 +++--- 10 files changed, 212 insertions(+), 92 deletions(-) create mode 100644 src/packets.rs diff --git a/Cargo.toml b/Cargo.toml index 7497022..b4ebf48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty-fume" -version = "0.1.0" +version = "0.8.0" authors = ["Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com>"] license = "GPLv3" readme = "README.md" @@ -34,7 +34,18 @@ serde_with = {version="3.1.0", features = ["hex"]} console-subscriber = "0.1.10" # For Websocket support tokio-tungstenite = "0.20.0" +# For TLS support +tokio-rustls = { version="0.24.1", features = ["dangerous_configuration"], optional = true } +rustls = { version="0.21.6", features = ["dangerous_configuration"], optional = true } [profile.release] debug = true codegen-units = 1 + +[features] +default = ["tcp"] +tcp = [] +# TODO: Add quic, ws support +quic = [] +websocket = [] +tls = ["dep:tokio-rustls", "dep:rustls"] diff --git a/src/main.rs b/src/main.rs index d84060d..60853c1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,24 +20,16 @@ //! Once they get to S2 they behave the same way. use crate::markov::MAX_PACKETS; use crate::mqtt::test_connection; +use crate::network::connect_to_broker; +use crate::packets::PacketQueue; use crate::process_monitor::start_supervised_process; use crate::runtime::{iterations_tracker, run_thread}; -use clap::{Parser, Subcommand, ValueEnum}; +use clap::{Parser, Subcommand}; use futures::future::join_all; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; -use serde_with::formats::CommaSeparator; -use serde_with::serde_as; -use serde_with::StringWithSeparator; -use std::cmp::min; -use std::collections::BTreeMap; -use std::fmt::Display; -use std::path::Path; use std::str::FromStr; use std::sync::Arc; -use tokio::fs::File; -use tokio::io::AsyncReadExt; -use tokio::net::TcpStream; use tokio::sync::mpsc::channel as mpsc_channel; use tokio::sync::RwLock; use tokio::{fs, task}; @@ -47,6 +39,7 @@ mod markov; pub mod mqtt; mod network; mod packet_pool; +mod packets; mod process_monitor; mod runtime; // TODO: Clean up main @@ -54,61 +47,6 @@ mod runtime; // TODO: Try fuzzing a basic mongoose server? // TODO: Fuzz mosquitto compiled with sanitizers // TODO: Support TLS, WS and QUIC -#[serde_as] -#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] -pub struct Packets { - #[serde_as(as = "[StringWithSeparator::; MAX_PACKETS]")] - inner: [Vec; MAX_PACKETS], -} -impl Display for Packets { - // Hex dump the packets - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut s = String::new(); - for i in 0..MAX_PACKETS { - if !self.inner[i].is_empty() { - s.push_str(hex::encode(&self.inner[i]).as_str()); - s.push('\n'); - } - } - write!(f, "{}", s) - } -} -impl Packets { - pub fn append(&mut self, packet: &mut Vec) { - // Search the first free slot and insert it there - let size = self.size(); - if size < MAX_PACKETS { - self.inner[size] = packet.clone(); - } - } - pub fn is_full(&self) -> bool { - self.inner.iter().all(|x| !x.is_empty()) - } - pub fn size(&self) -> usize { - min(1, self.inner.iter().filter(|x| !x.is_empty()).count()) - } - pub fn new() -> Self { - Self { - inner: Default::default(), - } - } -} - -#[serde_as] -#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] -struct PacketQueue { - #[serde_as(as = "BTreeMap, _>")] - inner: BTreeMap, Packets>, -} - -impl PacketQueue { - async fn read_from_file(path: impl AsRef) -> color_eyre::Result { - let mut content = String::new(); - File::open(path).await?.read_to_string(&mut content).await?; - let queue = toml::from_str(&content)?; - Ok(queue) - } -} #[derive(Parser, Debug)] #[command(author, version, about)] @@ -163,8 +101,8 @@ async fn main() -> color_eyre::Result<()> { } start_supervised_process(sender, cli.broker_command).await?; let address = cli.target.clone(); - let mut tcpstream = TcpStream::connect(&address).await?; - test_connection(&mut tcpstream).await?; + let mut stream = connect_to_broker(&cli.target).await?; + test_connection(&mut stream).await?; info!("Connection established, starting fuzzing!"); let mut rng = thread_rng(); let _ = fs::create_dir("./threads").await; @@ -218,8 +156,8 @@ async fn main() -> color_eyre::Result<()> { subscribers.push(sender.subscribe()); } start_supervised_process(sender, cli.broker_command).await?; - let mut tcpstream = TcpStream::connect(&cli.target).await?; - test_connection(&mut tcpstream).await?; + let mut stream = connect_to_broker(&cli.target).await?; + test_connection(&mut stream).await?; debug!("Connection established"); debug!("Starting replay with {} seeds", filtered_files.len()); let mut threads = vec![]; diff --git a/src/markov/mod.rs b/src/markov/mod.rs index 41db56c..15b517f 100644 --- a/src/markov/mod.rs +++ b/src/markov/mod.rs @@ -23,7 +23,7 @@ use crate::mqtt::{ generate_publish_packet, generate_subscribe_packet, generate_unsubscribe_packet, send_packets, SendError, }; -use crate::{PacketQueue, Packets}; +use crate::packets::{PacketQueue, Packets}; use rand::distributions::Standard; use rand::prelude::Distribution; use rand::Rng; @@ -75,9 +75,9 @@ impl Distribution for Standard { } } -pub trait ByteStream: AsyncReadExt + AsyncWriteExt + Unpin {} +pub trait ByteStream: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} -impl ByteStream for T where T: AsyncReadExt + AsyncWriteExt + Unpin {} +impl ByteStream for T where T: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} pub struct StateMachine where B: ByteStream, diff --git a/src/markov/mutations.rs b/src/markov/mutations.rs index 7090a46..5dd6df6 100644 --- a/src/markov/mutations.rs +++ b/src/markov/mutations.rs @@ -1,4 +1,4 @@ -use crate::Packets; +use crate::packets::Packets; use rand::distributions::Standard; use rand::prelude::Distribution; use rand::Rng; diff --git a/src/mqtt/mod.rs b/src/mqtt/mod.rs index 429451f..ff61aae 100644 --- a/src/mqtt/mod.rs +++ b/src/mqtt/mod.rs @@ -1,16 +1,11 @@ use crate::markov::ByteStream; -use crate::{PacketQueue, Packets}; +use crate::packets::{PacketQueue, Packets}; use std::sync::Arc; use std::time::Duration; -use tokio::net::{TcpStream, ToSocketAddrs}; use tokio::sync::RwLock; use tokio::time::timeout; -use tokio_tungstenite::tungstenite::client::IntoClientRequest; use tracing::{debug, info, trace}; -// TODO: Maybe we can begin the packet queue with some more interesting packets that triggered bugs in the past from CVEs -pub(crate) fn generate_auth_packet() -> Vec { - unimplemented!("Auth packet not implemented yet. Switch to MQTT V5") -} + pub(crate) fn generate_connect_packet() -> [u8; 62] { [ 16, 60, 0, 4, 77, 81, 84, 84, 4, 4, 0, 0, 0, 17, 72, 101, 108, 108, 111, 32, 77, 81, 84, diff --git a/src/network.rs b/src/network.rs index 8b13789..d1e6e0b 100644 --- a/src/network.rs +++ b/src/network.rs @@ -1 +1,109 @@ +use color_eyre::Result; +// Avoid double protocols +// We have a lot of features which are mutually exclusive, so we need compile time errors if someone tries to use them together. +// These are the features: tcp, websocket, tls, quic +#[cfg(all( + feature = "tcp", + any(feature = "websocket", feature = "tls", feature = "quic") +))] +compile_error!( + "You can only use one protocol at a time. Please disable tcp with --no-default-features" +); +#[cfg(all(feature = "websocket", any(feature = "tls", feature = "quic")))] +compile_error!( + "You can only use one protocol at a time. Please disable websocket with --no-default-features" +); +#[cfg(all(feature = "tls", feature = "quic"))] +compile_error!( + "You can only use one protocol at a time. Please disable tls with --no-default-features" +); +#[cfg(feature = "tcp")] +pub use tcp::*; +#[cfg(feature = "tcp")] +mod tcp { + use super::*; + use tokio::net::{TcpStream, ToSocketAddrs}; + + pub async fn connect_to_broker(address: impl ToSocketAddrs) -> Result { + let tcpstream = TcpStream::connect(address).await?; + Ok(tcpstream) + } +} + +#[cfg(feature = "websocket")] +pub use websocket::*; + +#[cfg(feature = "websocket")] +mod websocket { + compile_error!("Websocket is not implemented yet"); + /* + use super::*; + use tokio::net::TcpStream; + use tokio_tungstenite::tungstenite::client::IntoClientRequest; + use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; + + pub async fn connect_to_broker( + address: impl IntoClientRequest + Unpin, + ) -> Result>> { + let (ws_stream, _) = tokio_tungstenite::connect_async(address).await?; + Ok(ws_stream) + } + */ +} + +#[cfg(feature = "tls")] +pub use tls::*; + +#[cfg(feature = "tls")] +mod tls { + use super::*; + use crate::markov::ByteStream; + use futures::FutureExt; + use rustls::client::{ServerCertVerified, ServerCertVerifier}; + use rustls::{Certificate, ClientConfig, ClientConnection, Error, ServerName}; + use std::net::IpAddr; + use std::str::FromStr; + use std::sync::Arc; + use std::time::SystemTime; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use tokio::net::{TcpStream, ToSocketAddrs}; + use tokio_rustls::client::TlsStream; + use tokio_rustls::TlsConnector; + + struct NoCertificateVerifier; + + impl ServerCertVerifier for NoCertificateVerifier { + fn verify_server_cert( + &self, + end_entity: &Certificate, + intermediates: &[Certificate], + server_name: &ServerName, + scts: &mut dyn Iterator, + ocsp_response: &[u8], + now: SystemTime, + ) -> std::result::Result { + Ok(ServerCertVerified::assertion()) + } + } + /// Connects to the broker using TLS ignoring the server certificate since that would just be + /// wasted iterations + pub async fn connect_to_broker(address: &str) -> Result> { + let mut socket = TcpStream::connect(address).await?; + let mut config = ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(NoCertificateVerifier)) + .with_no_client_auth(); + let connector = TlsConnector::from(Arc::new(config)); + let stream = connector + .connect( + // Trim everything including the port beginning ':' + ServerName::IpAddress( + IpAddr::from_str(address.split(':').next().unwrap()).unwrap(), + ), + socket, + ) + .await?; + Ok(stream) + } +} diff --git a/src/packet_pool.rs b/src/packet_pool.rs index 6012e99..8808381 100644 --- a/src/packet_pool.rs +++ b/src/packet_pool.rs @@ -1,7 +1,7 @@ //! Here are packets from previously discovered CVEs use crate::MAX_PACKETS; - +/* /// https://www.cvedetails.com/cve/CVE-2021-34432/ const CVE_2021_34432: &[&[u8]; MAX_PACKETS] = &[ &[ @@ -41,6 +41,7 @@ const OTHER_MOSQUITTO_CVE: &[&[u8]; MAX_PACKETS] = &[ &[], &[], ]; +*/ mod tests { use super::*; diff --git a/src/packets.rs b/src/packets.rs new file mode 100644 index 0000000..6a2486a --- /dev/null +++ b/src/packets.rs @@ -0,0 +1,66 @@ +use crate::markov::MAX_PACKETS; +use serde::{Deserialize, Serialize}; +use serde_with::formats::CommaSeparator; +use serde_with::serde_as; +use serde_with::StringWithSeparator; +use std::cmp::min; +use std::collections::BTreeMap; +use std::fmt::Display; +use std::path::Path; +use tokio::fs::File; +use tokio::io::AsyncReadExt; +#[serde_as] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] +pub struct Packets { + #[serde_as(as = "[StringWithSeparator::; MAX_PACKETS]")] + pub(crate) inner: [Vec; MAX_PACKETS], +} +impl Display for Packets { + // Hex dump the packets + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = String::new(); + for i in 0..MAX_PACKETS { + if !self.inner[i].is_empty() { + s.push_str(hex::encode(&self.inner[i]).as_str()); + s.push('\n'); + } + } + write!(f, "{}", s) + } +} +impl Packets { + pub fn append(&mut self, packet: &mut Vec) { + // Search the first free slot and insert it there + let size = self.size(); + if size < MAX_PACKETS { + self.inner[size] = packet.clone(); + } + } + pub fn is_full(&self) -> bool { + self.inner.iter().all(|x| !x.is_empty()) + } + pub fn size(&self) -> usize { + min(1, self.inner.iter().filter(|x| !x.is_empty()).count()) + } + pub fn new() -> Self { + Self { + inner: Default::default(), + } + } +} + +#[serde_as] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] +pub struct PacketQueue { + #[serde_as(as = "BTreeMap, _>")] + pub(crate) inner: BTreeMap, Packets>, +} + +impl PacketQueue { + pub async fn read_from_file(path: impl AsRef) -> color_eyre::Result { + let mut content = String::new(); + File::open(path).await?.read_to_string(&mut content).await?; + let queue = toml::from_str(&content)?; + Ok(queue) + } +} diff --git a/src/process_monitor/mod.rs b/src/process_monitor/mod.rs index a2a293e..d976210 100644 --- a/src/process_monitor/mod.rs +++ b/src/process_monitor/mod.rs @@ -22,7 +22,7 @@ pub async fn start_supervised_process( .stderr(std::process::Stdio::piped()) .spawn() .expect("failed to execute process"); - assert!(child.id().is_some()); + debug_assert!(child.id().is_some()); debug!("Started broker process"); // No broker should take longer than 2 seconds to start. But we could make this configurable. sleep(tokio::time::Duration::from_secs(5)).await; diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index c570ef1..94c6340 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -1,10 +1,11 @@ -use crate::markov::{ByteStream, StateMachine}; -use crate::{PacketQueue, SeedAndIterations}; +use crate::markov::StateMachine; +use crate::network::connect_to_broker; +use crate::packets::PacketQueue; +use crate::SeedAndIterations; use rand::{Rng, SeedableRng}; use rand_xoshiro::Xoshiro256PlusPlus; use std::sync::Arc; use std::time::Duration; -use tokio::net::{TcpStream, ToSocketAddrs}; use tokio::sync::broadcast::Receiver; use tokio::sync::mpsc::Receiver as MpscReceiver; use tokio::sync::mpsc::Sender; @@ -18,7 +19,7 @@ use tracing::*; pub(crate) async fn run_thread( seed: u64, receiver_clone: Receiver<()>, - address: impl ToSocketAddrs + Clone + Send + Sync + 'static, + address: String, iterations: u64, packet_queue: Arc>, it_sender_clone: Sender, @@ -28,12 +29,12 @@ pub(crate) async fn run_thread( let mut counter: u64 = 0; let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); while counter < iterations { - let new_tcpstream = TcpStream::connect(address.clone()).await; - if new_tcpstream.is_err() { + let new_stream = connect_to_broker(&address.clone()).await; + if new_stream.is_err() { // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request error!( "Error connecting to broker: {:?}. See recommendations", - new_tcpstream + new_stream ); if !receiver_clone.is_empty() { break; @@ -42,7 +43,7 @@ pub(crate) async fn run_thread( sleep(Duration::from_millis(100)).await; continue; } - let new_tcpstream = new_tcpstream.unwrap(); + let new_tcpstream = new_stream.unwrap(); let mut state_machine = StateMachine::new(new_tcpstream); let mode = rng.gen(); state_machine.execute(mode, &mut rng, &packet_queue).await; From 5017ac9535721c0ee97bd850d1be957a3dc03f04 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:15:54 +0200 Subject: [PATCH 03/17] fix: Tests, clippy cleanup --- src/main.rs | 2 +- src/markov/mod.rs | 15 ++++++--------- src/mqtt/mod.rs | 2 +- src/packet_pool.rs | 6 ++++-- src/packets.rs | 4 ++-- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 60853c1..c0cd8d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,7 +43,6 @@ mod packets; mod process_monitor; mod runtime; // TODO: Clean up main -// TODO: Pick a mqtt packet generation/decoding library that is customizable for the purpose of this project and also supports v3,v4 and v5. // TODO: Try fuzzing a basic mongoose server? // TODO: Fuzz mosquitto compiled with sanitizers // TODO: Support TLS, WS and QUIC @@ -196,6 +195,7 @@ async fn main() -> color_eyre::Result<()> { #[cfg(test)] mod tests { use super::*; + use crate::packets::Packets; #[test] fn test_serialize_packet_queue() { let mut packet_queue = PacketQueue::default(); diff --git a/src/markov/mod.rs b/src/markov/mod.rs index 15b517f..5f3b273 100644 --- a/src/markov/mod.rs +++ b/src/markov/mod.rs @@ -164,25 +164,22 @@ where State::ADD(packet_type) => { match packet_type { PacketType::CONNECT => { - self.packets.append(&mut generate_connect_packet().to_vec()); + self.packets.append(&generate_connect_packet()); } PacketType::PUBLISH => { - self.packets.append(&mut generate_publish_packet().to_vec()); + self.packets.append(&generate_publish_packet()); } PacketType::SUBSCRIBE => { - self.packets - .append(&mut generate_subscribe_packet().to_vec()); + self.packets.append(&generate_subscribe_packet()); } PacketType::UNSUBSCRIBE => { - self.packets - .append(&mut generate_unsubscribe_packet().to_vec()); + self.packets.append(&generate_unsubscribe_packet()); } PacketType::PINGREQ => { - self.packets.append(&mut generate_pingreq_packet().to_vec()); + self.packets.append(&generate_pingreq_packet()); } PacketType::DISCONNECT => { - self.packets - .append(&mut generate_disconnect_packet().to_vec()); + self.packets.append(&generate_disconnect_packet()); } _ => unreachable!(), } diff --git a/src/mqtt/mod.rs b/src/mqtt/mod.rs index ff61aae..6859136 100644 --- a/src/mqtt/mod.rs +++ b/src/mqtt/mod.rs @@ -43,7 +43,7 @@ pub(crate) async fn test_connection(stream: &mut impl ByteStream) -> color_eyre: .await?; let mut buf = [0; 1024]; let _ = timeout(Duration::from_secs(1), stream.read(&mut buf)).await; - debug!("Received Packet hex encoded: {:?}", hex::encode(&buf)); + debug!("Received Packet hex encoded: {:?}", hex::encode(buf)); Ok(()) } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/src/packet_pool.rs b/src/packet_pool.rs index 8808381..5b73b2f 100644 --- a/src/packet_pool.rs +++ b/src/packet_pool.rs @@ -1,8 +1,8 @@ //! Here are packets from previously discovered CVEs use crate::MAX_PACKETS; -/* /// https://www.cvedetails.com/cve/CVE-2021-34432/ +#[allow(unused)] const CVE_2021_34432: &[&[u8]; MAX_PACKETS] = &[ &[ 16, 60, 0, 4, 77, 81, 84, 84, 4, 4, 0, 0, 0, 17, 72, 101, 108, 108, 111, 32, 77, 81, 84, @@ -19,6 +19,7 @@ const CVE_2021_34432: &[&[u8]; MAX_PACKETS] = &[ &[], &[], ]; +#[allow(unused)] const OTHER_MOSQUITTO_CVE: &[&[u8]; MAX_PACKETS] = &[ &[ 16, 96, 0, 4, 77, 81, 84, 84, 5, 192, 93, 85, 34, 21, 0, 15, 98, 99, 82, 85, 100, 109, 83, @@ -41,9 +42,10 @@ const OTHER_MOSQUITTO_CVE: &[&[u8]; MAX_PACKETS] = &[ &[], &[], ]; -*/ mod tests { use super::*; + use crate::packets::{PacketQueue, Packets}; + use std::fs::write; #[test] fn nanomq_bug() { diff --git a/src/packets.rs b/src/packets.rs index 6a2486a..adc39e0 100644 --- a/src/packets.rs +++ b/src/packets.rs @@ -29,11 +29,11 @@ impl Display for Packets { } } impl Packets { - pub fn append(&mut self, packet: &mut Vec) { + pub fn append(&mut self, packet: &[u8]) { // Search the first free slot and insert it there let size = self.size(); if size < MAX_PACKETS { - self.inner[size] = packet.clone(); + self.inner[size] = packet.to_vec(); } } pub fn is_full(&self) -> bool { From 5465af0da24769e886b96a6b0dedeb9c203b50cb Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:21:07 +0200 Subject: [PATCH 04/17] fix: ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d93876..9925da6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: run: cargo test -- --test-threads=1 - name: Run cargo doc if: ${{ runner.os == 'Linux' }} - run: cargo doc --no-deps --document-private-items --all-features + run: cargo doc --no-deps --document-private-items - name: Run build --release --all-targets run: cargo build --release --all-targets From dccaff3b3cf859b70549f4c3c05c5894774efc1f Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Mon, 4 Sep 2023 23:28:27 +0200 Subject: [PATCH 05/17] feat: Instrumentation --- Cargo.toml | 8 ++++ chain.yaml | 35 -------------- src/main.rs | 44 +++++++++++++++++- src/markov/mod.rs | 9 +++- src/mqtt/mod.rs | 15 +++++- src/network.rs | 2 + src/process_monitor/mod.rs | 3 -- src/runtime/mod.rs | 95 +++++++++++++++++++------------------- 8 files changed, 120 insertions(+), 91 deletions(-) delete mode 100644 chain.yaml diff --git a/Cargo.toml b/Cargo.toml index b4ebf48..c69a95b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,12 @@ tokio-tungstenite = "0.20.0" tokio-rustls = { version="0.24.1", features = ["dangerous_configuration"], optional = true } rustls = { version="0.21.6", features = ["dangerous_configuration"], optional = true } +# Opentelemetry tracing. Only activated when using the "stats" feature +opentelemetry = { version = "0.20.0", features=["trace", "metrics", "rt-tokio"] ,optional = true } +opentelemetry-otlp = { version = "0.13.0", features=["metrics"], optional = true } +opentelemetry-jaeger = { version = "0.19.0", features = ["rt-tokio", "isahc_collector_client", "collector_client", "reqwest_rustls_collector_client", "reqwest_blocking_collector_client"], optional = true } +tracing-opentelemetry = { version = "0.20.0", optional = true } + [profile.release] debug = true codegen-units = 1 @@ -49,3 +55,5 @@ tcp = [] quic = [] websocket = [] tls = ["dep:tokio-rustls", "dep:rustls"] +# TODO: Stats that add insight into performance, coverage etc. +stats = ["dep:opentelemetry-otlp", "dep:opentelemetry", "dep:opentelemetry-jaeger", "dep:tracing-opentelemetry"] \ No newline at end of file diff --git a/chain.yaml b/chain.yaml deleted file mode 100644 index 946678a..0000000 --- a/chain.yaml +++ /dev/null @@ -1,35 +0,0 @@ ---- -map: - ? - ~ - : I: 2 - ? - like - : "them,": 1 - green: 1 - ? - I - : do: 2 - am.: 2 - am: 1 - ? - am. - : I: 1 - ~: 1 - ? - do - : not: 2 - ? - "them," - : Sam: 1 - ? - not - : like: 2 - ? - and - : ham.: 1 - ? - green - : eggs: 1 - ? - eggs - : and: 1 - ? - ham. - : ~: 1 - ? - Sam - : I: 2 - ? - Sam. - : Sam: 1 - ? - am - : Sam.: 1 -order: 1 diff --git a/src/main.rs b/src/main.rs index c0cd8d9..32cf120 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,14 +26,25 @@ use crate::process_monitor::start_supervised_process; use crate::runtime::{iterations_tracker, run_thread}; use clap::{Parser, Subcommand}; use futures::future::join_all; + +#[cfg(feature = "stats")] +use opentelemetry::runtime::Tokio; +#[cfg(feature = "stats")] +use opentelemetry::trace::Tracer; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "stats")] +use std::env; use std::str::FromStr; use std::sync::Arc; use tokio::sync::mpsc::channel as mpsc_channel; use tokio::sync::RwLock; use tokio::{fs, task}; -use tracing::{debug, info, trace}; +use tracing::{debug, info, instrument, span, trace}; +#[cfg(feature = "stats")] +use tracing_subscriber::layer::SubscriberExt; +#[cfg(feature = "stats")] +use tracing_subscriber::util::SubscriberInitExt; mod markov; pub mod mqtt; @@ -82,8 +93,37 @@ struct SeedAndIterations { #[tokio::main] async fn main() -> color_eyre::Result<()> { - console_subscriber::init(); color_eyre::install()?; + #[cfg(not(feature = "stats"))] + { + let subscriber = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .finish(); + tracing::subscriber::set_global_default(subscriber)?; + } + #[cfg(feature = "stats")] + let tracer = opentelemetry_jaeger::new_collector_pipeline() + .with_endpoint(env::var("OPENTELEMETRY_JAEGER_ENDPOINT").unwrap()) + .with_service_name("rusty-fume") + .with_reqwest() + .install_batch(Tokio)?; + #[cfg(feature = "stats")] + let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer); + #[cfg(feature = "stats")] + tracing_subscriber::registry() + .with(tracing_subscriber::EnvFilter::from_default_env()) + .with(opentelemetry) + .init(); + start_app().await?; + #[cfg(feature = "stats")] + { + opentelemetry::global::shutdown_tracer_provider(); + } + Ok(()) +} + +#[instrument] +async fn start_app() -> color_eyre::Result<()> { let cli = Cli::parse(); let packet_queue = Arc::new(RwLock::new( PacketQueue::read_from_file("./packet_pool.toml").await?, diff --git a/src/markov/mod.rs b/src/markov/mod.rs index 5f3b273..e138deb 100644 --- a/src/markov/mod.rs +++ b/src/markov/mod.rs @@ -39,7 +39,7 @@ const SEL_FROM_QUEUE: f32 = 0.7; const PACKET_APPEND_CHANCE: f32 = 0.2; const SEND_CHANCE: f32 = 0.2; const BOF_CHANCE: f32 = 0.2; -const MUT_AFTER_SEND: f32 = 0.7; +const MUT_AFTER_SEND: f32 = 0.3; pub const MAX_PACKETS: usize = 10; #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] @@ -78,6 +78,8 @@ impl Distribution for Standard { pub trait ByteStream: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} impl ByteStream for T where T: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} + +#[derive(Debug)] pub struct StateMachine where B: ByteStream, @@ -106,6 +108,7 @@ impl Distribution for Standard { } } } + impl StateMachine where B: ByteStream, @@ -118,6 +121,8 @@ where previous_packets: Vec::new(), } } + + #[instrument] pub(crate) async fn execute( &mut self, mode: Mode, @@ -229,7 +234,7 @@ where } else { trace!("Sent packet successfully"); } - if rng.gen_range(0f32..1f32) > MUT_AFTER_SEND || res.is_err() { + if (rng.gen_range(0f32..1f32) > MUT_AFTER_SEND) || res.is_err() { self.state = State::Sf; } else { self.state = State::Mutate(rng.gen()); diff --git a/src/mqtt/mod.rs b/src/mqtt/mod.rs index 6859136..8ba03d8 100644 --- a/src/mqtt/mod.rs +++ b/src/mqtt/mod.rs @@ -1,10 +1,11 @@ use crate::markov::ByteStream; use crate::packets::{PacketQueue, Packets}; +use std::fmt::Display; use std::sync::Arc; use std::time::Duration; use tokio::sync::RwLock; use tokio::time::timeout; -use tracing::{debug, info, trace}; +use tracing::{debug, info, instrument, trace}; pub(crate) fn generate_connect_packet() -> [u8; 62] { [ @@ -55,7 +56,16 @@ pub(crate) enum SendError { // We couldn't send the packet. A previous packet might have crashed the server SendErr, } - +impl Display for SendError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SendError::Timeout => write!(f, "Timeout"), + SendError::ReceiveErr => write!(f, "Rcv error"), + SendError::SendErr => write!(f, "Send error"), + } + } +} +#[instrument(err)] pub(crate) async fn send_packets( stream: &mut impl ByteStream, packets: &Packets, @@ -110,6 +120,7 @@ pub(crate) async fn send_packet( } /// This works by using the response packet as the key in a hashmap. If the packet is already in the hashmap we know that we have seen it before +#[instrument] async fn known_packet( response_packet: &[u8], input_packet: &Packets, diff --git a/src/network.rs b/src/network.rs index d1e6e0b..a329380 100644 --- a/src/network.rs +++ b/src/network.rs @@ -24,7 +24,9 @@ pub use tcp::*; mod tcp { use super::*; use tokio::net::{TcpStream, ToSocketAddrs}; + use tracing::instrument; + #[instrument(err, skip(address))] pub async fn connect_to_broker(address: impl ToSocketAddrs) -> Result { let tcpstream = TcpStream::connect(address).await?; Ok(tcpstream) diff --git a/src/process_monitor/mod.rs b/src/process_monitor/mod.rs index d976210..989ad4f 100644 --- a/src/process_monitor/mod.rs +++ b/src/process_monitor/mod.rs @@ -7,10 +7,7 @@ use tokio::sync::oneshot::channel; use tokio::time::{sleep, timeout}; use tracing::{debug, info}; -// TODO: How do the tasks ask if the server has exited? And better yet, how do they get the message back? -// TODO: Also, how do the tasks know when it has caused new stdout/stderr output? // TODO: Allow the user to specify where to write the stdout/stderr of the monitored process. Maybe gzip compress it? -// TODO: Ask threads what their last packets were and dump it. /// Start the broker process and monitor it. If it crashes, we stop our execution. pub async fn start_supervised_process( sender: Sender<()>, diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 94c6340..6502909 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -25,63 +25,64 @@ pub(crate) async fn run_thread( it_sender_clone: Sender, ) { let task_handle = task::spawn(async move { - let mut last_packets = Vec::new(); - let mut counter: u64 = 0; - let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); - while counter < iterations { - let new_stream = connect_to_broker(&address.clone()).await; - if new_stream.is_err() { - // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request - error!( + info_span!("Thread {}", seed).in_scope(|| async { + let mut last_packets = Vec::new(); + let mut counter: u64 = 0; + let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); + while counter < iterations { + let new_stream = connect_to_broker(&address.clone()).await; + if new_stream.is_err() { + // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request + error!( "Error connecting to broker: {:?}. See recommendations", new_stream ); + if !receiver_clone.is_empty() { + break; + } + // So we'll just have a "back-off" sleep here + sleep(Duration::from_millis(100)).await; + continue; + } + let new_tcpstream = new_stream.unwrap(); + let mut state_machine = StateMachine::new(new_tcpstream); + let mode = rng.gen(); + state_machine.execute(mode, &mut rng, &packet_queue).await; + last_packets = state_machine.previous_packets.clone(); + // We receive a message once the broker is stopped if !receiver_clone.is_empty() { break; } - // So we'll just have a "back-off" sleep here - sleep(Duration::from_millis(100)).await; - continue; - } - let new_tcpstream = new_stream.unwrap(); - let mut state_machine = StateMachine::new(new_tcpstream); - let mode = rng.gen(); - state_machine.execute(mode, &mut rng, &packet_queue).await; - last_packets = state_machine.previous_packets.clone(); - // We receive a message once the broker is stopped - // TODO: Also save last packets upon crash - if !receiver_clone.is_empty() { - break; - } - counter += 1; - if counter % 5000 == 0 { - // Display iterations per second - let _ = it_sender_clone.send(counter).await; + counter += 1; + if counter % 5000 == 0 { + // Display iterations per second + let _ = it_sender_clone.send(counter).await; + } } - } - if iterations == u64::MAX { - // If the fuzzing is stopped we dump the packets - let serialized = toml::to_string(&SeedAndIterations { - seed: seed.to_string(), - iterations: counter.to_string(), - }) - .unwrap(); - let res = fs::write(format!("threads/fuzzing_{}.txt", seed), serialized).await; + if iterations == u64::MAX { + // If the fuzzing is stopped we dump the packets + let serialized = toml::to_string(&SeedAndIterations { + seed: seed.to_string(), + iterations: counter.to_string(), + }) + .unwrap(); + let res = fs::write(format!("threads/fuzzing_{}.txt", seed), serialized).await; - // TODO: Handle some errors - if res.is_err() { - error!("Error dumping packets: {:?}", res); + // TODO: Handle some errors + if res.is_err() { + error!("Error dumping packets: {:?}", res); + } } - } - // Dump the packet we crashed on - let _ = fs::create_dir("crashes").await; - let _ = fs::write( - format!("crashes/crash_{}.txt", seed), - format!("{:?}", last_packets), - ) - .await; + // Dump the packet we crashed on + let _ = fs::create_dir("crashes").await; + let _ = fs::write( + format!("crashes/crash_{}.txt", seed), + format!("{:?}", last_packets), + ) + .await; - info!("Thread {seed} finished at {counter} iterations, when {iterations} were the target!"); + info!("Thread {seed} finished at {counter} iterations, when {iterations} were the target!"); + }).await; }); let _ = task_handle.await; } From 230f22da1d49bece1b6489bfefbd908082c868da Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:05:17 +0200 Subject: [PATCH 06/17] fix: Crash wehen calculating zero iterations --- src/runtime/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 6502909..a4584af 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -101,7 +101,7 @@ pub async fn iterations_tracker(threads: usize, mut it_receiver: MpscReceiver Date: Wed, 6 Sep 2023 11:06:00 +0200 Subject: [PATCH 07/17] feat: Further work on instrumentation --- src/main.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 32cf120..3b39ad4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,12 +95,11 @@ struct SeedAndIterations { async fn main() -> color_eyre::Result<()> { color_eyre::install()?; #[cfg(not(feature = "stats"))] - { - let subscriber = tracing_subscriber::fmt() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .finish(); - tracing::subscriber::set_global_default(subscriber)?; - } + let subscriber = tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) + .finish(); + #[cfg(not(feature = "stats"))] + tracing::subscriber::set_global_default(subscriber)?; #[cfg(feature = "stats")] let tracer = opentelemetry_jaeger::new_collector_pipeline() .with_endpoint(env::var("OPENTELEMETRY_JAEGER_ENDPOINT").unwrap()) From 61b40304b19e7ac1bb94b4368fca2bdff78df845 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Wed, 6 Sep 2023 11:06:50 +0200 Subject: [PATCH 08/17] Revert "feat: Instrumentation" This reverts commit dccaff3b --- Cargo.toml | 8 ---- src/main.rs | 43 +---------------- src/markov/mod.rs | 9 +--- src/mqtt/mod.rs | 15 +----- src/network.rs | 2 - src/process_monitor/mod.rs | 3 ++ src/runtime/mod.rs | 95 +++++++++++++++++++------------------- 7 files changed, 56 insertions(+), 119 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c69a95b..b4ebf48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,12 +38,6 @@ tokio-tungstenite = "0.20.0" tokio-rustls = { version="0.24.1", features = ["dangerous_configuration"], optional = true } rustls = { version="0.21.6", features = ["dangerous_configuration"], optional = true } -# Opentelemetry tracing. Only activated when using the "stats" feature -opentelemetry = { version = "0.20.0", features=["trace", "metrics", "rt-tokio"] ,optional = true } -opentelemetry-otlp = { version = "0.13.0", features=["metrics"], optional = true } -opentelemetry-jaeger = { version = "0.19.0", features = ["rt-tokio", "isahc_collector_client", "collector_client", "reqwest_rustls_collector_client", "reqwest_blocking_collector_client"], optional = true } -tracing-opentelemetry = { version = "0.20.0", optional = true } - [profile.release] debug = true codegen-units = 1 @@ -55,5 +49,3 @@ tcp = [] quic = [] websocket = [] tls = ["dep:tokio-rustls", "dep:rustls"] -# TODO: Stats that add insight into performance, coverage etc. -stats = ["dep:opentelemetry-otlp", "dep:opentelemetry", "dep:opentelemetry-jaeger", "dep:tracing-opentelemetry"] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 3b39ad4..c0cd8d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,25 +26,14 @@ use crate::process_monitor::start_supervised_process; use crate::runtime::{iterations_tracker, run_thread}; use clap::{Parser, Subcommand}; use futures::future::join_all; - -#[cfg(feature = "stats")] -use opentelemetry::runtime::Tokio; -#[cfg(feature = "stats")] -use opentelemetry::trace::Tracer; use rand::{thread_rng, Rng}; use serde::{Deserialize, Serialize}; -#[cfg(feature = "stats")] -use std::env; use std::str::FromStr; use std::sync::Arc; use tokio::sync::mpsc::channel as mpsc_channel; use tokio::sync::RwLock; use tokio::{fs, task}; -use tracing::{debug, info, instrument, span, trace}; -#[cfg(feature = "stats")] -use tracing_subscriber::layer::SubscriberExt; -#[cfg(feature = "stats")] -use tracing_subscriber::util::SubscriberInitExt; +use tracing::{debug, info, trace}; mod markov; pub mod mqtt; @@ -93,36 +82,8 @@ struct SeedAndIterations { #[tokio::main] async fn main() -> color_eyre::Result<()> { + console_subscriber::init(); color_eyre::install()?; - #[cfg(not(feature = "stats"))] - let subscriber = tracing_subscriber::fmt() - .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) - .finish(); - #[cfg(not(feature = "stats"))] - tracing::subscriber::set_global_default(subscriber)?; - #[cfg(feature = "stats")] - let tracer = opentelemetry_jaeger::new_collector_pipeline() - .with_endpoint(env::var("OPENTELEMETRY_JAEGER_ENDPOINT").unwrap()) - .with_service_name("rusty-fume") - .with_reqwest() - .install_batch(Tokio)?; - #[cfg(feature = "stats")] - let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer); - #[cfg(feature = "stats")] - tracing_subscriber::registry() - .with(tracing_subscriber::EnvFilter::from_default_env()) - .with(opentelemetry) - .init(); - start_app().await?; - #[cfg(feature = "stats")] - { - opentelemetry::global::shutdown_tracer_provider(); - } - Ok(()) -} - -#[instrument] -async fn start_app() -> color_eyre::Result<()> { let cli = Cli::parse(); let packet_queue = Arc::new(RwLock::new( PacketQueue::read_from_file("./packet_pool.toml").await?, diff --git a/src/markov/mod.rs b/src/markov/mod.rs index e138deb..5f3b273 100644 --- a/src/markov/mod.rs +++ b/src/markov/mod.rs @@ -39,7 +39,7 @@ const SEL_FROM_QUEUE: f32 = 0.7; const PACKET_APPEND_CHANCE: f32 = 0.2; const SEND_CHANCE: f32 = 0.2; const BOF_CHANCE: f32 = 0.2; -const MUT_AFTER_SEND: f32 = 0.3; +const MUT_AFTER_SEND: f32 = 0.7; pub const MAX_PACKETS: usize = 10; #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] @@ -78,8 +78,6 @@ impl Distribution for Standard { pub trait ByteStream: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} impl ByteStream for T where T: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} - -#[derive(Debug)] pub struct StateMachine where B: ByteStream, @@ -108,7 +106,6 @@ impl Distribution for Standard { } } } - impl StateMachine where B: ByteStream, @@ -121,8 +118,6 @@ where previous_packets: Vec::new(), } } - - #[instrument] pub(crate) async fn execute( &mut self, mode: Mode, @@ -234,7 +229,7 @@ where } else { trace!("Sent packet successfully"); } - if (rng.gen_range(0f32..1f32) > MUT_AFTER_SEND) || res.is_err() { + if rng.gen_range(0f32..1f32) > MUT_AFTER_SEND || res.is_err() { self.state = State::Sf; } else { self.state = State::Mutate(rng.gen()); diff --git a/src/mqtt/mod.rs b/src/mqtt/mod.rs index 8ba03d8..6859136 100644 --- a/src/mqtt/mod.rs +++ b/src/mqtt/mod.rs @@ -1,11 +1,10 @@ use crate::markov::ByteStream; use crate::packets::{PacketQueue, Packets}; -use std::fmt::Display; use std::sync::Arc; use std::time::Duration; use tokio::sync::RwLock; use tokio::time::timeout; -use tracing::{debug, info, instrument, trace}; +use tracing::{debug, info, trace}; pub(crate) fn generate_connect_packet() -> [u8; 62] { [ @@ -56,16 +55,7 @@ pub(crate) enum SendError { // We couldn't send the packet. A previous packet might have crashed the server SendErr, } -impl Display for SendError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - SendError::Timeout => write!(f, "Timeout"), - SendError::ReceiveErr => write!(f, "Rcv error"), - SendError::SendErr => write!(f, "Send error"), - } - } -} -#[instrument(err)] + pub(crate) async fn send_packets( stream: &mut impl ByteStream, packets: &Packets, @@ -120,7 +110,6 @@ pub(crate) async fn send_packet( } /// This works by using the response packet as the key in a hashmap. If the packet is already in the hashmap we know that we have seen it before -#[instrument] async fn known_packet( response_packet: &[u8], input_packet: &Packets, diff --git a/src/network.rs b/src/network.rs index a329380..d1e6e0b 100644 --- a/src/network.rs +++ b/src/network.rs @@ -24,9 +24,7 @@ pub use tcp::*; mod tcp { use super::*; use tokio::net::{TcpStream, ToSocketAddrs}; - use tracing::instrument; - #[instrument(err, skip(address))] pub async fn connect_to_broker(address: impl ToSocketAddrs) -> Result { let tcpstream = TcpStream::connect(address).await?; Ok(tcpstream) diff --git a/src/process_monitor/mod.rs b/src/process_monitor/mod.rs index 989ad4f..d976210 100644 --- a/src/process_monitor/mod.rs +++ b/src/process_monitor/mod.rs @@ -7,7 +7,10 @@ use tokio::sync::oneshot::channel; use tokio::time::{sleep, timeout}; use tracing::{debug, info}; +// TODO: How do the tasks ask if the server has exited? And better yet, how do they get the message back? +// TODO: Also, how do the tasks know when it has caused new stdout/stderr output? // TODO: Allow the user to specify where to write the stdout/stderr of the monitored process. Maybe gzip compress it? +// TODO: Ask threads what their last packets were and dump it. /// Start the broker process and monitor it. If it crashes, we stop our execution. pub async fn start_supervised_process( sender: Sender<()>, diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index a4584af..6fcad9e 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -25,64 +25,63 @@ pub(crate) async fn run_thread( it_sender_clone: Sender, ) { let task_handle = task::spawn(async move { - info_span!("Thread {}", seed).in_scope(|| async { - let mut last_packets = Vec::new(); - let mut counter: u64 = 0; - let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); - while counter < iterations { - let new_stream = connect_to_broker(&address.clone()).await; - if new_stream.is_err() { - // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request - error!( + let mut last_packets = Vec::new(); + let mut counter: u64 = 0; + let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); + while counter < iterations { + let new_stream = connect_to_broker(&address.clone()).await; + if new_stream.is_err() { + // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request + error!( "Error connecting to broker: {:?}. See recommendations", new_stream ); - if !receiver_clone.is_empty() { - break; - } - // So we'll just have a "back-off" sleep here - sleep(Duration::from_millis(100)).await; - continue; - } - let new_tcpstream = new_stream.unwrap(); - let mut state_machine = StateMachine::new(new_tcpstream); - let mode = rng.gen(); - state_machine.execute(mode, &mut rng, &packet_queue).await; - last_packets = state_machine.previous_packets.clone(); - // We receive a message once the broker is stopped if !receiver_clone.is_empty() { break; } - counter += 1; - if counter % 5000 == 0 { - // Display iterations per second - let _ = it_sender_clone.send(counter).await; - } + // So we'll just have a "back-off" sleep here + sleep(Duration::from_millis(100)).await; + continue; + } + let new_tcpstream = new_stream.unwrap(); + let mut state_machine = StateMachine::new(new_tcpstream); + let mode = rng.gen(); + state_machine.execute(mode, &mut rng, &packet_queue).await; + last_packets = state_machine.previous_packets.clone(); + // We receive a message once the broker is stopped + // TODO: Also save last packets upon crash + if !receiver_clone.is_empty() { + break; + } + counter += 1; + if counter % 5000 == 0 { + // Display iterations per second + let _ = it_sender_clone.send(counter).await; } - if iterations == u64::MAX { - // If the fuzzing is stopped we dump the packets - let serialized = toml::to_string(&SeedAndIterations { - seed: seed.to_string(), - iterations: counter.to_string(), - }) - .unwrap(); - let res = fs::write(format!("threads/fuzzing_{}.txt", seed), serialized).await; + } + if iterations == u64::MAX { + // If the fuzzing is stopped we dump the packets + let serialized = toml::to_string(&SeedAndIterations { + seed: seed.to_string(), + iterations: counter.to_string(), + }) + .unwrap(); + let res = fs::write(format!("threads/fuzzing_{}.txt", seed), serialized).await; - // TODO: Handle some errors - if res.is_err() { - error!("Error dumping packets: {:?}", res); - } + // TODO: Handle some errors + if res.is_err() { + error!("Error dumping packets: {:?}", res); } - // Dump the packet we crashed on - let _ = fs::create_dir("crashes").await; - let _ = fs::write( - format!("crashes/crash_{}.txt", seed), - format!("{:?}", last_packets), - ) - .await; + } + // Dump the packet we crashed on + let _ = fs::create_dir("crashes").await; + let _ = fs::write( + format!("crashes/crash_{}.txt", seed), + format!("{:?}", last_packets), + ) + .await; - info!("Thread {seed} finished at {counter} iterations, when {iterations} were the target!"); - }).await; + info!("Thread {seed} finished at {counter} iterations, when {iterations} were the target!"); }); let _ = task_handle.await; } From b866f36c56870e985e04138a338226cc38b4a1bd Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:45:46 +0200 Subject: [PATCH 09/17] refactor: lib-bin split to allow benchmarking --- Cargo.toml | 11 +- Readme.md | 2 +- src/main.rs | 206 --------------------------- src/markov/mod.rs | 283 ------------------------------------- src/markov/mutations.rs | 118 ---------------- src/mqtt/mod.rs | 207 --------------------------- src/network.rs | 109 -------------- src/packet_pool.rs | 85 ----------- src/packets.rs | 66 --------- src/process_monitor/mod.rs | 68 --------- src/runtime/mod.rs | 107 -------------- 11 files changed, 5 insertions(+), 1257 deletions(-) delete mode 100644 src/main.rs delete mode 100644 src/markov/mod.rs delete mode 100644 src/markov/mutations.rs delete mode 100644 src/mqtt/mod.rs delete mode 100644 src/network.rs delete mode 100644 src/packet_pool.rs delete mode 100644 src/packets.rs delete mode 100644 src/process_monitor/mod.rs delete mode 100644 src/runtime/mod.rs diff --git a/Cargo.toml b/Cargo.toml index b4ebf48..b72b0f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,10 @@ license = "GPLv3" readme = "README.md" edition = "2021" +[workspace] + [dependencies] +lib = {path = "lib"} # Convenient error handling color-eyre = "0.6.2" # Logging @@ -32,12 +35,6 @@ serde_with = {version="3.1.0", features = ["hex"]} # Tokio Console Support console-subscriber = "0.1.10" -# For Websocket support -tokio-tungstenite = "0.20.0" -# For TLS support -tokio-rustls = { version="0.24.1", features = ["dangerous_configuration"], optional = true } -rustls = { version="0.21.6", features = ["dangerous_configuration"], optional = true } - [profile.release] debug = true codegen-units = 1 @@ -48,4 +45,4 @@ tcp = [] # TODO: Add quic, ws support quic = [] websocket = [] -tls = ["dep:tokio-rustls", "dep:rustls"] +tls = ["lib/tls"] diff --git a/Readme.md b/Readme.md index 2b8e882..fc788fe 100644 --- a/Readme.md +++ b/Readme.md @@ -36,7 +36,7 @@ Pull requests are welcome. For major changes, please open an issue first to disc Currently, the Windows build is failing in the ci, however i've only tested this on Linux so far. Maybe it works on Windows, maybe it doesn't. I don't know. Pull Requests to fix this if necessary are welcome. ## Trophies -All bugs found with this software. If you find a bug using rusty-FUME, please open an issue and i'll add it to the list. +All bugs found with this software. If you find a bug using rusty-FUME, please open an issue and I'll add it to the list once it is patched. - [FlashMQ Null pointer dereference](https://github.com/halfgaar/FlashMQ/commit/eb3acf88771af3eeddf086e4c9dc51d703456eee) diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index c0cd8d9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,206 +0,0 @@ -//! # rusty-FUME -//! rusty-FUME is a fuzzer for the MQTT protocol. It is based on [FUME-Fuzzing-MQTT Brokers](https://github.com/PBearson/FUME-Fuzzing-MQTT-Brokers) -//! and uses markov chains to generate new packet chains. If it discovers a new response behaviour the chain is added to the fuzzing queue. -//! We use [tokio](https://tokio.rs/) for async networking. -//! ## The state machine -//! We implement a State machine with a markov chain. All probabilities are configurable for this process(except the ones with only one option). -//! The state machine is defined as follows for the Mutation Guided Fuzzing: -//! - S0: Initial State: Either goto CONNECT state or select a packet from the queue and go to MUTATION state -//! - CONNECT: Add connect to the current chain and go to ADDING State -//! - ADDING: Either add a new packet(configurable probability for each one) to the chain or go to MUTATION state -//! - MUTATION: Mutate, delete, inject or SEND the current chain -//! - SEND: Send the current chain and either go to Sf or MUTATION state -//! - Sf: Final State -//! And this way for Generation Guided Fuzzing: -//! - S0: Initial State: Goto ADD(CONNECT) state -//! - CONNECT: Add connect to the current chain and go to S1 -//! - S1: Either add a new packet or go to S2 -//! - S2: Inject/Delete/Mutate the current chain or go to SEND -//! - SEND: Send the current chain and either go to Sf or S2 -//! Once they get to S2 they behave the same way. -use crate::markov::MAX_PACKETS; -use crate::mqtt::test_connection; -use crate::network::connect_to_broker; -use crate::packets::PacketQueue; -use crate::process_monitor::start_supervised_process; -use crate::runtime::{iterations_tracker, run_thread}; -use clap::{Parser, Subcommand}; -use futures::future::join_all; -use rand::{thread_rng, Rng}; -use serde::{Deserialize, Serialize}; -use std::str::FromStr; -use std::sync::Arc; -use tokio::sync::mpsc::channel as mpsc_channel; -use tokio::sync::RwLock; -use tokio::{fs, task}; -use tracing::{debug, info, trace}; - -mod markov; -pub mod mqtt; -mod network; -mod packet_pool; -mod packets; -mod process_monitor; -mod runtime; -// TODO: Clean up main -// TODO: Try fuzzing a basic mongoose server? -// TODO: Fuzz mosquitto compiled with sanitizers -// TODO: Support TLS, WS and QUIC - -#[derive(Parser, Debug)] -#[command(author, version, about)] -struct Cli { - #[command(subcommand)] - subcommand: SubCommands, - #[arg(short, long, default_value = "127.0.0.1:1883")] - target: String, - #[arg(short, long)] - broker_command: String, - // TODO: Make the timeout configurable - #[arg(long, default_value = "200")] - timeout: u64, -} - -#[derive(Subcommand, Debug)] -enum SubCommands { - // TODO: Do Fuzzing args like threads, chances etc - Fuzz { - #[arg(short, long, default_value_t = 100)] - threads: u64, - }, - Replay { - #[arg(short, long, default_value_t = false)] - sequential: bool, - }, -} -/// Struct to serialize threads once they are done(aka the broker has crashed). -#[derive(Serialize, Deserialize, Debug)] -struct SeedAndIterations { - pub seed: String, - pub iterations: String, -} - -#[tokio::main] -async fn main() -> color_eyre::Result<()> { - console_subscriber::init(); - color_eyre::install()?; - let cli = Cli::parse(); - let packet_queue = Arc::new(RwLock::new( - PacketQueue::read_from_file("./packet_pool.toml").await?, - )); - match &cli.subcommand { - SubCommands::Fuzz { threads } => { - // The channel used for iteration counting - let (it_sender, it_receiver) = mpsc_channel::(*threads as usize); - // This receiver is necessary to dump the packets once the broker is stopped - let (sender, _) = tokio::sync::broadcast::channel(1); - let mut subscribers = vec![]; - for _ in 0..*threads { - subscribers.push(sender.subscribe()); - } - start_supervised_process(sender, cli.broker_command).await?; - let address = cli.target.clone(); - let mut stream = connect_to_broker(&cli.target).await?; - test_connection(&mut stream).await?; - info!("Connection established, starting fuzzing!"); - let mut rng = thread_rng(); - let _ = fs::create_dir("./threads").await; - let mut task_handles = vec![]; - for _ in 0u64..*threads { - let it_sender_clone = it_sender.clone(); - let receiver_clone = subscribers.pop().unwrap(); - let seed: u64 = rng.gen(); - task_handles.push(run_thread( - seed, - receiver_clone, - address.clone(), - u64::MAX, - packet_queue.clone(), - it_sender_clone, - )); - } - // Track it/s - let threads = *threads as usize; - task::spawn(async move { - iterations_tracker(threads, it_receiver).await; - }); - join_all(task_handles).await; - let serialized_pkg_pool = toml::to_string(&packet_queue.write().await.clone()); - trace!("Packet Queue: {:?}", serialized_pkg_pool); - } - SubCommands::Replay { sequential } => { - // TODO: When replaying the tasks may get loaded in different order - // Iterate through all fuzzing_{}.txt files and replay them one after another - let mut files = fs::read_dir("./threads") - .await - .expect("Failed to find threads folder. Cannot Replay"); - let mut filtered_files = vec![]; - trace!( - "Found files: {:?} in folder {:?}", - files, - std::env::current_dir() - ); - - while let Some(entry) = files.next_entry().await? { - let path = entry.path(); - let path_str = path.to_string_lossy().to_string(); - if path_str.starts_with("./threads/fuzzing_") && path_str.ends_with(".txt") { - filtered_files.push(path_str); - } - } - trace!("Found {} files", filtered_files.len()); - let (sender, receiver) = tokio::sync::broadcast::channel::<()>(1); - let mut subscribers = vec![]; - for _ in 0..filtered_files.len() { - subscribers.push(sender.subscribe()); - } - start_supervised_process(sender, cli.broker_command).await?; - let mut stream = connect_to_broker(&cli.target).await?; - test_connection(&mut stream).await?; - debug!("Connection established"); - debug!("Starting replay with {} seeds", filtered_files.len()); - let mut threads = vec![]; - let unused_it_channel = mpsc_channel::(filtered_files.len()).0; - for file in filtered_files { - let receiver_clone = subscribers.pop().unwrap(); - let seed_and_iterations: SeedAndIterations = - toml::from_str(&fs::read_to_string(file).await?)?; - threads.push(run_thread( - u64::from_str(&seed_and_iterations.seed).unwrap(), - receiver_clone, - cli.target.clone(), - u64::from_str(&seed_and_iterations.iterations).unwrap(), - packet_queue.clone(), - unused_it_channel.clone(), - )); - } - if *sequential { - for (index, thread) in threads.into_iter().enumerate() { - info!("Replaying thread number {}", index); - thread.await; - if !receiver.is_empty() { - info!("Crashing seed found!"); - return Ok(()); - } - } - info!("No crash found :/"); - } else { - join_all(threads).await; - } - } - } - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::packets::Packets; - #[test] - fn test_serialize_packet_queue() { - let mut packet_queue = PacketQueue::default(); - packet_queue.inner.insert(vec![0x10], Packets::default()); - let serialized = toml::to_string(&packet_queue).unwrap(); - println!("{}", serialized); - } -} diff --git a/src/markov/mod.rs b/src/markov/mod.rs deleted file mode 100644 index 5f3b273..0000000 --- a/src/markov/mod.rs +++ /dev/null @@ -1,283 +0,0 @@ -//! ## The state machine -//! We implement a State machine with a markov chain. All probabilities are configurable for this process(except the ones with only one option). -//! The state machine is defined as follows for the Mutation Guided Fuzzing: -//! - S0: Initial State: Either goto CONNECT state or select a packet from the queue and go to MUTATION state -//! - CONNECT: Add connect to the current chain and go to ADDING State -//! - ADDING: Either add a new packet(configurable probability for each one) to the chain or go to MUTATION state -//! - MUTATION: Mutate, delete, inject or SEND the current chain -//! - SEND: Send the current chain and either go to Sf or MUTATION state -//! - Sf: Final State -//! And this way for Generation Guided Fuzzing: -//! - S0: Initial State: Goto ADD(CONNECT) state -//! - CONNECT: Add connect to the current chain and go to S1 -//! - S1: Either add a new packet or go to S2 -//! - S2: Inject/Delete/Mutate the current chain or go to SEND -//! - SEND: Send the current chain and either go to Sf or S2 -//! Once they get to S2 they behave the same way. -mod mutations; - -use crate::markov::mutations::{delete, inject, swap, InjectType}; -use crate::markov::Mode::{GenerationGuided, MutationGuided}; -use crate::mqtt::{ - generate_connect_packet, generate_disconnect_packet, generate_pingreq_packet, - generate_publish_packet, generate_subscribe_packet, generate_unsubscribe_packet, send_packets, - SendError, -}; -use crate::packets::{PacketQueue, Packets}; -use rand::distributions::Standard; -use rand::prelude::Distribution; -use rand::Rng; -use rand_xoshiro::Xoshiro256PlusPlus; -use std::default::Default; -use std::fmt::Debug; -use std::sync::Arc; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::sync::RwLock; -use tracing::*; - -const SEL_FROM_QUEUE: f32 = 0.7; -const PACKET_APPEND_CHANCE: f32 = 0.2; -const SEND_CHANCE: f32 = 0.2; -const BOF_CHANCE: f32 = 0.2; -const MUT_AFTER_SEND: f32 = 0.7; -pub const MAX_PACKETS: usize = 10; - -#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] -pub enum State { - #[default] - S0, - ADD(PacketType), - ADDING, - SelectFromQueue, - MUTATION, - Mutate(Mutations), - SEND, - Sf, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Mutations { - // Inserts bytes into the payload - Inject(InjectType), - // Deletes bytes from the payload - Delete, - // Changes bytes in the payload - Swap, -} -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Mutations { - match rng.gen_range(0..3) { - 0 => Mutations::Inject(rng.gen()), - 1 => Mutations::Delete, - 2 => Mutations::Swap, - _ => unreachable!(), - } - } -} - -pub trait ByteStream: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} - -impl ByteStream for T where T: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} -pub struct StateMachine -where - B: ByteStream, -{ - // The current state of the state machine - pub(crate) state: State, - // The current packet in bytes - packets: Packets, - // Previous packets. Useful for dumping the packets to disk(For tracking errors) - pub previous_packets: Vec, - // The current stream, TlsStream TcpStream or WebsocketStream - stream: B, -} -#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] -pub enum Mode { - MutationGuided, - GenerationGuided, -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> Mode { - match rng.gen_range(0..10) { - 0..=4 => MutationGuided, - 5..=9 => GenerationGuided, - _ => unreachable!(), - } - } -} -impl StateMachine -where - B: ByteStream, -{ - pub(crate) fn new(stream: B) -> Self { - Self { - stream, - state: Default::default(), - packets: Packets::new(), - previous_packets: Vec::new(), - } - } - pub(crate) async fn execute( - &mut self, - mode: Mode, - rng: &mut Xoshiro256PlusPlus, - packet_queue: &Arc>, - ) { - while self.state != State::Sf { - self.next(mode, rng, packet_queue).await; - trace!("State: {:?}", self.state); - } - } - async fn next( - &mut self, - mode: Mode, - rng: &mut Xoshiro256PlusPlus, - packet_queue: &Arc>, - ) { - match &self.state { - State::S0 => match mode { - MutationGuided => { - if rng.gen_range(0f32..1f32) < SEL_FROM_QUEUE && !self.packets.is_full() { - self.state = State::ADD(PacketType::CONNECT); - } else { - self.state = State::SelectFromQueue; - } - } - GenerationGuided => { - self.state = State::ADD(PacketType::CONNECT); - } - }, - State::SelectFromQueue => { - // Maybe we should use a priority queue in-memory here instead of storing on disk(overhead). Should be measured in the future. - if packet_queue.read().await.inner.is_empty() { - self.state = State::ADD(PacketType::CONNECT); - } else { - let packet_queue_read = packet_queue.read().await; - let packet_index = rng.gen_range(0..packet_queue_read.inner.len()); - let packet = packet_queue_read.inner.iter().nth(packet_index).unwrap(); - // TODO: Really? - self.packets = packet.1.clone().clone(); - self.state = State::MUTATION; - } - } - State::ADD(packet_type) => { - match packet_type { - PacketType::CONNECT => { - self.packets.append(&generate_connect_packet()); - } - PacketType::PUBLISH => { - self.packets.append(&generate_publish_packet()); - } - PacketType::SUBSCRIBE => { - self.packets.append(&generate_subscribe_packet()); - } - PacketType::UNSUBSCRIBE => { - self.packets.append(&generate_unsubscribe_packet()); - } - PacketType::PINGREQ => { - self.packets.append(&generate_pingreq_packet()); - } - PacketType::DISCONNECT => { - self.packets.append(&generate_disconnect_packet()); - } - _ => unreachable!(), - } - self.state = State::ADDING - } - State::ADDING => { - if rng.gen_range(0f32..1f32) < PACKET_APPEND_CHANCE { - self.state = State::ADD(rng.gen()); - } else { - self.state = State::MUTATION; - } - } - State::MUTATION => { - if rng.gen_range(0f32..1f32) < SEND_CHANCE { - self.state = State::SEND; - } else { - self.state = State::Mutate(rng.gen()); - } - } - State::Mutate(mutation) => { - match mutation { - Mutations::Inject(t) => { - inject(&mut self.packets, rng, t); - } - Mutations::Delete => { - delete(&mut self.packets, rng); - } - Mutations::Swap => { - swap(&mut self.packets, rng); - } - } - self.state = State::MUTATION; - } - State::SEND => { - self.previous_packets.push(self.packets.clone()); - let res = send_packets(&mut self.stream, &self.packets, packet_queue).await; - if let Err(e) = res { - match e { - SendError::Timeout => {} - SendError::ReceiveErr => { - trace!("Receive error, probably disconnected by the broker...") - } - SendError::SendErr => { - trace!("Send error, probably disconnected by the broker...") - } - } - } else { - trace!("Sent packet successfully"); - } - if rng.gen_range(0f32..1f32) > MUT_AFTER_SEND || res.is_err() { - self.state = State::Sf; - } else { - self.state = State::Mutate(rng.gen()); - } - } - _ => todo!(), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -enum BOF { - BOF, - NOBOF, -} - -/// The MQTT Packet types -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum PacketType { - AUTH, - CONNACK, - CONNECT, - DISCONNECT, - PINGREQ, - PINGRESP, - PUBACK, - PUBCOMP, - PUBLISH, - PUBREC, - PUBREL, - RESERVED, - SUBACK, - SUBSCRIBE, - UNSUBACK, - UNSUBSCRIBE, -} - -// Implement a distribution for the packet types -impl Distribution for rand::distributions::Standard { - fn sample(&self, rng: &mut R) -> PacketType { - match rng.gen_range(0..6) { - 0 => PacketType::CONNECT, - 1 => PacketType::PUBLISH, - 2 => PacketType::SUBSCRIBE, - 3 => PacketType::UNSUBSCRIBE, - 4 => PacketType::PINGREQ, - 5 => PacketType::DISCONNECT, - _ => unreachable!(), - } - } -} diff --git a/src/markov/mutations.rs b/src/markov/mutations.rs deleted file mode 100644 index 5dd6df6..0000000 --- a/src/markov/mutations.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::packets::Packets; -use rand::distributions::Standard; -use rand::prelude::Distribution; -use rand::Rng; -use rand_xoshiro::Xoshiro256PlusPlus; - -pub fn inject(packets: &mut Packets, rng: &mut Xoshiro256PlusPlus, inject_type: &InjectType) { - let packets_size = packets.size(); - debug_assert!(packets_size > 0, "Packet size should be greater than 0"); - let packet = packets - .inner - .get_mut(rng.gen_range(0..packets_size)) - .unwrap(); - match inject_type { - InjectType::Single => inject_single(packet, rng), - InjectType::BOF => inject_bof(packet, rng), - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum InjectType { - Single, - BOF, -} - -impl Distribution for Standard { - fn sample(&self, rng: &mut R) -> InjectType { - match rng.gen_range(0..2) { - 0 => InjectType::Single, - 1 => InjectType::BOF, - _ => unreachable!(), - } - } -} - -fn inject_bof(packet: &mut Vec, rng: &mut Xoshiro256PlusPlus) { - let idx = rng.gen_range(0..packet.len()); - // To fight big packets - let byte_length = 350 / packet.len(); - let mut bytes = vec![0; byte_length]; - rng.fill(&mut bytes[..]); - packet.splice(idx..idx, bytes); -} - -fn inject_single(packet: &mut Vec, rng: &mut Xoshiro256PlusPlus) { - let idx = rng.gen_range(0..packet.len()); - let byte = rng.gen::(); - packet.insert(idx, byte); -} - -pub fn delete(packets: &mut Packets, rng: &mut Xoshiro256PlusPlus) { - let packets_size = packets.size(); - let packet = packets - .inner - .get_mut(rng.gen_range(0..packets_size)) - .unwrap(); - let idx = rng.gen_range(0..packet.len()); - packet.remove(idx); -} - -pub fn swap(packets: &mut Packets, rng: &mut Xoshiro256PlusPlus) { - let packets_size = packets.size(); - let packet = packets - .inner - .get_mut(rng.gen_range(0..packets_size)) - .unwrap(); - let idx = rng.gen_range(0..packet.len()); - let byte = rng.gen::(); - packet[idx] = byte; -} - -#[cfg(test)] -mod tests { - use super::*; - use rand::prelude::thread_rng; - /* - #[test] - fn test_inject_bof() { - let mut rng = thread_rng(); - let mut packet = vec![0; 10]; - println!("Input packet: {:?}", packet); - inject_bof(&mut packet, &mut rng); - println!("Output packet: {:?}", packet); - assert!(packet.len() > 10); - assert!(packet.len() < 30); - } - - #[test] - fn test_inject_single() { - let mut rng = thread_rng(); - let mut packet = vec![0; 10]; - println!("Input packet: {:?}", packet); - inject_single(&mut packet, &mut rng); - println!("Output packet: {:?}", packet); - assert_eq!(packet.len(), 11); - } - - #[test] - fn test_swap() { - let mut rng = thread_rng(); - let mut packet = vec![0; 10]; - println!("Input packet: {:?}", packet); - swap(&mut packet, &mut rng); - println!("Output packet: {:?}", packet); - assert_eq!(packet.len(), 10); - } - - #[test] - fn test_delete() { - let mut rng = thread_rng(); - let mut packet = vec![0; 10]; - println!("Input packet: {:?}", packet); - delete(&mut packet, &mut rng); - println!("Output packet: {:?}", packet); - assert!(packet.len() < 10); - assert!(packet.len() > 0); - }*/ -} diff --git a/src/mqtt/mod.rs b/src/mqtt/mod.rs deleted file mode 100644 index 6859136..0000000 --- a/src/mqtt/mod.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::markov::ByteStream; -use crate::packets::{PacketQueue, Packets}; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::RwLock; -use tokio::time::timeout; -use tracing::{debug, info, trace}; - -pub(crate) fn generate_connect_packet() -> [u8; 62] { - [ - 16, 60, 0, 4, 77, 81, 84, 84, 4, 4, 0, 0, 0, 17, 72, 101, 108, 108, 111, 32, 77, 81, 84, - 84, 32, 66, 114, 111, 107, 101, 114, 0, 5, 116, 111, 112, 105, 99, 0, 22, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 72, 255, 50, 0, 0, 0, - ] -} - -pub(crate) fn generate_publish_packet() -> [u8; 27] { - [ - 49, 25, 0, 5, 116, 111, 112, 105, 99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 72, 255, 50, - 0, 0, 0, - ] -} - -pub(crate) fn generate_subscribe_packet() -> [u8; 12] { - [130, 10, 0, 100, 0, 5, 116, 111, 112, 105, 99, 0] -} - -pub(crate) fn generate_unsubscribe_packet() -> [u8; 11] { - [162, 9, 0, 10, 0, 5, 116, 111, 112, 105, 99] -} - -pub(crate) fn generate_disconnect_packet() -> [u8; 2] { - [224, 0] -} - -pub(crate) fn generate_pingreq_packet() -> [u8; 2] { - [192, 0] -} - -pub(crate) async fn test_connection(stream: &mut impl ByteStream) -> color_eyre::Result<()> { - stream - .write_all(generate_connect_packet().as_slice()) - .await?; - let mut buf = [0; 1024]; - let _ = timeout(Duration::from_secs(1), stream.read(&mut buf)).await; - debug!("Received Packet hex encoded: {:?}", hex::encode(buf)); - Ok(()) -} -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub(crate) enum SendError { - // Probably a DOS discovered. The server didn't respond in time - Timeout, - // The server crashed. As the connection is closed or similar - ReceiveErr, - // We couldn't send the packet. A previous packet might have crashed the server - SendErr, -} - -pub(crate) async fn send_packets( - stream: &mut impl ByteStream, - packets: &Packets, - packet_queue: &Arc>, -) -> Result<(), SendError> { - for packet in packets.inner.iter().filter(|p| !p.is_empty()) { - send_packet(stream, packet.as_slice(), packets, packet_queue).await?; - } - Ok(()) -} - -const PACKET_TIMEOUT: u64 = 30; - -pub(crate) async fn send_packet( - stream: &mut impl ByteStream, - packet: &[u8], - packets: &Packets, - packet_queue: &Arc>, -) -> Result<(), SendError> { - let write_result = timeout( - Duration::from_millis(PACKET_TIMEOUT), - stream.write_all(packet), - ) - .await; - match write_result { - Ok(Ok(_)) => (), - Err(t) => { - trace!("Timeout: {:?}", t); - } - Ok(Err(e)) => { - trace!("Send error: {:?}", e); - return Err(SendError::SendErr); - } - } - let mut buf = [0; 1024]; - let res = timeout(Duration::from_millis(PACKET_TIMEOUT), stream.read(&mut buf)).await; - match res { - Ok(Ok(p)) => { - known_packet(&buf[..p], packets, packet_queue).await; - Ok(()) - } - Err(t) => { - trace!("Timeout: {:?}", t); - // TODO: Retry sending the packet - Err(SendError::Timeout) - } - Ok(Err(e)) => { - trace!("Receive error: {:?}", e); - Err(SendError::ReceiveErr) - } - } -} - -/// This works by using the response packet as the key in a hashmap. If the packet is already in the hashmap we know that we have seen it before -async fn known_packet( - response_packet: &[u8], - input_packet: &Packets, - packet_queue: &Arc>, -) -> bool { - // TODO: decode the packet and extract user id, payload, topic etc. because those don't matter to see if it is a known packet - let queue_lock = packet_queue.read().await; - let response_packet = response_packet.to_vec(); - if !queue_lock.inner.contains_key(&response_packet) { - info!("New behavior discovered, adding it to the queue",); - debug!("Response packet: {:?}", response_packet); - drop(queue_lock); - let mut queue_lock = packet_queue.write().await; - queue_lock - .inner - .insert(response_packet, input_packet.clone()); - } - trace!( - "Known behavior. We have {} known behaviors", - packet_queue.read().await.inner.len() - ); - false -} -#[cfg(test)] -mod tests { - use super::*; - use mqtt::packet::QoSWithPacketIdentifier; - use mqtt::{Encodable, TopicFilter, TopicName}; - // To not generate these packets over and over again during execution of the markov model, we generate them here and then use them in the functions - #[test] - fn generate_connect_packet() { - let mut connect = mqtt::packet::ConnectPacket::new("Hello MQTT Broker"); - connect.set_will(Some(( - TopicName::new("topic").unwrap(), - vec![ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 72, 255, 50, 0, 0, 0, - ], - ))); - let mut packet = Vec::new(); - connect.encode(&mut packet).unwrap(); - println!("{packet:?}"); - } - - #[test] - fn generate_publish_packet() { - let mut publish = mqtt::packet::PublishPacket::new( - TopicName::new("topic").unwrap(), - QoSWithPacketIdentifier::Level0, - vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 72, 255, 50, 0, 0, 0], - ); - publish.set_retain(true); - let mut packet = Vec::new(); - publish.encode(&mut packet).unwrap(); - println!("{packet:?}"); - } - - #[test] - fn generate_subscribe_packet() { - let subscribe = mqtt::packet::SubscribePacket::new( - 100, - vec![( - TopicFilter::new("topic").unwrap(), - mqtt::QualityOfService::Level0, - )], - ); - let mut packet = Vec::new(); - subscribe.encode(&mut packet).unwrap(); - println!("{packet:?}"); - } - - #[test] - fn generate_unsubscribe_packet() { - let unsubscribe = - mqtt::packet::UnsubscribePacket::new(10, vec![TopicFilter::new("topic").unwrap()]); - let mut packet = Vec::new(); - unsubscribe.encode(&mut packet).unwrap(); - println!("{packet:?}"); - } - - #[test] - fn generate_disconnect_packet() { - let disconnect = mqtt::packet::DisconnectPacket::new(); - let mut packet = Vec::new(); - disconnect.encode(&mut packet).unwrap(); - println!("{packet:?}"); - } - - #[test] - fn generate_pingreq_packet() { - let pingreq = mqtt::packet::PingreqPacket::new(); - let mut packet = Vec::new(); - pingreq.encode(&mut packet).unwrap(); - println!("{packet:?}"); - } -} diff --git a/src/network.rs b/src/network.rs deleted file mode 100644 index d1e6e0b..0000000 --- a/src/network.rs +++ /dev/null @@ -1,109 +0,0 @@ -use color_eyre::Result; -// Avoid double protocols -// We have a lot of features which are mutually exclusive, so we need compile time errors if someone tries to use them together. -// These are the features: tcp, websocket, tls, quic -#[cfg(all( - feature = "tcp", - any(feature = "websocket", feature = "tls", feature = "quic") -))] -compile_error!( - "You can only use one protocol at a time. Please disable tcp with --no-default-features" -); -#[cfg(all(feature = "websocket", any(feature = "tls", feature = "quic")))] -compile_error!( - "You can only use one protocol at a time. Please disable websocket with --no-default-features" -); -#[cfg(all(feature = "tls", feature = "quic"))] -compile_error!( - "You can only use one protocol at a time. Please disable tls with --no-default-features" -); - -#[cfg(feature = "tcp")] -pub use tcp::*; -#[cfg(feature = "tcp")] -mod tcp { - use super::*; - use tokio::net::{TcpStream, ToSocketAddrs}; - - pub async fn connect_to_broker(address: impl ToSocketAddrs) -> Result { - let tcpstream = TcpStream::connect(address).await?; - Ok(tcpstream) - } -} - -#[cfg(feature = "websocket")] -pub use websocket::*; - -#[cfg(feature = "websocket")] -mod websocket { - compile_error!("Websocket is not implemented yet"); - /* - use super::*; - use tokio::net::TcpStream; - use tokio_tungstenite::tungstenite::client::IntoClientRequest; - use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; - - pub async fn connect_to_broker( - address: impl IntoClientRequest + Unpin, - ) -> Result>> { - let (ws_stream, _) = tokio_tungstenite::connect_async(address).await?; - Ok(ws_stream) - } - */ -} - -#[cfg(feature = "tls")] -pub use tls::*; - -#[cfg(feature = "tls")] -mod tls { - use super::*; - use crate::markov::ByteStream; - use futures::FutureExt; - use rustls::client::{ServerCertVerified, ServerCertVerifier}; - use rustls::{Certificate, ClientConfig, ClientConnection, Error, ServerName}; - use std::net::IpAddr; - use std::str::FromStr; - use std::sync::Arc; - use std::time::SystemTime; - use tokio::io::{AsyncReadExt, AsyncWriteExt}; - use tokio::net::{TcpStream, ToSocketAddrs}; - use tokio_rustls::client::TlsStream; - use tokio_rustls::TlsConnector; - - struct NoCertificateVerifier; - - impl ServerCertVerifier for NoCertificateVerifier { - fn verify_server_cert( - &self, - end_entity: &Certificate, - intermediates: &[Certificate], - server_name: &ServerName, - scts: &mut dyn Iterator, - ocsp_response: &[u8], - now: SystemTime, - ) -> std::result::Result { - Ok(ServerCertVerified::assertion()) - } - } - /// Connects to the broker using TLS ignoring the server certificate since that would just be - /// wasted iterations - pub async fn connect_to_broker(address: &str) -> Result> { - let mut socket = TcpStream::connect(address).await?; - let mut config = ClientConfig::builder() - .with_safe_defaults() - .with_custom_certificate_verifier(Arc::new(NoCertificateVerifier)) - .with_no_client_auth(); - let connector = TlsConnector::from(Arc::new(config)); - let stream = connector - .connect( - // Trim everything including the port beginning ':' - ServerName::IpAddress( - IpAddr::from_str(address.split(':').next().unwrap()).unwrap(), - ), - socket, - ) - .await?; - Ok(stream) - } -} diff --git a/src/packet_pool.rs b/src/packet_pool.rs deleted file mode 100644 index 5b73b2f..0000000 --- a/src/packet_pool.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! Here are packets from previously discovered CVEs - -use crate::MAX_PACKETS; -/// https://www.cvedetails.com/cve/CVE-2021-34432/ -#[allow(unused)] -const CVE_2021_34432: &[&[u8]; MAX_PACKETS] = &[ - &[ - 16, 60, 0, 4, 77, 81, 84, 84, 4, 4, 0, 0, 0, 17, 72, 101, 108, 108, 111, 32, 77, 81, 84, - 84, 32, 66, 114, 111, 107, 101, 114, 0, 5, 116, 111, 112, 105, 99, 0, 22, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 72, 255, 50, 0, 0, 0, - ], - &[48, 10, 0, 0, 116, 101, 115, 116, 116, 101, 115, 116], - &[224], - &[], - &[], - &[], - &[], - &[], - &[], - &[], -]; -#[allow(unused)] -const OTHER_MOSQUITTO_CVE: &[&[u8]; MAX_PACKETS] = &[ - &[ - 16, 96, 0, 4, 77, 81, 84, 84, 5, 192, 93, 85, 34, 21, 0, 15, 98, 99, 82, 85, 100, 109, 83, - 68, 89, 117, 119, 98, 86, 54, 50, 22, 0, 6, 72, 55, 70, 79, 120, 77, 23, 0, 25, 1, 34, 234, - 35, 0, 25, 118, 116, 78, 87, 72, 80, 101, 56, 48, 98, 52, 99, 86, 120, 102, 85, 107, 110, - 114, 116, 86, 89, 68, 122, 88, 0, 14, 86, 52, 86, 108, 79, 115, 54, 55, 73, 100, 84, 81, - 70, 68, 0, 6, 90, 48, 53, 99, 79, 57, 112, 4, 122, 230, 236, 0, 112, 24, 205, 229, 136, 20, - 38, 12, 49, 107, 51, 69, 111, 109, 83, 86, 119, 70, 114, 0, 3, 50, 75, 86, 64, 54, 206, - 187, 210, 50, 38, 0, 24, 57, 105, 111, 113, 118, 80, 115, 53, 68, 85, 111, 97, 56, 115, 79, - 43, 81, 54, 86, 103, 77, 49, 54, 112, 21, 100, 50, 84, 116, 114, 75, 66, 73, 116, 88, 103, - 106, 56, 97, 84, 84, 81, 89, 107, 81, 118, - ], - &[], - &[], - &[], - &[], - &[], - &[], - &[], - &[], - &[], -]; -mod tests { - use super::*; - use crate::packets::{PacketQueue, Packets}; - use std::fs::write; - - #[test] - fn nanomq_bug() { - let packet = hex::decode("106000044d51545405c05d552215000f62635255646d5344597577625636321600064837464f784d1700190122ea23001976744e574850653830623463567866556b6e72745659447a58000e5634566c4f73363749645451464400065a3035634f3970047ae6ec007018cde58814260c316b33456f6d53567746720003324b564036cebbd23226001839696f717650733544556f6138734f2b513656674d3136701564325474724b42497458676a3861545451596b5176"); - let packet = packet.unwrap(); - println!("Packet: {:?}", packet); - } - #[test] - fn serialize_packet_pool() { - let mut packet_pool = PacketQueue::default(); - let packets = Packets { - inner: CVE_2021_34432 - .iter() - .map(|x| x.to_vec()) - .collect::>>() - .try_into() - .unwrap(), - }; - packet_pool - .inner - .insert(CVE_2021_34432[1].to_vec(), packets); - let packets = Packets { - inner: OTHER_MOSQUITTO_CVE - .iter() - .map(|x| x.to_vec()) - .collect::>>() - .try_into() - .unwrap(), - }; - packet_pool - .inner - .insert(OTHER_MOSQUITTO_CVE[0].to_vec(), packets); - let serialized = toml::to_string(&packet_pool).unwrap(); - println!("Serialized: {}", serialized); - write("packet_pool.toml", serialized).unwrap(); - } -} diff --git a/src/packets.rs b/src/packets.rs deleted file mode 100644 index adc39e0..0000000 --- a/src/packets.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::markov::MAX_PACKETS; -use serde::{Deserialize, Serialize}; -use serde_with::formats::CommaSeparator; -use serde_with::serde_as; -use serde_with::StringWithSeparator; -use std::cmp::min; -use std::collections::BTreeMap; -use std::fmt::Display; -use std::path::Path; -use tokio::fs::File; -use tokio::io::AsyncReadExt; -#[serde_as] -#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] -pub struct Packets { - #[serde_as(as = "[StringWithSeparator::; MAX_PACKETS]")] - pub(crate) inner: [Vec; MAX_PACKETS], -} -impl Display for Packets { - // Hex dump the packets - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut s = String::new(); - for i in 0..MAX_PACKETS { - if !self.inner[i].is_empty() { - s.push_str(hex::encode(&self.inner[i]).as_str()); - s.push('\n'); - } - } - write!(f, "{}", s) - } -} -impl Packets { - pub fn append(&mut self, packet: &[u8]) { - // Search the first free slot and insert it there - let size = self.size(); - if size < MAX_PACKETS { - self.inner[size] = packet.to_vec(); - } - } - pub fn is_full(&self) -> bool { - self.inner.iter().all(|x| !x.is_empty()) - } - pub fn size(&self) -> usize { - min(1, self.inner.iter().filter(|x| !x.is_empty()).count()) - } - pub fn new() -> Self { - Self { - inner: Default::default(), - } - } -} - -#[serde_as] -#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] -pub struct PacketQueue { - #[serde_as(as = "BTreeMap, _>")] - pub(crate) inner: BTreeMap, Packets>, -} - -impl PacketQueue { - pub async fn read_from_file(path: impl AsRef) -> color_eyre::Result { - let mut content = String::new(); - File::open(path).await?.read_to_string(&mut content).await?; - let queue = toml::from_str(&content)?; - Ok(queue) - } -} diff --git a/src/process_monitor/mod.rs b/src/process_monitor/mod.rs deleted file mode 100644 index d976210..0000000 --- a/src/process_monitor/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::time::Duration; -use tokio::io::{AsyncBufReadExt, BufReader}; -use tokio::process::Command; -use tokio::signal; -use tokio::sync::broadcast::Sender; -use tokio::sync::oneshot::channel; -use tokio::time::{sleep, timeout}; -use tracing::{debug, info}; - -// TODO: How do the tasks ask if the server has exited? And better yet, how do they get the message back? -// TODO: Also, how do the tasks know when it has caused new stdout/stderr output? -// TODO: Allow the user to specify where to write the stdout/stderr of the monitored process. Maybe gzip compress it? -// TODO: Ask threads what their last packets were and dump it. -/// Start the broker process and monitor it. If it crashes, we stop our execution. -pub async fn start_supervised_process( - sender: Sender<()>, - command: String, -) -> color_eyre::Result<()> { - let mut child = Command::new("/bin/sh") - .args(["-c", &command]) - .stdout(std::process::Stdio::piped()) - .stderr(std::process::Stdio::piped()) - .spawn() - .expect("failed to execute process"); - debug_assert!(child.id().is_some()); - debug!("Started broker process"); - // No broker should take longer than 2 seconds to start. But we could make this configurable. - sleep(tokio::time::Duration::from_secs(5)).await; - // Buffers for stdout and stderr - let mut stdout_reader = BufReader::new(child.stdout.take().unwrap()).lines(); - let mut stderr_reader = BufReader::new(child.stderr.take().unwrap()).lines(); - // For handling crtlc - let (tx, mut rx) = channel(); - tokio::spawn(async move { - signal::ctrl_c().await.unwrap(); - info!("Crtl C received, stopping..."); - tx.send(()).expect("Could not send to crtlc_receiver"); - }); - tokio::spawn(async move { - let mut last_stdout: String = String::new(); - let mut last_stderr: String = String::new(); - loop { - if let Ok(Ok(Some(new_stdout))) = - timeout(Duration::from_millis(100), stdout_reader.next_line()).await - { - last_stdout.push_str(new_stdout.as_str()); - } - if let Ok(Ok(Some(new_stderr))) = - timeout(Duration::from_millis(100), stderr_reader.next_line()).await - { - last_stderr.push_str(new_stderr.as_str()); - } - let status = child.try_wait(); - if let Ok(Some(status)) = status { - sender.send(()).unwrap(); - info!("Broker process exited with status: {}", status); - info!("Stdout: {:?}", last_stdout); - info!("Stderr: {:?}", last_stderr); - break; - } else if rx.try_recv().is_ok() { - child.kill().await.unwrap(); - sender.send(()).unwrap(); - break; - } - } - }); - Ok(()) -} diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs deleted file mode 100644 index 6fcad9e..0000000 --- a/src/runtime/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::markov::StateMachine; -use crate::network::connect_to_broker; -use crate::packets::PacketQueue; -use crate::SeedAndIterations; -use rand::{Rng, SeedableRng}; -use rand_xoshiro::Xoshiro256PlusPlus; -use std::sync::Arc; -use std::time::Duration; -use tokio::sync::broadcast::Receiver; -use tokio::sync::mpsc::Receiver as MpscReceiver; -use tokio::sync::mpsc::Sender; -use tokio::sync::RwLock; -use tokio::time::sleep; -use tokio::{fs, task}; -use tracing::*; - -// TODO: Change address to allow other kinds of Streams -/// Runs a task that connects to the broker and fuzzes it -pub(crate) async fn run_thread( - seed: u64, - receiver_clone: Receiver<()>, - address: String, - iterations: u64, - packet_queue: Arc>, - it_sender_clone: Sender, -) { - let task_handle = task::spawn(async move { - let mut last_packets = Vec::new(); - let mut counter: u64 = 0; - let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); - while counter < iterations { - let new_stream = connect_to_broker(&address.clone()).await; - if new_stream.is_err() { - // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request - error!( - "Error connecting to broker: {:?}. See recommendations", - new_stream - ); - if !receiver_clone.is_empty() { - break; - } - // So we'll just have a "back-off" sleep here - sleep(Duration::from_millis(100)).await; - continue; - } - let new_tcpstream = new_stream.unwrap(); - let mut state_machine = StateMachine::new(new_tcpstream); - let mode = rng.gen(); - state_machine.execute(mode, &mut rng, &packet_queue).await; - last_packets = state_machine.previous_packets.clone(); - // We receive a message once the broker is stopped - // TODO: Also save last packets upon crash - if !receiver_clone.is_empty() { - break; - } - counter += 1; - if counter % 5000 == 0 { - // Display iterations per second - let _ = it_sender_clone.send(counter).await; - } - } - if iterations == u64::MAX { - // If the fuzzing is stopped we dump the packets - let serialized = toml::to_string(&SeedAndIterations { - seed: seed.to_string(), - iterations: counter.to_string(), - }) - .unwrap(); - let res = fs::write(format!("threads/fuzzing_{}.txt", seed), serialized).await; - - // TODO: Handle some errors - if res.is_err() { - error!("Error dumping packets: {:?}", res); - } - } - // Dump the packet we crashed on - let _ = fs::create_dir("crashes").await; - let _ = fs::write( - format!("crashes/crash_{}.txt", seed), - format!("{:?}", last_packets), - ) - .await; - - info!("Thread {seed} finished at {counter} iterations, when {iterations} were the target!"); - }); - let _ = task_handle.await; -} - -pub async fn iterations_tracker(threads: usize, mut it_receiver: MpscReceiver) { - let mut last_iterations = 0; - loop { - let start = std::time::Instant::now(); - let mut iteration_buffer = vec![0; threads]; - for i in 1..threads { - let value = it_receiver.recv().await; - match value { - Some(v) => iteration_buffer[i] = v, - None => break, - } - } - let sum: u64 = iteration_buffer.iter().sum(); - let elapsed = start.elapsed().as_millis(); - let it_per_second = (sum.saturating_sub(last_iterations)) as f64 / elapsed as f64 * 1000f64; - info!("{} it/s", it_per_second); - last_iterations = sum; - } -} From 8ddf068da7d42bd5400a56d657585ee6fdaaa6b9 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:31:21 +0200 Subject: [PATCH 10/17] feat: Added benchmark --- .github/workflows/ci.yml | 6 +- .gitignore | 2 + lib/.gitignore | 6 + lib/Cargo.toml | 58 +++++++ lib/benches/markov_models.rs | 37 +++++ lib/src/lib.rs | 58 +++++++ lib/src/markov/mod.rs | 292 +++++++++++++++++++++++++++++++++ lib/src/markov/mutations.rs | 118 +++++++++++++ lib/src/mqtt/mod.rs | 207 +++++++++++++++++++++++ lib/src/network.rs | 109 ++++++++++++ lib/src/packet_pool.rs | 85 ++++++++++ lib/src/packets.rs | 66 ++++++++ lib/src/process_monitor/mod.rs | 68 ++++++++ lib/src/runtime/mod.rs | 106 ++++++++++++ src/main.rs | 153 +++++++++++++++++ 15 files changed, 1368 insertions(+), 3 deletions(-) create mode 100644 lib/.gitignore create mode 100644 lib/Cargo.toml create mode 100644 lib/benches/markov_models.rs create mode 100644 lib/src/lib.rs create mode 100644 lib/src/markov/mod.rs create mode 100644 lib/src/markov/mutations.rs create mode 100644 lib/src/mqtt/mod.rs create mode 100644 lib/src/network.rs create mode 100644 lib/src/packet_pool.rs create mode 100644 lib/src/packets.rs create mode 100644 lib/src/process_monitor/mod.rs create mode 100644 lib/src/runtime/mod.rs create mode 100644 src/main.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9925da6..d08c3b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: components: rustfmt - name: Run cargo fmt --all -- --check - run: cargo fmt --all -- --check + run: cd lib && cargo fmt --all -- --check clippy: runs-on: ubuntu-20.04 @@ -79,5 +79,5 @@ jobs: override: true components: clippy - - name: Run cargo clippy --package rusty-FUME --all-targets - run: cargo clippy --package rusty-fume --all-targets + - name: Run cargo clippy --package lib --all-targets + run: cargo clippy --package lib --all-targets diff --git a/.gitignore b/.gitignore index 991239a..889353d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ Cargo.lock .vscode *.log .env +crashes +threads \ No newline at end of file diff --git a/lib/.gitignore b/lib/.gitignore new file mode 100644 index 0000000..991239a --- /dev/null +++ b/lib/.gitignore @@ -0,0 +1,6 @@ +/target +Cargo.lock +.idea +.vscode +*.log +.env diff --git a/lib/Cargo.toml b/lib/Cargo.toml new file mode 100644 index 0000000..b02ed47 --- /dev/null +++ b/lib/Cargo.toml @@ -0,0 +1,58 @@ +[package] +name = "lib" +version = "0.8.0" +edition = "2021" + + +[dependencies] +# Convenient error handling +color-eyre = "0.6.2" +# Logging +tracing = "0.1.37" +tracing-subscriber = "0.3.17" +# Futures +tokio = { version = "1.32.0", features = ["full"] } +futures = "0.3.28" +# Hex en/decoding +hex = "0.4.3" +# MQTT Packet generation +mqtt-protocol = "0.11.2" +# Random number generation with xoshiro for faster PRNG +rand = "0.8.5" +rand_xoshiro = "0.6.0" +# Command line interface +clap = { version = "4.3.24", features = ["derive"] } +# For serialization +serde = { version = "1.0.186", features = ["derive"] } +toml = "0.7.6" +# For serialization of raw bytes +serde_with = {version="3.1.0", features = ["hex"]} + +# Tokio Console Support +console-subscriber = "0.1.10" +# For Websocket support +tokio-tungstenite = "0.20.0" +# For TLS support +tokio-rustls = { version="0.24.1", features = ["dangerous_configuration"], optional = true } +rustls = { version="0.21.6", features = ["dangerous_configuration"], optional = true } + + + +[dev-dependencies] +criterion = { version = "0.5.1", features=["html_reports", "async_tokio"]} + +[[bench]] +name = "markov_models" +harness = false + +[profile.release] +debug = true +codegen-units = 1 + +[features] +default = ["tcp"] +tcp = [] +# TODO: Add quic, ws support +quic = [] +websocket = [] +tls = ["dep:tokio-rustls", "dep:rustls"] diff --git a/lib/benches/markov_models.rs b/lib/benches/markov_models.rs new file mode 100644 index 0000000..559d93f --- /dev/null +++ b/lib/benches/markov_models.rs @@ -0,0 +1,37 @@ +use criterion::BenchmarkId; +use criterion::Criterion; +use criterion::{criterion_group, criterion_main}; +use rand::SeedableRng; +use rand_xoshiro::Xoshiro256PlusPlus; +use std::sync::Arc; +use tokio::io::duplex; +// This is a struct that tells Criterion.rs to use the "futures" crate's current-thread executor +use lib::markov::{Mode, StateMachine}; +use lib::packets::PacketQueue; +use tokio::runtime::Runtime; +use tokio::sync::RwLock; + +async fn exec_markov_fast(m: Mode) { + let stream = duplex(10 * 1024).0; + let mut machine = StateMachine::new(stream); + let mut rng = Xoshiro256PlusPlus::from_seed([5; 32]); + machine + .execute(m, &mut rng, &Arc::new(RwLock::new(PacketQueue::default()))) + .await; +} + +fn markov_model_execution(c: &mut Criterion) { + for mode in [Mode::MutationGuided, Mode::GenerationGuided] { + c.bench_with_input( + BenchmarkId::new("execute_markov_model", mode), + &mode, + |b, &m| { + b.to_async(Runtime::new().unwrap()) + .iter(|| exec_markov_fast(m)); + }, + ); + } +} + +criterion_group!(benches, markov_model_execution); +criterion_main!(benches); diff --git a/lib/src/lib.rs b/lib/src/lib.rs new file mode 100644 index 0000000..a248bcb --- /dev/null +++ b/lib/src/lib.rs @@ -0,0 +1,58 @@ +//! # rusty-FUME +//! rusty-FUME is a fuzzer for the MQTT protocol. It is based on [FUME-Fuzzing-MQTT Brokers](https://github.com/PBearson/FUME-Fuzzing-MQTT-Brokers) +//! and uses markov chains to generate new packet chains. If it discovers a new response behaviour the chain is added to the fuzzing queue. +//! We use [tokio](https://tokio.rs/) for async networking. +//! ## The state machine +//! We implement a State machine with a markov chain. All probabilities are configurable for this process(except the ones with only one option). +//! The state machine is defined as follows for the Mutation Guided Fuzzing: +//! - S0: Initial State: Either goto CONNECT state or select a packet from the queue and go to MUTATION state +//! - CONNECT: Add connect to the current chain and go to ADDING State +//! - ADDING: Either add a new packet(configurable probability for each one) to the chain or go to MUTATION state +//! - MUTATION: Mutate, delete, inject or SEND the current chain +//! - SEND: Send the current chain and either go to Sf or MUTATION state +//! - Sf: Final State +//! And this way for Generation Guided Fuzzing: +//! - S0: Initial State: Goto ADD(CONNECT) state +//! - CONNECT: Add connect to the current chain and go to S1 +//! - S1: Either add a new packet or go to S2 +//! - S2: Inject/Delete/Mutate the current chain or go to SEND +//! - SEND: Send the current chain and either go to Sf or S2 +//! Once they get to S2 they behave the same way. +use crate::markov::MAX_PACKETS; +use crate::packets::PacketQueue; +use clap::{Parser, Subcommand}; +use rand::{thread_rng, Rng}; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +pub mod markov; +pub mod mqtt; +pub mod network; +mod packet_pool; +pub mod packets; +pub mod process_monitor; +pub mod runtime; +// TODO: Clean up main +// TODO: Try fuzzing a basic mongoose server? +// TODO: Fuzz mosquitto compiled with sanitizers +// TODO: Lib-split to allow benchmarking + +/// Struct to serialize threads once they are done(aka the broker has crashed). +#[derive(Serialize, Deserialize, Debug)] +pub struct SeedAndIterations { + pub seed: String, + pub iterations: String, +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::packets::Packets; + #[test] + fn test_serialize_packet_queue() { + let mut packet_queue = PacketQueue::default(); + packet_queue.inner.insert(vec![0x10], Packets::default()); + let serialized = toml::to_string(&packet_queue).unwrap(); + println!("{}", serialized); + } +} diff --git a/lib/src/markov/mod.rs b/lib/src/markov/mod.rs new file mode 100644 index 0000000..c17637e --- /dev/null +++ b/lib/src/markov/mod.rs @@ -0,0 +1,292 @@ +//! ## The state machine +//! We implement a State machine with a markov chain. All probabilities are configurable for this process(except the ones with only one option). +//! The state machine is defined as follows for the Mutation Guided Fuzzing: +//! - S0: Initial State: Either goto CONNECT state or select a packet from the queue and go to MUTATION state +//! - CONNECT: Add connect to the current chain and go to ADDING State +//! - ADDING: Either add a new packet(configurable probability for each one) to the chain or go to MUTATION state +//! - MUTATION: Mutate, delete, inject or SEND the current chain +//! - SEND: Send the current chain and either go to Sf or MUTATION state +//! - Sf: Final State +//! And this way for Generation Guided Fuzzing: +//! - S0: Initial State: Goto ADD(CONNECT) state +//! - CONNECT: Add connect to the current chain and go to S1 +//! - S1: Either add a new packet or go to S2 +//! - S2: Inject/Delete/Mutate the current chain or go to SEND +//! - SEND: Send the current chain and either go to Sf or S2 +//! Once they get to S2 they behave the same way. +mod mutations; + +use crate::markov::mutations::{delete, inject, swap, InjectType}; +use crate::markov::Mode::{GenerationGuided, MutationGuided}; +use crate::mqtt::{ + generate_connect_packet, generate_disconnect_packet, generate_pingreq_packet, + generate_publish_packet, generate_subscribe_packet, generate_unsubscribe_packet, send_packets, + SendError, +}; +use crate::packets::{PacketQueue, Packets}; +use rand::distributions::Standard; +use rand::prelude::Distribution; +use rand::Rng; +use rand_xoshiro::Xoshiro256PlusPlus; +use std::default::Default; +use std::fmt::{Debug, Display}; +use std::sync::Arc; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::sync::RwLock; +use tracing::*; + +const SEL_FROM_QUEUE: f32 = 0.7; +const PACKET_APPEND_CHANCE: f32 = 0.2; +const SEND_CHANCE: f32 = 0.2; +const BOF_CHANCE: f32 = 0.2; +const MUT_AFTER_SEND: f32 = 0.7; +pub const MAX_PACKETS: usize = 10; + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub enum State { + #[default] + S0, + ADD(PacketType), + ADDING, + SelectFromQueue, + MUTATION, + Mutate(Mutations), + SEND, + Sf, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum Mutations { + // Inserts bytes into the payload + Inject(InjectType), + // Deletes bytes from the payload + Delete, + // Changes bytes in the payload + Swap, +} +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Mutations { + match rng.gen_range(0..3) { + 0 => Mutations::Inject(rng.gen()), + 1 => Mutations::Delete, + 2 => Mutations::Swap, + _ => unreachable!(), + } + } +} + +pub trait ByteStream: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} + +impl ByteStream for T where T: AsyncReadExt + AsyncWriteExt + Unpin + Debug + Send {} +pub struct StateMachine +where + B: ByteStream, +{ + // The current state of the state machine + pub(crate) state: State, + // The current packet in bytes + packets: Packets, + // Previous packets. Useful for dumping the packets to disk(For tracking errors) + pub previous_packets: Vec, + // The current stream, TlsStream TcpStream or WebsocketStream + stream: B, +} +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub enum Mode { + MutationGuided, + GenerationGuided, +} + +impl Display for Mode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + MutationGuided => write!(f, "Mutation Guided"), + GenerationGuided => write!(f, "Generation Guided"), + } + } +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Mode { + match rng.gen_range(0..10) { + 0..=4 => MutationGuided, + 5..=9 => GenerationGuided, + _ => unreachable!(), + } + } +} +impl StateMachine +where + B: ByteStream, +{ + pub fn new(stream: B) -> Self { + Self { + stream, + state: Default::default(), + packets: Packets::new(), + previous_packets: Vec::new(), + } + } + pub async fn execute( + &mut self, + mode: Mode, + rng: &mut Xoshiro256PlusPlus, + packet_queue: &Arc>, + ) { + while self.state != State::Sf { + self.next(mode, rng, packet_queue).await; + trace!("State: {:?}", self.state); + } + } + async fn next( + &mut self, + mode: Mode, + rng: &mut Xoshiro256PlusPlus, + packet_queue: &Arc>, + ) { + match &self.state { + State::S0 => match mode { + MutationGuided => { + if rng.gen_range(0f32..1f32) < SEL_FROM_QUEUE && !self.packets.is_full() { + self.state = State::ADD(PacketType::CONNECT); + } else { + self.state = State::SelectFromQueue; + } + } + GenerationGuided => { + self.state = State::ADD(PacketType::CONNECT); + } + }, + State::SelectFromQueue => { + // Maybe we should use a priority queue in-memory here instead of storing on disk(overhead). Should be measured in the future. + if packet_queue.read().await.inner.is_empty() { + self.state = State::ADD(PacketType::CONNECT); + } else { + let packet_queue_read = packet_queue.read().await; + let packet_index = rng.gen_range(0..packet_queue_read.inner.len()); + let packet = packet_queue_read.inner.iter().nth(packet_index).unwrap(); + // TODO: Really? + self.packets = packet.1.clone().clone(); + self.state = State::MUTATION; + } + } + State::ADD(packet_type) => { + match packet_type { + PacketType::CONNECT => { + self.packets.append(&generate_connect_packet()); + } + PacketType::PUBLISH => { + self.packets.append(&generate_publish_packet()); + } + PacketType::SUBSCRIBE => { + self.packets.append(&generate_subscribe_packet()); + } + PacketType::UNSUBSCRIBE => { + self.packets.append(&generate_unsubscribe_packet()); + } + PacketType::PINGREQ => { + self.packets.append(&generate_pingreq_packet()); + } + PacketType::DISCONNECT => { + self.packets.append(&generate_disconnect_packet()); + } + _ => unreachable!(), + } + self.state = State::ADDING + } + State::ADDING => { + if rng.gen_range(0f32..1f32) < PACKET_APPEND_CHANCE { + self.state = State::ADD(rng.gen()); + } else { + self.state = State::MUTATION; + } + } + State::MUTATION => { + if rng.gen_range(0f32..1f32) < SEND_CHANCE { + self.state = State::SEND; + } else { + self.state = State::Mutate(rng.gen()); + } + } + State::Mutate(mutation) => { + match mutation { + Mutations::Inject(t) => { + inject(&mut self.packets, rng, t); + } + Mutations::Delete => { + delete(&mut self.packets, rng); + } + Mutations::Swap => { + swap(&mut self.packets, rng); + } + } + self.state = State::MUTATION; + } + State::SEND => { + self.previous_packets.push(self.packets.clone()); + let res = send_packets(&mut self.stream, &self.packets, packet_queue).await; + if let Err(e) = res { + match e { + SendError::Timeout => {} + SendError::ReceiveErr => { + trace!("Receive error, probably disconnected by the broker...") + } + SendError::SendErr => { + trace!("Send error, probably disconnected by the broker...") + } + } + } else { + trace!("Sent packet successfully"); + } + if rng.gen_range(0f32..1f32) > MUT_AFTER_SEND || res.is_err() { + self.state = State::Sf; + } else { + self.state = State::Mutate(rng.gen()); + } + } + _ => todo!(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum BOF { + BOF, + NOBOF, +} + +/// The MQTT Packet types +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum PacketType { + AUTH, + CONNACK, + CONNECT, + DISCONNECT, + PINGREQ, + PINGRESP, + PUBACK, + PUBCOMP, + PUBLISH, + PUBREC, + PUBREL, + RESERVED, + SUBACK, + SUBSCRIBE, + UNSUBACK, + UNSUBSCRIBE, +} + +// Implement a distribution for the packet types +impl Distribution for rand::distributions::Standard { + fn sample(&self, rng: &mut R) -> PacketType { + match rng.gen_range(0..6) { + 0 => PacketType::CONNECT, + 1 => PacketType::PUBLISH, + 2 => PacketType::SUBSCRIBE, + 3 => PacketType::UNSUBSCRIBE, + 4 => PacketType::PINGREQ, + 5 => PacketType::DISCONNECT, + _ => unreachable!(), + } + } +} diff --git a/lib/src/markov/mutations.rs b/lib/src/markov/mutations.rs new file mode 100644 index 0000000..5dd6df6 --- /dev/null +++ b/lib/src/markov/mutations.rs @@ -0,0 +1,118 @@ +use crate::packets::Packets; +use rand::distributions::Standard; +use rand::prelude::Distribution; +use rand::Rng; +use rand_xoshiro::Xoshiro256PlusPlus; + +pub fn inject(packets: &mut Packets, rng: &mut Xoshiro256PlusPlus, inject_type: &InjectType) { + let packets_size = packets.size(); + debug_assert!(packets_size > 0, "Packet size should be greater than 0"); + let packet = packets + .inner + .get_mut(rng.gen_range(0..packets_size)) + .unwrap(); + match inject_type { + InjectType::Single => inject_single(packet, rng), + InjectType::BOF => inject_bof(packet, rng), + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum InjectType { + Single, + BOF, +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> InjectType { + match rng.gen_range(0..2) { + 0 => InjectType::Single, + 1 => InjectType::BOF, + _ => unreachable!(), + } + } +} + +fn inject_bof(packet: &mut Vec, rng: &mut Xoshiro256PlusPlus) { + let idx = rng.gen_range(0..packet.len()); + // To fight big packets + let byte_length = 350 / packet.len(); + let mut bytes = vec![0; byte_length]; + rng.fill(&mut bytes[..]); + packet.splice(idx..idx, bytes); +} + +fn inject_single(packet: &mut Vec, rng: &mut Xoshiro256PlusPlus) { + let idx = rng.gen_range(0..packet.len()); + let byte = rng.gen::(); + packet.insert(idx, byte); +} + +pub fn delete(packets: &mut Packets, rng: &mut Xoshiro256PlusPlus) { + let packets_size = packets.size(); + let packet = packets + .inner + .get_mut(rng.gen_range(0..packets_size)) + .unwrap(); + let idx = rng.gen_range(0..packet.len()); + packet.remove(idx); +} + +pub fn swap(packets: &mut Packets, rng: &mut Xoshiro256PlusPlus) { + let packets_size = packets.size(); + let packet = packets + .inner + .get_mut(rng.gen_range(0..packets_size)) + .unwrap(); + let idx = rng.gen_range(0..packet.len()); + let byte = rng.gen::(); + packet[idx] = byte; +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::prelude::thread_rng; + /* + #[test] + fn test_inject_bof() { + let mut rng = thread_rng(); + let mut packet = vec![0; 10]; + println!("Input packet: {:?}", packet); + inject_bof(&mut packet, &mut rng); + println!("Output packet: {:?}", packet); + assert!(packet.len() > 10); + assert!(packet.len() < 30); + } + + #[test] + fn test_inject_single() { + let mut rng = thread_rng(); + let mut packet = vec![0; 10]; + println!("Input packet: {:?}", packet); + inject_single(&mut packet, &mut rng); + println!("Output packet: {:?}", packet); + assert_eq!(packet.len(), 11); + } + + #[test] + fn test_swap() { + let mut rng = thread_rng(); + let mut packet = vec![0; 10]; + println!("Input packet: {:?}", packet); + swap(&mut packet, &mut rng); + println!("Output packet: {:?}", packet); + assert_eq!(packet.len(), 10); + } + + #[test] + fn test_delete() { + let mut rng = thread_rng(); + let mut packet = vec![0; 10]; + println!("Input packet: {:?}", packet); + delete(&mut packet, &mut rng); + println!("Output packet: {:?}", packet); + assert!(packet.len() < 10); + assert!(packet.len() > 0); + }*/ +} diff --git a/lib/src/mqtt/mod.rs b/lib/src/mqtt/mod.rs new file mode 100644 index 0000000..52aba16 --- /dev/null +++ b/lib/src/mqtt/mod.rs @@ -0,0 +1,207 @@ +use crate::markov::ByteStream; +use crate::packets::{PacketQueue, Packets}; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::RwLock; +use tokio::time::timeout; +use tracing::{debug, info, trace}; + +pub(crate) fn generate_connect_packet() -> [u8; 62] { + [ + 16, 60, 0, 4, 77, 81, 84, 84, 4, 4, 0, 0, 0, 17, 72, 101, 108, 108, 111, 32, 77, 81, 84, + 84, 32, 66, 114, 111, 107, 101, 114, 0, 5, 116, 111, 112, 105, 99, 0, 22, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 72, 255, 50, 0, 0, 0, + ] +} + +pub(crate) fn generate_publish_packet() -> [u8; 27] { + [ + 49, 25, 0, 5, 116, 111, 112, 105, 99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 72, 255, 50, + 0, 0, 0, + ] +} + +pub(crate) fn generate_subscribe_packet() -> [u8; 12] { + [130, 10, 0, 100, 0, 5, 116, 111, 112, 105, 99, 0] +} + +pub(crate) fn generate_unsubscribe_packet() -> [u8; 11] { + [162, 9, 0, 10, 0, 5, 116, 111, 112, 105, 99] +} + +pub(crate) fn generate_disconnect_packet() -> [u8; 2] { + [224, 0] +} + +pub(crate) fn generate_pingreq_packet() -> [u8; 2] { + [192, 0] +} + +pub async fn test_connection(stream: &mut impl ByteStream) -> color_eyre::Result<()> { + stream + .write_all(generate_connect_packet().as_slice()) + .await?; + let mut buf = [0; 1024]; + let _ = timeout(Duration::from_secs(1), stream.read(&mut buf)).await; + debug!("Received Packet hex encoded: {:?}", hex::encode(buf)); + Ok(()) +} +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) enum SendError { + // Probably a DOS discovered. The server didn't respond in time + Timeout, + // The server crashed. As the connection is closed or similar + ReceiveErr, + // We couldn't send the packet. A previous packet might have crashed the server + SendErr, +} + +pub(crate) async fn send_packets( + stream: &mut impl ByteStream, + packets: &Packets, + packet_queue: &Arc>, +) -> Result<(), SendError> { + for packet in packets.inner.iter().filter(|p| !p.is_empty()) { + send_packet(stream, packet.as_slice(), packets, packet_queue).await?; + } + Ok(()) +} + +const PACKET_TIMEOUT: u64 = 30; + +pub(crate) async fn send_packet( + stream: &mut impl ByteStream, + packet: &[u8], + packets: &Packets, + packet_queue: &Arc>, +) -> Result<(), SendError> { + let write_result = timeout( + Duration::from_millis(PACKET_TIMEOUT), + stream.write_all(packet), + ) + .await; + match write_result { + Ok(Ok(_)) => (), + Err(t) => { + trace!("Timeout: {:?}", t); + } + Ok(Err(e)) => { + trace!("Send error: {:?}", e); + return Err(SendError::SendErr); + } + } + let mut buf = [0; 1024]; + let res = timeout(Duration::from_millis(PACKET_TIMEOUT), stream.read(&mut buf)).await; + match res { + Ok(Ok(p)) => { + known_packet(&buf[..p], packets, packet_queue).await; + Ok(()) + } + Err(t) => { + trace!("Timeout: {:?}", t); + // TODO: Retry sending the packet + Err(SendError::Timeout) + } + Ok(Err(e)) => { + trace!("Receive error: {:?}", e); + Err(SendError::ReceiveErr) + } + } +} + +/// This works by using the response packet as the key in a hashmap. If the packet is already in the hashmap we know that we have seen it before +async fn known_packet( + response_packet: &[u8], + input_packet: &Packets, + packet_queue: &Arc>, +) -> bool { + // TODO: decode the packet and extract user id, payload, topic etc. because those don't matter to see if it is a known packet + let queue_lock = packet_queue.read().await; + let response_packet = response_packet.to_vec(); + if !queue_lock.inner.contains_key(&response_packet) { + info!("New behavior discovered, adding it to the queue",); + debug!("Response packet: {:?}", response_packet); + drop(queue_lock); + let mut queue_lock = packet_queue.write().await; + queue_lock + .inner + .insert(response_packet, input_packet.clone()); + } + trace!( + "Known behavior. We have {} known behaviors", + packet_queue.read().await.inner.len() + ); + false +} +#[cfg(test)] +mod tests { + use super::*; + use mqtt::packet::QoSWithPacketIdentifier; + use mqtt::{Encodable, TopicFilter, TopicName}; + // To not generate these packets over and over again during execution of the markov model, we generate them here and then use them in the functions + #[test] + fn generate_connect_packet() { + let mut connect = mqtt::packet::ConnectPacket::new("Hello MQTT Broker"); + connect.set_will(Some(( + TopicName::new("topic").unwrap(), + vec![ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 72, 255, 50, 0, 0, 0, + ], + ))); + let mut packet = Vec::new(); + connect.encode(&mut packet).unwrap(); + println!("{packet:?}"); + } + + #[test] + fn generate_publish_packet() { + let mut publish = mqtt::packet::PublishPacket::new( + TopicName::new("topic").unwrap(), + QoSWithPacketIdentifier::Level0, + vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 72, 255, 50, 0, 0, 0], + ); + publish.set_retain(true); + let mut packet = Vec::new(); + publish.encode(&mut packet).unwrap(); + println!("{packet:?}"); + } + + #[test] + fn generate_subscribe_packet() { + let subscribe = mqtt::packet::SubscribePacket::new( + 100, + vec![( + TopicFilter::new("topic").unwrap(), + mqtt::QualityOfService::Level0, + )], + ); + let mut packet = Vec::new(); + subscribe.encode(&mut packet).unwrap(); + println!("{packet:?}"); + } + + #[test] + fn generate_unsubscribe_packet() { + let unsubscribe = + mqtt::packet::UnsubscribePacket::new(10, vec![TopicFilter::new("topic").unwrap()]); + let mut packet = Vec::new(); + unsubscribe.encode(&mut packet).unwrap(); + println!("{packet:?}"); + } + + #[test] + fn generate_disconnect_packet() { + let disconnect = mqtt::packet::DisconnectPacket::new(); + let mut packet = Vec::new(); + disconnect.encode(&mut packet).unwrap(); + println!("{packet:?}"); + } + + #[test] + fn generate_pingreq_packet() { + let pingreq = mqtt::packet::PingreqPacket::new(); + let mut packet = Vec::new(); + pingreq.encode(&mut packet).unwrap(); + println!("{packet:?}"); + } +} diff --git a/lib/src/network.rs b/lib/src/network.rs new file mode 100644 index 0000000..d1e6e0b --- /dev/null +++ b/lib/src/network.rs @@ -0,0 +1,109 @@ +use color_eyre::Result; +// Avoid double protocols +// We have a lot of features which are mutually exclusive, so we need compile time errors if someone tries to use them together. +// These are the features: tcp, websocket, tls, quic +#[cfg(all( + feature = "tcp", + any(feature = "websocket", feature = "tls", feature = "quic") +))] +compile_error!( + "You can only use one protocol at a time. Please disable tcp with --no-default-features" +); +#[cfg(all(feature = "websocket", any(feature = "tls", feature = "quic")))] +compile_error!( + "You can only use one protocol at a time. Please disable websocket with --no-default-features" +); +#[cfg(all(feature = "tls", feature = "quic"))] +compile_error!( + "You can only use one protocol at a time. Please disable tls with --no-default-features" +); + +#[cfg(feature = "tcp")] +pub use tcp::*; +#[cfg(feature = "tcp")] +mod tcp { + use super::*; + use tokio::net::{TcpStream, ToSocketAddrs}; + + pub async fn connect_to_broker(address: impl ToSocketAddrs) -> Result { + let tcpstream = TcpStream::connect(address).await?; + Ok(tcpstream) + } +} + +#[cfg(feature = "websocket")] +pub use websocket::*; + +#[cfg(feature = "websocket")] +mod websocket { + compile_error!("Websocket is not implemented yet"); + /* + use super::*; + use tokio::net::TcpStream; + use tokio_tungstenite::tungstenite::client::IntoClientRequest; + use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; + + pub async fn connect_to_broker( + address: impl IntoClientRequest + Unpin, + ) -> Result>> { + let (ws_stream, _) = tokio_tungstenite::connect_async(address).await?; + Ok(ws_stream) + } + */ +} + +#[cfg(feature = "tls")] +pub use tls::*; + +#[cfg(feature = "tls")] +mod tls { + use super::*; + use crate::markov::ByteStream; + use futures::FutureExt; + use rustls::client::{ServerCertVerified, ServerCertVerifier}; + use rustls::{Certificate, ClientConfig, ClientConnection, Error, ServerName}; + use std::net::IpAddr; + use std::str::FromStr; + use std::sync::Arc; + use std::time::SystemTime; + use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use tokio::net::{TcpStream, ToSocketAddrs}; + use tokio_rustls::client::TlsStream; + use tokio_rustls::TlsConnector; + + struct NoCertificateVerifier; + + impl ServerCertVerifier for NoCertificateVerifier { + fn verify_server_cert( + &self, + end_entity: &Certificate, + intermediates: &[Certificate], + server_name: &ServerName, + scts: &mut dyn Iterator, + ocsp_response: &[u8], + now: SystemTime, + ) -> std::result::Result { + Ok(ServerCertVerified::assertion()) + } + } + /// Connects to the broker using TLS ignoring the server certificate since that would just be + /// wasted iterations + pub async fn connect_to_broker(address: &str) -> Result> { + let mut socket = TcpStream::connect(address).await?; + let mut config = ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(NoCertificateVerifier)) + .with_no_client_auth(); + let connector = TlsConnector::from(Arc::new(config)); + let stream = connector + .connect( + // Trim everything including the port beginning ':' + ServerName::IpAddress( + IpAddr::from_str(address.split(':').next().unwrap()).unwrap(), + ), + socket, + ) + .await?; + Ok(stream) + } +} diff --git a/lib/src/packet_pool.rs b/lib/src/packet_pool.rs new file mode 100644 index 0000000..5b73b2f --- /dev/null +++ b/lib/src/packet_pool.rs @@ -0,0 +1,85 @@ +//! Here are packets from previously discovered CVEs + +use crate::MAX_PACKETS; +/// https://www.cvedetails.com/cve/CVE-2021-34432/ +#[allow(unused)] +const CVE_2021_34432: &[&[u8]; MAX_PACKETS] = &[ + &[ + 16, 60, 0, 4, 77, 81, 84, 84, 4, 4, 0, 0, 0, 17, 72, 101, 108, 108, 111, 32, 77, 81, 84, + 84, 32, 66, 114, 111, 107, 101, 114, 0, 5, 116, 111, 112, 105, 99, 0, 22, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 72, 255, 50, 0, 0, 0, + ], + &[48, 10, 0, 0, 116, 101, 115, 116, 116, 101, 115, 116], + &[224], + &[], + &[], + &[], + &[], + &[], + &[], + &[], +]; +#[allow(unused)] +const OTHER_MOSQUITTO_CVE: &[&[u8]; MAX_PACKETS] = &[ + &[ + 16, 96, 0, 4, 77, 81, 84, 84, 5, 192, 93, 85, 34, 21, 0, 15, 98, 99, 82, 85, 100, 109, 83, + 68, 89, 117, 119, 98, 86, 54, 50, 22, 0, 6, 72, 55, 70, 79, 120, 77, 23, 0, 25, 1, 34, 234, + 35, 0, 25, 118, 116, 78, 87, 72, 80, 101, 56, 48, 98, 52, 99, 86, 120, 102, 85, 107, 110, + 114, 116, 86, 89, 68, 122, 88, 0, 14, 86, 52, 86, 108, 79, 115, 54, 55, 73, 100, 84, 81, + 70, 68, 0, 6, 90, 48, 53, 99, 79, 57, 112, 4, 122, 230, 236, 0, 112, 24, 205, 229, 136, 20, + 38, 12, 49, 107, 51, 69, 111, 109, 83, 86, 119, 70, 114, 0, 3, 50, 75, 86, 64, 54, 206, + 187, 210, 50, 38, 0, 24, 57, 105, 111, 113, 118, 80, 115, 53, 68, 85, 111, 97, 56, 115, 79, + 43, 81, 54, 86, 103, 77, 49, 54, 112, 21, 100, 50, 84, 116, 114, 75, 66, 73, 116, 88, 103, + 106, 56, 97, 84, 84, 81, 89, 107, 81, 118, + ], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], + &[], +]; +mod tests { + use super::*; + use crate::packets::{PacketQueue, Packets}; + use std::fs::write; + + #[test] + fn nanomq_bug() { + let packet = hex::decode("106000044d51545405c05d552215000f62635255646d5344597577625636321600064837464f784d1700190122ea23001976744e574850653830623463567866556b6e72745659447a58000e5634566c4f73363749645451464400065a3035634f3970047ae6ec007018cde58814260c316b33456f6d53567746720003324b564036cebbd23226001839696f717650733544556f6138734f2b513656674d3136701564325474724b42497458676a3861545451596b5176"); + let packet = packet.unwrap(); + println!("Packet: {:?}", packet); + } + #[test] + fn serialize_packet_pool() { + let mut packet_pool = PacketQueue::default(); + let packets = Packets { + inner: CVE_2021_34432 + .iter() + .map(|x| x.to_vec()) + .collect::>>() + .try_into() + .unwrap(), + }; + packet_pool + .inner + .insert(CVE_2021_34432[1].to_vec(), packets); + let packets = Packets { + inner: OTHER_MOSQUITTO_CVE + .iter() + .map(|x| x.to_vec()) + .collect::>>() + .try_into() + .unwrap(), + }; + packet_pool + .inner + .insert(OTHER_MOSQUITTO_CVE[0].to_vec(), packets); + let serialized = toml::to_string(&packet_pool).unwrap(); + println!("Serialized: {}", serialized); + write("packet_pool.toml", serialized).unwrap(); + } +} diff --git a/lib/src/packets.rs b/lib/src/packets.rs new file mode 100644 index 0000000..adc39e0 --- /dev/null +++ b/lib/src/packets.rs @@ -0,0 +1,66 @@ +use crate::markov::MAX_PACKETS; +use serde::{Deserialize, Serialize}; +use serde_with::formats::CommaSeparator; +use serde_with::serde_as; +use serde_with::StringWithSeparator; +use std::cmp::min; +use std::collections::BTreeMap; +use std::fmt::Display; +use std::path::Path; +use tokio::fs::File; +use tokio::io::AsyncReadExt; +#[serde_as] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] +pub struct Packets { + #[serde_as(as = "[StringWithSeparator::; MAX_PACKETS]")] + pub(crate) inner: [Vec; MAX_PACKETS], +} +impl Display for Packets { + // Hex dump the packets + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = String::new(); + for i in 0..MAX_PACKETS { + if !self.inner[i].is_empty() { + s.push_str(hex::encode(&self.inner[i]).as_str()); + s.push('\n'); + } + } + write!(f, "{}", s) + } +} +impl Packets { + pub fn append(&mut self, packet: &[u8]) { + // Search the first free slot and insert it there + let size = self.size(); + if size < MAX_PACKETS { + self.inner[size] = packet.to_vec(); + } + } + pub fn is_full(&self) -> bool { + self.inner.iter().all(|x| !x.is_empty()) + } + pub fn size(&self) -> usize { + min(1, self.inner.iter().filter(|x| !x.is_empty()).count()) + } + pub fn new() -> Self { + Self { + inner: Default::default(), + } + } +} + +#[serde_as] +#[derive(Debug, PartialEq, Eq, Clone, Hash, Default, Serialize, Deserialize)] +pub struct PacketQueue { + #[serde_as(as = "BTreeMap, _>")] + pub(crate) inner: BTreeMap, Packets>, +} + +impl PacketQueue { + pub async fn read_from_file(path: impl AsRef) -> color_eyre::Result { + let mut content = String::new(); + File::open(path).await?.read_to_string(&mut content).await?; + let queue = toml::from_str(&content)?; + Ok(queue) + } +} diff --git a/lib/src/process_monitor/mod.rs b/lib/src/process_monitor/mod.rs new file mode 100644 index 0000000..d976210 --- /dev/null +++ b/lib/src/process_monitor/mod.rs @@ -0,0 +1,68 @@ +use std::time::Duration; +use tokio::io::{AsyncBufReadExt, BufReader}; +use tokio::process::Command; +use tokio::signal; +use tokio::sync::broadcast::Sender; +use tokio::sync::oneshot::channel; +use tokio::time::{sleep, timeout}; +use tracing::{debug, info}; + +// TODO: How do the tasks ask if the server has exited? And better yet, how do they get the message back? +// TODO: Also, how do the tasks know when it has caused new stdout/stderr output? +// TODO: Allow the user to specify where to write the stdout/stderr of the monitored process. Maybe gzip compress it? +// TODO: Ask threads what their last packets were and dump it. +/// Start the broker process and monitor it. If it crashes, we stop our execution. +pub async fn start_supervised_process( + sender: Sender<()>, + command: String, +) -> color_eyre::Result<()> { + let mut child = Command::new("/bin/sh") + .args(["-c", &command]) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .expect("failed to execute process"); + debug_assert!(child.id().is_some()); + debug!("Started broker process"); + // No broker should take longer than 2 seconds to start. But we could make this configurable. + sleep(tokio::time::Duration::from_secs(5)).await; + // Buffers for stdout and stderr + let mut stdout_reader = BufReader::new(child.stdout.take().unwrap()).lines(); + let mut stderr_reader = BufReader::new(child.stderr.take().unwrap()).lines(); + // For handling crtlc + let (tx, mut rx) = channel(); + tokio::spawn(async move { + signal::ctrl_c().await.unwrap(); + info!("Crtl C received, stopping..."); + tx.send(()).expect("Could not send to crtlc_receiver"); + }); + tokio::spawn(async move { + let mut last_stdout: String = String::new(); + let mut last_stderr: String = String::new(); + loop { + if let Ok(Ok(Some(new_stdout))) = + timeout(Duration::from_millis(100), stdout_reader.next_line()).await + { + last_stdout.push_str(new_stdout.as_str()); + } + if let Ok(Ok(Some(new_stderr))) = + timeout(Duration::from_millis(100), stderr_reader.next_line()).await + { + last_stderr.push_str(new_stderr.as_str()); + } + let status = child.try_wait(); + if let Ok(Some(status)) = status { + sender.send(()).unwrap(); + info!("Broker process exited with status: {}", status); + info!("Stdout: {:?}", last_stdout); + info!("Stderr: {:?}", last_stderr); + break; + } else if rx.try_recv().is_ok() { + child.kill().await.unwrap(); + sender.send(()).unwrap(); + break; + } + } + }); + Ok(()) +} diff --git a/lib/src/runtime/mod.rs b/lib/src/runtime/mod.rs new file mode 100644 index 0000000..e53964a --- /dev/null +++ b/lib/src/runtime/mod.rs @@ -0,0 +1,106 @@ +use crate::markov::StateMachine; +use crate::network::connect_to_broker; +use crate::packets::PacketQueue; +use crate::SeedAndIterations; +use rand::{Rng, SeedableRng}; +use rand_xoshiro::Xoshiro256PlusPlus; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::broadcast::Receiver; +use tokio::sync::mpsc::Receiver as MpscReceiver; +use tokio::sync::mpsc::Sender; +use tokio::sync::RwLock; +use tokio::time::sleep; +use tokio::{fs, task}; +use tracing::*; + +/// Runs a task that connects to the broker and fuzzes it +pub async fn run_thread( + seed: u64, + receiver_clone: Receiver<()>, + address: String, + iterations: u64, + packet_queue: Arc>, + it_sender_clone: Sender, +) { + let task_handle = task::spawn(async move { + let mut last_packets = Vec::new(); + let mut counter: u64 = 0; + let mut rng = Xoshiro256PlusPlus::seed_from_u64(seed); + while counter < iterations { + let new_stream = connect_to_broker(&address.clone()).await; + if new_stream.is_err() { + // Workaround for connections not being closed fast enough. See https://stackoverflow.com/questions/76238841/cant-assign-requested-address-in-request + error!( + "Error connecting to broker: {:?}. See recommendations", + new_stream + ); + if !receiver_clone.is_empty() { + break; + } + // So we'll just have a "back-off" sleep here + sleep(Duration::from_millis(100)).await; + continue; + } + let new_tcpstream = new_stream.unwrap(); + let mut state_machine = StateMachine::new(new_tcpstream); + let mode = rng.gen(); + state_machine.execute(mode, &mut rng, &packet_queue).await; + last_packets = state_machine.previous_packets.clone(); + // We receive a message once the broker is stopped + // TODO: Also save last packets upon crash + if !receiver_clone.is_empty() { + break; + } + counter += 1; + if counter % 5000 == 0 { + // Display iterations per second + let _ = it_sender_clone.send(counter).await; + } + } + if iterations == u64::MAX { + // If the fuzzing is stopped we dump the packets + let serialized = toml::to_string(&SeedAndIterations { + seed: seed.to_string(), + iterations: counter.to_string(), + }) + .unwrap(); + let res = fs::write(format!("threads/fuzzing_{}.txt", seed), serialized).await; + + // TODO: Handle some errors + if res.is_err() { + error!("Error dumping packets: {:?}", res); + } + } + // Dump the packet we crashed on + let _ = fs::create_dir("crashes").await; + let _ = fs::write( + format!("crashes/crash_{}.txt", seed), + format!("{:?}", last_packets), + ) + .await; + + info!("Thread {seed} finished at {counter} iterations, when {iterations} were the target!"); + }); + let _ = task_handle.await; +} + +pub async fn iterations_tracker(threads: usize, mut it_receiver: MpscReceiver) { + let mut last_iterations = 0; + loop { + let start = std::time::Instant::now(); + let mut iteration_buffer = vec![0; threads]; + for i in 1..threads { + let value = it_receiver.recv().await; + match value { + Some(v) => iteration_buffer[i] = v, + None => break, + } + } + let sum: u64 = iteration_buffer.iter().sum(); + let elapsed = start.elapsed().as_millis(); + let it_per_second = (sum.saturating_sub(last_iterations)) as f64 / elapsed as f64 * 1000f64; + info!("{} it/s", it_per_second); + last_iterations = sum; + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e05b96a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,153 @@ +use clap::{Parser, Subcommand}; +use futures::future::join_all; +use lib::mqtt::test_connection; +use lib::network::connect_to_broker; +use lib::packets::PacketQueue; +use lib::process_monitor::start_supervised_process; +use lib::runtime::{iterations_tracker, run_thread}; +use lib::SeedAndIterations; +use rand::{thread_rng, Rng}; +use std::str::FromStr; +use std::sync::Arc; +use tokio::sync::mpsc::channel as mpsc_channel; +use tokio::sync::RwLock; +use tokio::{fs, task}; +use tracing::{debug, info, trace}; + +#[derive(Parser, Debug)] +#[command(author, version, about)] +struct Cli { + #[command(subcommand)] + subcommand: SubCommands, + #[arg(short, long, default_value = "127.0.0.1:1883")] + target: String, + #[arg(short, long)] + broker_command: String, + // TODO: Make the timeout configurable + #[arg(long, default_value = "200")] + timeout: u64, +} + +#[derive(Subcommand, Debug)] +enum SubCommands { + // TODO: Do Fuzzing args like threads, chances etc + Fuzz { + #[arg(short, long, default_value_t = 100)] + threads: u64, + }, + Replay { + #[arg(short, long, default_value_t = false)] + sequential: bool, + }, +} +#[tokio::main] +async fn main() -> color_eyre::Result<()> { + console_subscriber::init(); + color_eyre::install()?; + let cli = Cli::parse(); + let packet_queue = Arc::new(RwLock::new( + PacketQueue::read_from_file("./packet_pool.toml").await?, + )); + match &cli.subcommand { + SubCommands::Fuzz { threads } => { + // The channel used for iteration counting + let (it_sender, it_receiver) = mpsc_channel::(*threads as usize); + // This receiver is necessary to dump the packets once the broker is stopped + let (sender, _) = tokio::sync::broadcast::channel(1); + let mut subscribers = vec![]; + for _ in 0..*threads { + subscribers.push(sender.subscribe()); + } + start_supervised_process(sender, cli.broker_command).await?; + let address = cli.target.clone(); + let mut stream = connect_to_broker(&cli.target).await?; + test_connection(&mut stream).await?; + info!("Connection established, starting fuzzing!"); + let mut rng = thread_rng(); + let _ = fs::create_dir("./threads").await; + let mut task_handles = vec![]; + for _ in 0u64..*threads { + let it_sender_clone = it_sender.clone(); + let receiver_clone = subscribers.pop().unwrap(); + let seed: u64 = rng.gen(); + task_handles.push(run_thread( + seed, + receiver_clone, + address.clone(), + u64::MAX, + packet_queue.clone(), + it_sender_clone, + )); + } + // Track it/s + let threads = *threads as usize; + task::spawn(async move { + iterations_tracker(threads, it_receiver).await; + }); + join_all(task_handles).await; + let serialized_pkg_pool = toml::to_string(&packet_queue.write().await.clone()); + trace!("Packet Queue: {:?}", serialized_pkg_pool); + } + SubCommands::Replay { sequential } => { + // TODO: When replaying the tasks may get loaded in different order + // Iterate through all fuzzing_{}.txt files and replay them one after another + let mut files = fs::read_dir("./threads") + .await + .expect("Failed to find threads folder. Cannot Replay"); + let mut filtered_files = vec![]; + trace!( + "Found files: {:?} in folder {:?}", + files, + std::env::current_dir() + ); + + while let Some(entry) = files.next_entry().await? { + let path = entry.path(); + let path_str = path.to_string_lossy().to_string(); + if path_str.starts_with("./threads/fuzzing_") && path_str.ends_with(".txt") { + filtered_files.push(path_str); + } + } + trace!("Found {} files", filtered_files.len()); + let (sender, receiver) = tokio::sync::broadcast::channel::<()>(1); + let mut subscribers = vec![]; + for _ in 0..filtered_files.len() { + subscribers.push(sender.subscribe()); + } + start_supervised_process(sender, cli.broker_command).await?; + let mut stream = connect_to_broker(&cli.target).await?; + test_connection(&mut stream).await?; + debug!("Connection established"); + debug!("Starting replay with {} seeds", filtered_files.len()); + let mut threads = vec![]; + let unused_it_channel = mpsc_channel::(filtered_files.len()).0; + for file in filtered_files { + let receiver_clone = subscribers.pop().unwrap(); + let seed_and_iterations: SeedAndIterations = + toml::from_str(&fs::read_to_string(file).await?)?; + threads.push(run_thread( + u64::from_str(&seed_and_iterations.seed).unwrap(), + receiver_clone, + cli.target.clone(), + u64::from_str(&seed_and_iterations.iterations).unwrap(), + packet_queue.clone(), + unused_it_channel.clone(), + )); + } + if *sequential { + for (index, thread) in threads.into_iter().enumerate() { + info!("Replaying thread number {}", index); + thread.await; + if !receiver.is_empty() { + info!("Crashing seed found!"); + return Ok(()); + } + } + info!("No crash found :/"); + } else { + join_all(threads).await; + } + } + } + Ok(()) +} From 3099ee52c795e9a945173165a1ddac2351bbdc95 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:42:06 +0200 Subject: [PATCH 11/17] feat: Added benchmark to CI --- .github/workflows/bench.yml | 64 +++++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 2 +- lib/Cargo.toml | 2 ++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/bench.yml diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml new file mode 100644 index 0000000..046e8dd --- /dev/null +++ b/.github/workflows/bench.yml @@ -0,0 +1,64 @@ +name: Benchmarks +## Adapted from this: https://github.com/infinyon/fluvio/blob/master/.github/workflows/benchmarks.yml +concurrency: + group: benchmark-${{ github.ref }} + cancel-in-progress: true + +on: + pull_request: + branches: + - main + - dev + push: + branches: + - main + - dev + +permissions: + contents: write + +jobs: + markov_model: + name: Markov Model Benchmarks + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Get Rust toolchain + uses: dtolnay/rust-toolchain@v1 + with: + toolchain: nightly + profile: minimal + + - name: Cache Rust Cargo files + uses: Swatinem/rust-cache@v2 + with: + # Additional non workspace directories, separated by newlines + key: benches-${{ runner.os }}-unit_fluvio_protocol-rust + + - name: Cache Benchmark data + uses: actions/cache@v3 + if: github.ref == 'refs/heads/master' + with: + path: ./benches_cache + key: benches-${{ runner.os }}-unit_fluvio_protocol + + - name: Run Benchmarks + run: cargo bench -- --output-format bencher | tee markov_model_bench.txt + + - name: Store benchmark result + uses: benchmark-action/github-action-benchmark@v1 + if: github.ref == 'refs/heads/master' + with: + # What benchmark tool the output.txt came from + tool: 'cargo' + # Where the output from the benchmark tool is stored + output-file-path: markov_model_bench.txt + # GitHub API token to make a commit comment + github-token: ${{ secrets.GITHUB_TOKEN }} + # Leave a job summary with benchmark result comparison + summary-always: true + # Where the previous data file is stored + external-data-json-path: ./benches_cache/benchmark-data.json + alert-comment-cc-users: '@Nereuxofficial' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d08c3b4..41b6a6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ on: env: CARGO_TERM_COLOR: always - +# TODO: Add benchmarking in CI(see https://github.com/benchmark-action/github-action-benchmark/pull/138) jobs: build: strategy: diff --git a/lib/Cargo.toml b/lib/Cargo.toml index b02ed47..6448e5f 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -3,6 +3,8 @@ name = "lib" version = "0.8.0" edition = "2021" +[lib] +bench=false [dependencies] # Convenient error handling From 6a5751ad94588e24d6eac950585da06574f063c4 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:55:55 +0200 Subject: [PATCH 12/17] fix: benchmark CI --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 046e8dd..b0cc6f4 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -45,7 +45,7 @@ jobs: key: benches-${{ runner.os }}-unit_fluvio_protocol - name: Run Benchmarks - run: cargo bench -- --output-format bencher | tee markov_model_bench.txt + run: cd lib; cargo bench -- --output-format bencher | tee markov_model_bench.txt - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 From 702656a12a5c5a1b57faf0fe2b5b8596c01fe9e5 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:00:36 +0200 Subject: [PATCH 13/17] fix: Benchmark CI --- .github/workflows/bench.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index b0cc6f4..b4f53d9 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -49,7 +49,7 @@ jobs: - name: Store benchmark result uses: benchmark-action/github-action-benchmark@v1 - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' with: # What benchmark tool the output.txt came from tool: 'cargo' From ce391c79dc80313956d0f6cbdb9ec32b6e5150a4 Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:03:28 +0200 Subject: [PATCH 14/17] fix: Adjusted cache keys --- .github/workflows/bench.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index b4f53d9..480bd1b 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -35,14 +35,14 @@ jobs: uses: Swatinem/rust-cache@v2 with: # Additional non workspace directories, separated by newlines - key: benches-${{ runner.os }}-unit_fluvio_protocol-rust + key: benches-${{ runner.os }}-unit_markov_model-rust - name: Cache Benchmark data uses: actions/cache@v3 - if: github.ref == 'refs/heads/master' + if: github.ref == 'refs/heads/main' with: path: ./benches_cache - key: benches-${{ runner.os }}-unit_fluvio_protocol + key: benches-${{ runner.os }}-unit_markov_model-rust - name: Run Benchmarks run: cd lib; cargo bench -- --output-format bencher | tee markov_model_bench.txt From 4b8140ab877a1c2e0fe6578259ca0ce52549665a Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:23:30 +0200 Subject: [PATCH 15/17] feat: Made timeout configurable --- lib/benches/markov_models.rs | 2 +- lib/src/markov/mod.rs | 7 +++++-- lib/src/mqtt/mod.rs | 14 +++++++++----- lib/src/runtime/mod.rs | 4 ++-- src/main.rs | 4 +++- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/benches/markov_models.rs b/lib/benches/markov_models.rs index 559d93f..82a7440 100644 --- a/lib/benches/markov_models.rs +++ b/lib/benches/markov_models.rs @@ -13,7 +13,7 @@ use tokio::sync::RwLock; async fn exec_markov_fast(m: Mode) { let stream = duplex(10 * 1024).0; - let mut machine = StateMachine::new(stream); + let mut machine = StateMachine::new(stream, 100); let mut rng = Xoshiro256PlusPlus::from_seed([5; 32]); machine .execute(m, &mut rng, &Arc::new(RwLock::new(PacketQueue::default()))) diff --git a/lib/src/markov/mod.rs b/lib/src/markov/mod.rs index c17637e..6b8b150 100644 --- a/lib/src/markov/mod.rs +++ b/lib/src/markov/mod.rs @@ -90,6 +90,7 @@ where pub previous_packets: Vec, // The current stream, TlsStream TcpStream or WebsocketStream stream: B, + timeout: u16, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] pub enum Mode { @@ -119,12 +120,13 @@ impl StateMachine where B: ByteStream, { - pub fn new(stream: B) -> Self { + pub fn new(stream: B, timeout: u16) -> Self { Self { stream, state: Default::default(), packets: Packets::new(), previous_packets: Vec::new(), + timeout, } } pub async fn execute( @@ -224,7 +226,8 @@ where } State::SEND => { self.previous_packets.push(self.packets.clone()); - let res = send_packets(&mut self.stream, &self.packets, packet_queue).await; + let res = + send_packets(&mut self.stream, &self.packets, packet_queue, self.timeout).await; if let Err(e) = res { match e { SendError::Timeout => {} diff --git a/lib/src/mqtt/mod.rs b/lib/src/mqtt/mod.rs index 52aba16..5a1e0ae 100644 --- a/lib/src/mqtt/mod.rs +++ b/lib/src/mqtt/mod.rs @@ -60,23 +60,23 @@ pub(crate) async fn send_packets( stream: &mut impl ByteStream, packets: &Packets, packet_queue: &Arc>, + timeout: u16, ) -> Result<(), SendError> { for packet in packets.inner.iter().filter(|p| !p.is_empty()) { - send_packet(stream, packet.as_slice(), packets, packet_queue).await?; + send_packet(stream, packet.as_slice(), packets, packet_queue, timeout).await?; } Ok(()) } -const PACKET_TIMEOUT: u64 = 30; - pub(crate) async fn send_packet( stream: &mut impl ByteStream, packet: &[u8], packets: &Packets, packet_queue: &Arc>, + timeout_ms: u16, ) -> Result<(), SendError> { let write_result = timeout( - Duration::from_millis(PACKET_TIMEOUT), + Duration::from_millis(timeout_ms as u64), stream.write_all(packet), ) .await; @@ -91,7 +91,11 @@ pub(crate) async fn send_packet( } } let mut buf = [0; 1024]; - let res = timeout(Duration::from_millis(PACKET_TIMEOUT), stream.read(&mut buf)).await; + let res = timeout( + Duration::from_millis(timeout_ms as u64), + stream.read(&mut buf), + ) + .await; match res { Ok(Ok(p)) => { known_packet(&buf[..p], packets, packet_queue).await; diff --git a/lib/src/runtime/mod.rs b/lib/src/runtime/mod.rs index e53964a..6d33b92 100644 --- a/lib/src/runtime/mod.rs +++ b/lib/src/runtime/mod.rs @@ -22,6 +22,7 @@ pub async fn run_thread( iterations: u64, packet_queue: Arc>, it_sender_clone: Sender, + timeout: u16, ) { let task_handle = task::spawn(async move { let mut last_packets = Vec::new(); @@ -43,12 +44,11 @@ pub async fn run_thread( continue; } let new_tcpstream = new_stream.unwrap(); - let mut state_machine = StateMachine::new(new_tcpstream); + let mut state_machine = StateMachine::new(new_tcpstream, timeout); let mode = rng.gen(); state_machine.execute(mode, &mut rng, &packet_queue).await; last_packets = state_machine.previous_packets.clone(); // We receive a message once the broker is stopped - // TODO: Also save last packets upon crash if !receiver_clone.is_empty() { break; } diff --git a/src/main.rs b/src/main.rs index e05b96a..edf8008 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ struct Cli { broker_command: String, // TODO: Make the timeout configurable #[arg(long, default_value = "200")] - timeout: u64, + timeout: u16, } #[derive(Subcommand, Debug)] @@ -77,6 +77,7 @@ async fn main() -> color_eyre::Result<()> { u64::MAX, packet_queue.clone(), it_sender_clone, + cli.timeout, )); } // Track it/s @@ -132,6 +133,7 @@ async fn main() -> color_eyre::Result<()> { u64::from_str(&seed_and_iterations.iterations).unwrap(), packet_queue.clone(), unused_it_channel.clone(), + cli.timeout, )); } if *sequential { From 12b90a53d26d505380db422ec6cc11e12d8bc97c Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:32:23 +0200 Subject: [PATCH 16/17] fix: Benchmark CI --- .github/workflows/bench.yml | 2 +- .github/workflows/ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 480bd1b..9a545db 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -54,7 +54,7 @@ jobs: # What benchmark tool the output.txt came from tool: 'cargo' # Where the output from the benchmark tool is stored - output-file-path: markov_model_bench.txt + output-file-path: lib/markov_model_bench.txt # GitHub API token to make a commit comment github-token: ${{ secrets.GITHUB_TOKEN }} # Leave a job summary with benchmark result comparison diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41b6a6b..d08c3b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ on: env: CARGO_TERM_COLOR: always -# TODO: Add benchmarking in CI(see https://github.com/benchmark-action/github-action-benchmark/pull/138) + jobs: build: strategy: From ae38e13729480036a46cfe42ac664f1eeb2bff4e Mon Sep 17 00:00:00 2001 From: Nereuxofficial <37740907+Nereuxofficial@users.noreply.github.com> Date: Thu, 7 Sep 2023 12:54:17 +0200 Subject: [PATCH 17/17] fix: Benchmark CI, Readme improvement --- Readme.md | 12 +++++++++++- images/MarkovModel.png | Bin 0 -> 247568 bytes 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 images/MarkovModel.png diff --git a/Readme.md b/Readme.md index fc788fe..831c1d2 100644 --- a/Readme.md +++ b/Readme.md @@ -9,6 +9,16 @@ A high-performance MQTT network Fuzzer. This is an implementation of [FUME-Fuzzing-MQTT-Brokers](https://github.com/PBearson/FUME-Fuzzing-MQTT-Brokers/) in Rust. +# Architecture +The Fuzzing process is based on a Markov Model seen in the following image: +![Markov Model](images/MarkovModel.png) +For more details see src/lib/lib.rs or the original paper. + +Notable changes from the paper include: +- The use of multiple asynchronous threads for fuzzing by default +- Improved performance +- Only counting broker crashes as crashes(The original paper also counted refused connections as crashes) + # Running the project After [installing Rust](https://rustup.rs), run the following command in the project directory: ``` @@ -35,7 +45,7 @@ Pull requests are welcome. For major changes, please open an issue first to disc ## Compatibility Currently, the Windows build is failing in the ci, however i've only tested this on Linux so far. Maybe it works on Windows, maybe it doesn't. I don't know. Pull Requests to fix this if necessary are welcome. -## Trophies +## 🏆 Trophies All bugs found with this software. If you find a bug using rusty-FUME, please open an issue and I'll add it to the list once it is patched. - [FlashMQ Null pointer dereference](https://github.com/halfgaar/FlashMQ/commit/eb3acf88771af3eeddf086e4c9dc51d703456eee) diff --git a/images/MarkovModel.png b/images/MarkovModel.png new file mode 100644 index 0000000000000000000000000000000000000000..683fc97dd8a270b20d9576b79d9e973b3c725cff GIT binary patch literal 247568 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%0DDhYALUK;x;CIKL&+s^9De0 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

12eqm#?n%Y|X9@`SxNr21CI_&CnjVA#0 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 literal 0 HcmV?d00001