From 622ea976c7923f2619111ff92c5169ae18126bbb Mon Sep 17 00:00:00 2001 From: jmestwa-coder Date: Tue, 30 Jun 2026 23:50:12 +0530 Subject: [PATCH] byte-swap sh_entry_count in elf symtab/dynsym link check (#2214) Missing byte swap in the ELF symtab/dynsym link check The SHT_SYMTAB and SHT_DYNSYM string table check compares sh_link against the raw e_shnum (sh_entry_count) while every other use in the macro byte-swaps it, so on a little-endian host reading a big-endian ELF the check accepts a link past the real section count and parses the symbol string table from an out-of-range section header. Swapped it to match the sibling reads, and added a regression test scanning a crafted big-endian ELF whose only symtab carries an out-of-range sh_link. --- libyara/modules/elf/elf.c | 6 ++++-- tests/blob.h | 33 +++++++++++++++++++++++++++++++++ tests/test-elf.c | 12 ++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/libyara/modules/elf/elf.c b/libyara/modules/elf/elf.c index 69342a1f..29f959fe 100644 --- a/libyara/modules/elf/elf.c +++ b/libyara/modules/elf/elf.c @@ -559,7 +559,8 @@ static const char* str_table_entry( } \ \ if (yr_##bo##32toh(section->type) == ELF_SHT_SYMTAB && \ - yr_##bo##32toh(section->link) < elf->sh_entry_count) \ + yr_##bo##32toh(section->link) < \ + yr_##bo##16toh(elf->sh_entry_count)) \ { \ elf##bits##_section_header_t* string_section = section_table + \ yr_##bo##32toh( \ @@ -577,7 +578,8 @@ static const char* str_table_entry( } \ \ if (yr_##bo##32toh(section->type) == ELF_SHT_DYNSYM && \ - yr_##bo##32toh(section->link) < elf->sh_entry_count) \ + yr_##bo##32toh(section->link) < \ + yr_##bo##16toh(elf->sh_entry_count)) \ { \ elf##bits##_section_header_t* dynstr_section = section_table + \ yr_##bo##32toh( \ diff --git a/tests/blob.h b/tests/blob.h index 4b1ed125..0387abe0 100644 --- a/tests/blob.h +++ b/tests/blob.h @@ -2139,6 +2139,39 @@ unsigned char ELF_x64_FILE[] = { }; +// Synthetic big-endian (ELFDATA2MSB) ELF32 used to exercise the symtab/dynsym +// sh_link bounds check. The only SHT_SYMTAB section has sh_link = 4, which is +// >= the real section count (3) but below the un-byteswapped count, so a +// little-endian host that fails to byte-swap e_shnum would treat an +// out-of-range section header as the symbol string table. +unsigned char ELF32_BE_BAD_SYMTAB_LINK[] = { + 0x7f, 0x45, 0x4c, 0x46, 0x01, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x42, 0x43, 0x44, + 0x00, 0x00, 0x00}; + uint8_t MACHO_X86_FILE[] = { 0xce, 0xfa, 0xed, 0xfe, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, diff --git a/tests/test-elf.c b/tests/test-elf.c index 74234a09..70511b2a 100644 --- a/tests/test-elf.c +++ b/tests/test-elf.c @@ -171,6 +171,18 @@ int main(int argc, char** argv) }", ELF32_MIPS_FILE); + // A big-endian symtab whose sh_link is past the real section count must not + // be parsed against an out-of-range section header. Before the e_shnum byte + // swap fix a little-endian host accepted the bogus link and reported a + // phantom symbol named "ABCD". + assert_false_rule_blob( + "import \"elf\" \ + rule test { \ + condition: \ + elf.symtab_entries == 1 and elf.symtab[0].name == \"ABCD\" \ + }", + ELF32_BE_BAD_SYMTAB_LINK); + assert_true_rule_blob( "import \"elf\" \ rule test { \