From 539e4ee6654f1642f45f1bd35dbef85e2728b7e3 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 22 Jan 2026 14:45:01 +0100 Subject: [PATCH] detect/transforms: add zlib_deflate transform Ticket: 7846 --- doc/userguide/rules/transforms.rst | 21 +++++++ rust/src/detect/transforms/decompress.rs | 72 +++++++++++++++++++++--- src/detect-engine-register.c | 1 + 3 files changed, 87 insertions(+), 7 deletions(-) diff --git a/doc/userguide/rules/transforms.rst b/doc/userguide/rules/transforms.rst index 62e1b92142..4bb7915170 100644 --- a/doc/userguide/rules/transforms.rst +++ b/doc/userguide/rules/transforms.rst @@ -415,3 +415,24 @@ Example:: from_base64: offset 13 ; gunzip; content:"This is compressed then base64-encoded"; startswith; endswith; sid:2; rev:1;) + +zlib_deflate +------------ + +Takes the buffer, applies zlib decompression. + +This transform takes an optional argument which is a comma-separated list of key-values. +The only key being interperted is ``max-size``, which is the max output size. +Default for max-size is 1024. +If the decompressed data were to be larger than max-size, +the transform will decompress data up to max-size. +Value 0 is forbidden for max-size (there is no unlimited value). + +This example alerts if ``http.uri`` contains base64-encoded zlib-compressed value +Example:: + + alert http any any -> any any (msg:"from_base64 + gunzip"; + http.uri; content:"/zb64?value="; fast_pattern; + from_base64: offset 12 ; + zlib_deflate; content:"This is compressed then base64-encoded"; startswith; endswith; + sid:2; rev:1;) diff --git a/rust/src/detect/transforms/decompress.rs b/rust/src/detect/transforms/decompress.rs index dc9afed084..110476be36 100644 --- a/rust/src/detect/transforms/decompress.rs +++ b/rust/src/detect/transforms/decompress.rs @@ -17,7 +17,7 @@ use crate::detect::uint::detect_parse_uint_with_unit; use crate::detect::SIGMATCH_OPTIONAL_OPT; -use flate2::bufread::GzDecoder; +use flate2::bufread::{GzDecoder, ZlibDecoder}; use suricata_sys::sys::{ DetectEngineCtx, DetectEngineThreadCtx, InspectionBuffer, SCDetectHelperTransformRegister, SCDetectSignatureAddTransform, SCInspectionBufferCheckAndExpand, SCInspectionBufferTruncate, @@ -29,6 +29,7 @@ use std::io::Read; use std::os::raw::{c_int, c_void}; static mut G_TRANSFORM_GUNZIP_ID: c_int = 0; +static mut G_TRANSFORM_ZLIB_DEFLATE_ID: c_int = 0; #[derive(Debug, PartialEq)] struct DetectTransformDecompressData { @@ -37,7 +38,7 @@ struct DetectTransformDecompressData { const DEFAULT_MAX_SIZE: u32 = 1024; // 16 MiB -const ABSOLUTE_MAX_SIZE: u32 = 16*1024*1024; +const ABSOLUTE_MAX_SIZE: u32 = 16 * 1024 * 1024; fn decompress_parse_do(s: &str) -> Option { let mut max_size_parsed = None; @@ -121,8 +122,10 @@ fn gunzip_transform_do(input: &[u8], output: &mut [u8]) -> Option { }; } -unsafe extern "C" fn gunzip_transform( - _det: *mut DetectEngineThreadCtx, buffer: *mut InspectionBuffer, ctx: *mut c_void, +pub type DecompressFn = fn(input: &[u8], output: &mut [u8]) -> Option; + +unsafe fn decompress_transform( + buffer: *mut InspectionBuffer, ctx: &DetectTransformDecompressData, decompress_fn: DecompressFn, ) { let input = (*buffer).inspect; let input_len = (*buffer).inspect_len; @@ -130,8 +133,6 @@ unsafe extern "C" fn gunzip_transform( return; } let input = build_slice!(input, input_len as usize); - let ctx = cast_pointer!(ctx, DetectTransformDecompressData); - let output = SCInspectionBufferCheckAndExpand(buffer, ctx.max_size); if output.is_null() { // allocation failure @@ -150,7 +151,7 @@ unsafe extern "C" fn gunzip_transform( }; // this succeeds if decompressed data > max_size, but we get nb = max_size - if let Some(nb) = gunzip_transform_do(input, buf) { + if let Some(nb) = decompress_fn(input, buf) { SCInspectionBufferTruncate(buffer, nb); } else { // decompression failure @@ -158,6 +159,13 @@ unsafe extern "C" fn gunzip_transform( } } +unsafe extern "C" fn gunzip_transform( + _det: *mut DetectEngineThreadCtx, buffer: *mut InspectionBuffer, ctx: *mut c_void, +) { + let ctx = cast_pointer!(ctx, DetectTransformDecompressData); + decompress_transform(buffer, ctx, gunzip_transform_do); +} + unsafe extern "C" fn decompress_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) { std::mem::drop(Box::from_raw(ctx as *mut DetectTransformDecompressData)); } @@ -171,6 +179,35 @@ unsafe extern "C" fn decompress_id(data: *mut *const u8, length: *mut u32, ctx: *length = std::mem::size_of::() as u32; // 4 } +unsafe extern "C" fn zlib_deflate_setup( + de: *mut DetectEngineCtx, s: *mut Signature, opt_str: *const std::os::raw::c_char, +) -> c_int { + let ctx = decompress_parse(opt_str); + if ctx.is_null() { + return -1; + } + let r = SCDetectSignatureAddTransform(s, G_TRANSFORM_ZLIB_DEFLATE_ID, ctx); + if r != 0 { + decompress_free(de, ctx); + } + return r; +} + +fn zlib_deflate_transform_do(input: &[u8], output: &mut [u8]) -> Option { + let mut gz = ZlibDecoder::new(input); + return match gz.read(output) { + Ok(n) => Some(n as u32), + _ => None, + }; +} + +unsafe extern "C" fn zlib_deflate_transform( + _det: *mut DetectEngineThreadCtx, buffer: *mut InspectionBuffer, ctx: *mut c_void, +) { + let ctx = cast_pointer!(ctx, DetectTransformDecompressData); + decompress_transform(buffer, ctx, zlib_deflate_transform_do); +} + #[no_mangle] pub unsafe extern "C" fn DetectTransformGunzipRegister() { let kw = SCTransformTableElmt { @@ -192,6 +229,27 @@ pub unsafe extern "C" fn DetectTransformGunzipRegister() { } } +#[no_mangle] +pub unsafe extern "C" fn DetectTransformZlibDeflateRegister() { + let kw = SCTransformTableElmt { + name: b"zlib_deflate\0".as_ptr() as *const libc::c_char, + desc: b"modify buffer via zlib decompression\0".as_ptr() as *const libc::c_char, + url: b"/rules/transforms.html#zlib_deflate\0".as_ptr() as *const libc::c_char, + Setup: Some(zlib_deflate_setup), + flags: SIGMATCH_OPTIONAL_OPT, + Transform: Some(zlib_deflate_transform), + Free: Some(decompress_free), + TransformValidate: None, + TransformId: Some(decompress_id), + }; + unsafe { + G_TRANSFORM_ZLIB_DEFLATE_ID = SCDetectHelperTransformRegister(&kw); + if G_TRANSFORM_ZLIB_DEFLATE_ID < 0 { + SCLogWarning!("Failed registering transform zlib_deflate"); + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 7b66659804..477247642b 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -750,6 +750,7 @@ void SigTableSetup(void) SCDetectTransformDomainRegister(); DetectTransformLuaxformRegister(); DetectTransformGunzipRegister(); + DetectTransformZlibDeflateRegister(); DetectFileHandlerRegister();