148 lines
5.5 KiB
Rust
148 lines
5.5 KiB
Rust
#![no_main]
|
|
use libfuzzer_sys::fuzz_target;
|
|
use pumpkin_protocol::{
|
|
ServerPacket,
|
|
java::{
|
|
packet_decoder::TCPNetworkDecoder,
|
|
server::{
|
|
config::{
|
|
SClientInformationConfig, SConfigCookieResponse, SConfigResourcePack, SKnownPacks,
|
|
SPluginMessage,
|
|
},
|
|
handshake::SHandShake,
|
|
login::{SEncryptionResponse, SLoginCookieResponse, SLoginPluginResponse, SLoginStart},
|
|
play::{
|
|
SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot,
|
|
SClientCommand, SClientInformationPlay, SCloseContainer, SCommandSuggestion,
|
|
SConfirmTeleport, SCookieResponse, SCustomPayload, SInteract, SKeepAlive,
|
|
SMoveVehicle, SPaddleBoat, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities,
|
|
SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded, SPlayerPosition,
|
|
SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SSetCommandBlock,
|
|
SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem,
|
|
SUseItemOn,
|
|
},
|
|
status::SStatusPingRequest,
|
|
},
|
|
},
|
|
};
|
|
use std::io::Cursor;
|
|
use tokio::runtime::Runtime;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helper: run every known ServerPacket::read against the same payload.
|
|
// We don't care about the result — only that nothing panics.
|
|
// ---------------------------------------------------------------------------
|
|
fn fuzz_all_deserializers(payload: &[u8]) {
|
|
// Handshake
|
|
let _ = SHandShake::read(payload);
|
|
|
|
// Status
|
|
//let _ = SStatusRequest::read(payload);
|
|
let _ = SStatusPingRequest::read(payload);
|
|
|
|
// Login
|
|
let _ = SLoginStart::read(payload);
|
|
let _ = SEncryptionResponse::read(payload);
|
|
let _ = SLoginPluginResponse::read(payload);
|
|
//let _ = SLoginAcknowledged::read(payload);
|
|
let _ = SLoginCookieResponse::read(payload);
|
|
|
|
// Config
|
|
let _ = SClientInformationConfig::read(payload);
|
|
let _ = SPluginMessage::read(payload);
|
|
//let _ = SAcknowledgeFinishConfig::read(payload);
|
|
let _ = SKnownPacks::read(payload);
|
|
let _ = SConfigCookieResponse::read(payload);
|
|
let _ = SConfigResourcePack::read(payload);
|
|
|
|
// Play
|
|
let _ = SConfirmTeleport::read(payload);
|
|
let _ = SChangeGameMode::read(payload);
|
|
let _ = SChatCommand::read(payload);
|
|
let _ = SChatMessage::read(payload);
|
|
let _ = SClientInformationPlay::read(payload);
|
|
let _ = SClientCommand::read(payload);
|
|
let _ = SPlayerInput::read(payload);
|
|
let _ = SMoveVehicle::read(payload);
|
|
let _ = SPaddleBoat::read(payload);
|
|
let _ = SInteract::read(payload);
|
|
let _ = SKeepAlive::read(payload);
|
|
let _ = SPlayerPosition::read(payload);
|
|
let _ = SPlayerPositionRotation::read(payload);
|
|
let _ = SPlayerRotation::read(payload);
|
|
let _ = SSetPlayerGround::read(payload);
|
|
let _ = SPickItemFromBlock::read(payload);
|
|
let _ = SPlayerAbilities::read(payload);
|
|
let _ = SPlayerAction::read(payload);
|
|
let _ = SSetCommandBlock::read(payload);
|
|
let _ = SPlayerCommand::read(payload);
|
|
let _ = SPlayerLoaded::read(payload);
|
|
let _ = SPlayPingRequest::read(payload);
|
|
let _ = SClickSlot::read(payload);
|
|
let _ = SSetHeldItem::read(payload);
|
|
let _ = SSetCreativeSlot::read(payload);
|
|
let _ = SSwingArm::read(payload);
|
|
let _ = SUpdateSign::read(payload);
|
|
let _ = SUseItemOn::read(payload);
|
|
let _ = SUseItem::read(payload);
|
|
let _ = SCommandSuggestion::read(payload);
|
|
let _ = SCookieResponse::read(payload);
|
|
let _ = SCloseContainer::read(payload);
|
|
let _ = SChunkBatch::read(payload);
|
|
let _ = SPlayerSession::read(payload);
|
|
let _ = SCustomPayload::read(payload);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fuzz target
|
|
// ---------------------------------------------------------------------------
|
|
fuzz_target!(|data: &[u8]| {
|
|
if data.len() < 18 {
|
|
return;
|
|
}
|
|
|
|
// Byte layout:
|
|
// [0] = decoder mode (0=plain, 1=compressed, 2=encrypted, 3=both)
|
|
// [1] = split point % rest.len() — how much goes to the decoder vs
|
|
// the deserializers, letting the fuzzer explore both paths
|
|
// independently with different byte patterns
|
|
// [2..18] = 16-byte AES key candidate
|
|
// [18..] = raw bytes fed to both fuzzing paths
|
|
let mode = data[0] % 4;
|
|
let key = &data[2..18];
|
|
let rest = &data[18..];
|
|
|
|
let split = if rest.is_empty() {
|
|
0
|
|
} else {
|
|
(data[1] as usize) % rest.len()
|
|
};
|
|
let (decoder_bytes, deser_bytes) = rest.split_at(split);
|
|
|
|
// --- Path 1: decoder (framing / encryption / compression) --------------
|
|
let rt = Runtime::new().unwrap();
|
|
rt.block_on(async {
|
|
let mut decoder = TCPNetworkDecoder::new(Cursor::new(decoder_bytes));
|
|
match mode {
|
|
1 => {
|
|
decoder.set_compression(256);
|
|
}
|
|
2 => {
|
|
let mut aes_key = [0u8; 16];
|
|
aes_key.copy_from_slice(key);
|
|
decoder.set_encryption(&aes_key);
|
|
}
|
|
3 => {
|
|
decoder.set_compression(256);
|
|
let mut aes_key = [0u8; 16];
|
|
aes_key.copy_from_slice(key);
|
|
decoder.set_encryption(&aes_key);
|
|
}
|
|
_ => {}
|
|
}
|
|
let _ = decoder.get_raw_packet().await;
|
|
});
|
|
|
|
fuzz_all_deserializers(deser_bytes);
|
|
});
|