fix: support 32KB page sizes in ESE parser for Windows Server 2025#2165
fix: support 32KB page sizes in ESE parser for Windows Server 2025#2165juliosuas wants to merge 1 commit into
Conversation
|
Hi, I’ve been reviewing this PR because it targets the same issue (#1924) as another one (#2158). The two PRs complement each other, but they address different parts of the problem:
Issue #1924 happens because, once the tag count is misread, the parser walks past the real tag array and starts interpreting ordinary page bytes as if they were additional tags. At that point it can decode bogus entries with valueSize In other words, #2158 fixes the root cause by making the parser stop at the real end of the tag table, while this PR hardens large-page tag parsing in two ways: it avoids one crash that can happen after reading past the real tag table (which should not happen anymore after #2158), The final piece of the puzzle is finding a sample that contains a correctly parsed tag with valueSize == 0; the spec says this is possible, and such a sample would demonstrate that this PR is necessary for those scenarios. |
|
Thanks for the careful framing — agree with how you've split it. Your #2158 stops the out-of-bounds walk by reading FirstAvailablePageTag correctly, so the bogus On the spec-valid valueSize == 0 case: I don't have a clean NTDS.dit sample where it occurs naturally — the only way I hit it was through the out-of-bounds walk #2158 fixes. The ESE format docs allow it (no value, common-key only) and libesedb handles it explicitly in its tag/value parsing, but I haven't been able to capture a real-world dit that has it on a properly-bounded tag. Happy to either:
Whichever you / the maintainers prefer. Thanks again for taking the time on this. |
|
The best for now will be to keep the PR open, at least until we find some scenario that could exercise this particular changes |
Windows Server 2025 uses 32KB (32768 byte) database pages in NTDS.dit instead of the previous 8KB (8192 byte) pages. The ESE parser's getTag() method crashed with 'IndexError: bytearray index out of range' when processing these larger pages. Root cause: For large pages (>8KB) with format revision >= 17, the page tag flags are stored in the upper 3 bits of the first 16-bit value of the entry data itself (not in the tag entry). The code accessed tmpData[1] unconditionally, but when valueSize is 0 (empty entries like leaf/branch page headers with no common key), tmpData is an empty bytearray, causing the IndexError. Fix: Add bounds checking before accessing tmpData[1]: - valueSize >= 2: extract flags from tmpData[1] as before (normal path) - valueSize == 1: set flags to 0, return single byte as-is - valueSize == 0: set flags to 0, return empty bytes (was crashing) This preserves backward compatibility with 8KB pages (the else branch is unchanged) and follows the ESE format specification from libyal. Fixes fortra#1924
8b3bef8 to
983272d
Compare
Summary
Fixes #1924 —
secretsdump.pycrashes withIndexError: bytearray index out of rangewhen parsing NTDS.dit files from Windows Server 2025.Root Cause
Windows Server 2025 uses 32KB (32768 byte) database pages in NTDS.dit, up from the previous 8KB (8192 byte) pages. The ESE format specification (revision 0x11+) defines a different page tag format for pages larger than 8KB:
The existing code in
getTag()correctly handles this format except whenvalueSizeis 0 (empty entries, such as leaf/branch page headers with no common key). In that case,tmpDatais an empty bytearray andtmpData[1]raisesIndexError.The Fix
Added bounds checking before accessing
tmpData[1]in the large-page code path:valueSizetmpData[1] >> 5(existing logic, unchanged)The 8KB page code path (the
elsebranch) is completely unchanged, preserving backward compatibility.References
Testing