100 lines
3.3 KiB
Rust
100 lines
3.3 KiB
Rust
#![no_main]
|
|
use libfuzzer_sys::fuzz_target;
|
|
use pumpkin_protocol::bedrock::packet_decoder::UDPNetworkDecoder;
|
|
use pumpkin_protocol::bedrock::server::{
|
|
client_cache_status::SClientCacheStatus,
|
|
command_request::SCommandRequest,
|
|
container_close::SContainerClose,
|
|
interaction::SInteraction,
|
|
loading_screen::SLoadingScreen,
|
|
login::SLogin,
|
|
player_auth_input::SPlayerAuthInput,
|
|
raknet::{
|
|
connection::SConnectionRequest,
|
|
open_connection::{SOpenConnectionRequest1, SOpenConnectionRequest2},
|
|
unconnected_ping::{SUnconnectedPing, SUnconnectedPingOpenConnections},
|
|
},
|
|
request_chunk_radius::SRequestChunkRadius,
|
|
request_network_settings::SRequestNetworkSettings,
|
|
text::SText,
|
|
};
|
|
use pumpkin_protocol::serial::PacketRead;
|
|
use std::io::Cursor;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helper: Run every Serverbound packet's read method.
|
|
// ---------------------------------------------------------------------------
|
|
fn fuzz_serverbound_packets(payload: &[u8]) {
|
|
let mut cursor = Cursor::new(payload);
|
|
|
|
macro_rules! run_read {
|
|
($($packet:ty),* $(,)?) => {
|
|
$(
|
|
cursor.set_position(0);
|
|
// Attempt to read as a standard Game Packet (with version)
|
|
let _ = <$packet>::read(&mut cursor);
|
|
)*
|
|
};
|
|
}
|
|
|
|
// Standard Bedrock Serverbound Packets
|
|
run_read!(
|
|
SClientCacheStatus,
|
|
SCommandRequest,
|
|
SContainerClose,
|
|
SInteraction,
|
|
SLoadingScreen,
|
|
SLogin,
|
|
SPlayerAuthInput,
|
|
SRequestChunkRadius,
|
|
SRequestNetworkSettings,
|
|
SText,
|
|
);
|
|
|
|
// RakNet Handshake Packets (Usually read without version)
|
|
cursor.set_position(0);
|
|
let _ = SConnectionRequest::read(&mut cursor);
|
|
cursor.set_position(0);
|
|
let _ = SOpenConnectionRequest1::read(&mut cursor);
|
|
cursor.set_position(0);
|
|
let _ = SOpenConnectionRequest2::read(&mut cursor);
|
|
cursor.set_position(0);
|
|
let _ = SUnconnectedPing::read(&mut cursor);
|
|
cursor.set_position(0);
|
|
let _ = SUnconnectedPingOpenConnections::read(&mut cursor);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fuzz Target
|
|
// ---------------------------------------------------------------------------
|
|
fuzz_target!(|data: &[u8]| {
|
|
if data.len() < 20 {
|
|
return;
|
|
}
|
|
|
|
// Split data for decoder configuration vs raw payload
|
|
let threshold_raw = data[0];
|
|
let key: [u8; 16] = data[1..17].try_into().unwrap();
|
|
let stream_data = &data[17..];
|
|
|
|
let mut decoder = UDPNetworkDecoder::new();
|
|
|
|
// Setup Decoder
|
|
if threshold_raw > 0 {
|
|
// Assuming your CompressionThreshold is a wrapper around u32
|
|
decoder.set_compression((threshold_raw as u32).try_into().unwrap());
|
|
}
|
|
decoder.set_encryption(&key);
|
|
|
|
// 1. Fuzz the Decoder (Framing/VarInts/Bitmasks)
|
|
let decoder_cursor = Cursor::new(stream_data.to_vec());
|
|
if let Ok(raw_packet) = decoder.get_game_packet(decoder_cursor) {
|
|
// If framed correctly, fuzz the internal payload
|
|
fuzz_serverbound_packets(&raw_packet.payload);
|
|
}
|
|
|
|
// 2. Shotgun Fuzz
|
|
// Pass raw data directly to all readers to catch panics in the parsers
|
|
fuzz_serverbound_packets(stream_data);
|
|
});
|