From 690b3d0fda0d138b349e8a3f66ce32b6ab991e20 Mon Sep 17 00:00:00 2001 From: jmestwa-coder Date: Mon, 1 Jun 2026 16:04:37 +0530 Subject: [PATCH] guard against undersized rich header in pe_parse_rich_signature (#2200) When the backward scan in pe_parse_rich_signature finds a DanS marker only one to three dwords ahead of the Rich marker, rich_len ends up smaller than RICH_SIGNATURE. The unsigned subtraction rich_len - sizeof(RICH_SIGNATURE) then wraps and rich_count becomes a huge bogus value that drives the version_data allocation and copy loop. _rich_version already rejects rich_length < sizeof(RICH_SIGNATURE) for the same computation, so apply the same check before computing rich_count here. --- libyara/modules/pe/pe.c | 9 +++++++++ tests/data/undersized_rich | Bin 0 -> 392 bytes tests/test-pe.c | 11 +++++++++++ 3 files changed, 20 insertions(+) create mode 100644 tests/data/undersized_rich diff --git a/libyara/modules/pe/pe.c b/libyara/modules/pe/pe.c index e2eda238..fe9a6b55 100644 --- a/libyara/modules/pe/pe.c +++ b/libyara/modules/pe/pe.c @@ -248,6 +248,15 @@ static void pe_parse_rich_signature(PE* pe, uint64_t base_address) yr_set_sized_string( (char*) clear_data, rich_len, pe->object, "rich_signature.clear_data"); + // A Rich header shorter than RICH_SIGNATURE makes the unsigned subtraction + // below wrap around, producing a bogus rich_count. _rich_version guards the + // same computation, do it here too. + if (rich_len < sizeof(RICH_SIGNATURE)) + { + yr_free(clear_data); + return; + } + // Allocate space for just the version data. This is a series of every other // dword from the clear data. This is useful to be able to hash alone. // We need to skip the first 3 DWORDs of the RICH_SIGNATURE, which are DanS diff --git a/tests/data/undersized_rich b/tests/data/undersized_rich new file mode 100644 index 0000000000000000000000000000000000000000..2d754302b10211102c55eb1d8a3a60182762f20d GIT binary patch literal 392 vcmeZ`Vjvq#AX_J@MjP`e$DqvQjEXQ5p#WC~1|LRLc@U2qix{S=p(_FaLevG$ literal 0 HcmV?d00001 diff --git a/tests/test-pe.c b/tests/test-pe.c index 1e706402..7e0dc3e8 100644 --- a/tests/test-pe.c +++ b/tests/test-pe.c @@ -635,6 +635,17 @@ int main(int argc, char** argv) }", "tests/data/weird_rich"); + // The DanS marker sits one dword before Rich, so the header is shorter than + // RICH_SIGNATURE. This used to underflow the rich_count computation; now it + // is rejected and the file still parses as a PE. + assert_true_rule_file( + "import \"pe\" \ + rule test { \ + condition: \ + pe.is_pe and pe.rich_signature.length == 4 \ + }", + "tests/data/undersized_rich"); + assert_true_rule_file( "import \"pe\" \ rule test { \