swf: validate configured depth limits

SWF depth settings were previously limited only to values representable by
uint32_t. This is not sufficient for the buffer-size calculations performed
during SWF decompression.

The decompressed output requires an additional 8-byte FWS header. The
inspection buffer also grows by doubling a uint32_t size, so a required buffer
larger than 2 GiB can cause the growth calculation to wrap.

For LZMA input, a synthesized 13-byte header to the compressed
data is used. The configured compressed-data limit must leave room for this header.

This patch applies the following upper limits:

- decompress-depth: 2147483640 bytes
  (2 GiB minus the 8-byte FWS header)
- compress-depth: 4294967282 bytes
  (UINT32_MAX minus the 13-byte LZMA header

Ticket: #8914
This commit is contained in:
Giuseppe Longo
2026-09-11 07:56:28 +02:00
committed by Victor Julien
parent e707b21e21
commit ae4341eb5f
8 changed files with 58 additions and 18 deletions
@@ -1729,11 +1729,11 @@ use of libhtp.
# 2 types: 'deflate', 'lzma', 'both' will decompress deflate and lzma
# compress-depth:
# Specifies the maximum amount of data to decompress,
# set 0 for unlimited.
# set 0 for unlimited. The maximum is 4294967282 bytes.
# decompress-depth:
# Specifies the maximum amount of decompressed data to obtain. The
# output buffer grows on demand up to this limit. Set 0 to use the
# default limit of 50 MB.
# default limit of 50 MB. The maximum is 2147483640 bytes.
swf-decompression:
enabled: yes
type: both
+2 -2
View File
@@ -872,11 +872,11 @@ setting.
# 2 types: 'deflate', 'lzma', 'both' will decompress deflate and lzma
# compress-depth:
# Specifies the maximum amount of data to decompress,
# set 0 for unlimited.
# set 0 for unlimited. The maximum is 4294967282 bytes.
# decompress-depth:
# Specifies the maximum amount of decompressed data to obtain. The
# output buffer grows on demand up to this limit. Set 0 to use the
# default limit of 50 MB.
# default limit of 50 MB. The maximum is 2147483640 bytes.
swf-decompression:
enabled: yes
type: both
+11 -8
View File
@@ -58,6 +58,7 @@
#include "app-layer-events.h"
#include "util-debug.h"
#include "util-file-decompression.h"
#include "util-misc.h"
#include "util-unittest.h"
@@ -2336,17 +2337,19 @@ static void HTPConfigParseParameters(HTPCfgRec *cfg_prec, SCConfNode *s, struct
exit(EXIT_FAILURE);
}
} else if (strcasecmp("compress-depth", pval->name) == 0) {
if (ParseSizeStringU32(pval->val, &cfg_prec->swf_compress_depth) < 0) {
SCLogError("Error parsing swf-decompression.compression-depth "
"from conf file - %s. Killing engine",
p->val);
if (ParseSizeStringU32(pval->val, &cfg_prec->swf_compress_depth) < 0 ||
cfg_prec->swf_compress_depth > MAX_SWF_COMPRESS_DEPTH) {
SCLogError("Invalid swf-decompression.compress-depth value %s: the "
"maximum is %u bytes. Killing engine",
pval->val, MAX_SWF_COMPRESS_DEPTH);
exit(EXIT_FAILURE);
}
} else if (strcasecmp("decompress-depth", pval->name) == 0) {
if (ParseSizeStringU32(pval->val, &cfg_prec->swf_decompress_depth) < 0) {
SCLogError("Error parsing swf-decompression.decompression-depth "
"from conf file - %s. Killing engine",
p->val);
if (ParseSizeStringU32(pval->val, &cfg_prec->swf_decompress_depth) < 0 ||
cfg_prec->swf_decompress_depth > MAX_SWF_DECOMPRESS_DEPTH) {
SCLogError("Invalid swf-decompression.decompress-depth value %s: the "
"maximum is %u bytes. Killing engine",
pval->val, MAX_SWF_DECOMPRESS_DEPTH);
exit(EXIT_FAILURE);
}
} else {
+26
View File
@@ -349,6 +349,30 @@ static int DetectFiledataSwfDecompressDepthTest02(void)
PASS;
}
/**
* \test SWF decompression rejects depth values above the supported limits.
*/
static int DetectFiledataSwfDecompressDepthTest03(void)
{
DetectEngineThreadCtx *det_ctx = SCCalloc(1, sizeof(*det_ctx));
FAIL_IF_NULL(det_ctx);
InspectionBuffer out_buffer = { 0 };
int r = FileSwfDecompression(swf_lzma_fixture, (uint32_t)sizeof(swf_lzma_fixture), det_ctx,
&out_buffer, HTTP_SWF_COMPRESSION_LZMA, MAX_SWF_DECOMPRESS_DEPTH + 1, 0);
FAIL_IF(r != 0);
FAIL_IF_NOT_NULL(out_buffer.buf);
r = FileSwfDecompression(swf_lzma_fixture, (uint32_t)sizeof(swf_lzma_fixture), det_ctx,
&out_buffer, HTTP_SWF_COMPRESSION_LZMA, 0, MAX_SWF_COMPRESS_DEPTH + 1);
FAIL_IF(r != 0);
FAIL_IF_NOT_NULL(out_buffer.buf);
SCAppLayerDecoderEventsFreeEvents(&det_ctx->decoder_events);
SCFree(det_ctx);
PASS;
}
static int DetectEngineSMTPFiledataTest02(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
@@ -401,6 +425,8 @@ void DetectFiledataRegisterTests(void)
"DetectFiledataSwfDecompressDepthTest01", DetectFiledataSwfDecompressDepthTest01);
UtRegisterTest(
"DetectFiledataSwfDecompressDepthTest02", DetectFiledataSwfDecompressDepthTest02);
UtRegisterTest(
"DetectFiledataSwfDecompressDepthTest03", DetectFiledataSwfDecompressDepthTest03);
UtRegisterTest("DetectEngineSMTPFiledataTest02", DetectEngineSMTPFiledataTest02);
UtRegisterTest("DetectFiledataParseTest04", DetectFiledataParseTest04);
}
+7 -2
View File
@@ -77,6 +77,10 @@ int FileSwfDecompression(const uint8_t *buffer, uint32_t buffer_len,
{
int r = 0;
if (decompress_depth > MAX_SWF_DECOMPRESS_DEPTH || compress_depth > MAX_SWF_COMPRESS_DEPTH) {
return 0;
}
int compression_type = FileIsSwfFile(buffer, buffer_len);
if (compression_type == FILE_SWF_NO_COMPRESSION) {
return 0;
@@ -160,7 +164,7 @@ int FileSwfDecompression(const uint8_t *buffer, uint32_t buffer_len,
* | 5 bytes | 8 bytes | n bytes |
* | LZMA properties | Uncompressed length | Compressed data |
*/
compressed_data_len += 13;
compressed_data_len += SWF_LZMA_HEADER_LEN;
uint8_t *compressed_data = SCCalloc(1, compressed_data_len);
if (compressed_data == NULL) {
DetectEngineSetEvent(det_ctx, FILE_DECODER_EVENT_NO_MEM);
@@ -171,7 +175,8 @@ int FileSwfDecompression(const uint8_t *buffer, uint32_t buffer_len,
/* put lzma end marker */
memset(compressed_data + 5, 0xFF, 8);
/* put compressed data */
memcpy(compressed_data + 13, buffer + offset, compressed_data_len - 13);
memcpy(compressed_data + SWF_LZMA_HEADER_LEN, buffer + offset,
compressed_data_len - SWF_LZMA_HEADER_LEN);
/* the first 8 bytes represents the fws header, see 'FWS format' above.
* data will start from 8th bytes
+6
View File
@@ -27,6 +27,12 @@
#include "detect.h"
#define SWF_HEADER_LEN 8
#define SWF_LZMA_HEADER_LEN 13
#define MAX_SWF_DECOMPRESS_DEPTH (UINT32_MAX / 2 + 1 - SWF_HEADER_LEN)
#define MAX_SWF_COMPRESS_DEPTH (UINT32_MAX - SWF_LZMA_HEADER_LEN)
enum {
FILE_IS_NOT_SWF = 0,
FILE_SWF_NO_COMPRESSION,
-1
View File
@@ -32,7 +32,6 @@
#define MAX_SWF_DECOMPRESSED_LEN 50000000
#define SWF_HEADER_LEN 8
#define SWF_DECOMPRESS_INITIAL_BUFFER_LEN 4096
uint8_t FileGetSwfVersion(const uint8_t *buffer, const uint32_t buffer_len);
+4 -3
View File
@@ -1172,10 +1172,11 @@ app-layer:
# Two types: 'deflate', 'lzma', 'both' will decompress deflate and lzma
# compress-depth:
# Specifies the maximum amount of data to decompress,
# set 0 for unlimited.
# set 0 for unlimited. The maximum is 4294967282 bytes.
# decompress-depth:
# Specifies the maximum amount of decompressed data to obtain,
# set 0 for unlimited.
# Specifies the maximum amount of decompressed data to obtain. The
# output buffer grows on demand up to this limit. Set 0 to use the
# default limit of 50 MB. The maximum is 2147483640 bytes.
swf-decompression:
enabled: no
type: both