Infinite loop in ChunkHeader._load_strings / _load_templates on a crafted chunk (cyclic next_offset)
Bewertung
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Anfängerfreundlichkeit
- 78/100
Rechercherichtung
Lies Evtx/Evtx.py und konzentriere dich auf ChunkHeader._load_strings und _load_templates sowie auf die next_offset-Felder in Nodes.py. Reproduziere zuerst unter einem Timeout den bereitgestellten Proof of Concept mit zyklischen Offsets und prüfe anschließend beide Loader auf wiederholte Offsets. Die Aufgabe ist erledigt, wenn zyklische Ketten zurückkehren, statt zu hängen, und Regressionstests die Zyklusfälle abdecken.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Summary
Evtx.Evtx.ChunkHeader._load_strings and ._load_templates walk each hash-bucket's on-disk next_offset chain with no guard against an offset that repeats. Because next_offset is read straight from the file, a crafted chunk whose string (or template) chain is cyclic — node A's next_offset points to B and B's points back to A — makes the while ofs > 0: loop run forever. Parsing an untrusted .evtx then hangs the process indefinitely (CWE-835, uncontrolled loop / denial of service).
This is separate from the length-0 records() loop fixed in #17; these two chunk loaders have no cycle/visited protection, and the problem is still present on master (c000744).
Affected code
Evtx/Evtx.py:
def _load_strings(self):
...
for i in range(64):
ofs = self.unpack_dword(0x80 + (i * 4))
while ofs > 0:
string_node = self.add_string(ofs)
ofs = string_node.next_offset() # dword read straight from the file
next_offset is a dword read directly from the buffer (Nodes.py, NameStringNode / TemplateNode: declare_field("dword", "next_offset", 0x0)), so an attacker fully controls it. _load_templates has the same shape — it checks a 0x0C token and a self-pointer per node, but two mutually-pointing templates satisfy both checks, so it can cycle too.
Reachability
Evtx(path).records() iterates chunks without verifying their checksums, and rendering a record reaches add_string → _load_strings. So a single crafted file passed to evtx_dump badfile.evtx hangs the process.
Proof of concept
Builds one 64 KB chunk whose first string bucket points at a two-node cycle (A↔B). The vulnerable call never returns, so run it under a timeout:
import struct
from Evtx.Evtx import ChunkHeader
buf = bytearray(0x10000)
buf[0:8] = b"ElfChnk\x00"
a, b = 0x200, 0x210
struct.pack_into("<I", buf, 0x80, a) # string hash bucket[0] -> A
struct.pack_into("<I", buf, a, b) # A.next_offset -> B
struct.pack_into("<H", buf, a + 6, 0) # A.string_length = 0
struct.pack_into("<I", buf, b, a) # B.next_offset -> A (A <-> B)
struct.pack_into("<H", buf, b + 6, 0) # B.string_length = 0
ChunkHeader(bytes(buf), 0).strings() # never returns
Measured on master (c000744): a non-cyclic bucket returns in ~0.002 s; the cyclic one does not return (killed after 8 s). Reproduced on Python 3.14 (the loop is version-independent).
Suggested fix
Track the offsets already visited while walking a bucket and stop when one repeats. I'll open a PR with the fix plus a regression test that fails on current master and passes with the change.
Environment
- python-evtx master (
c000744), version 0.8.1 - Python 3.14 (reproduces on 3.9–3.14; not version-specific)
- Vorherrschende Sprache
- Python
- Sterne
- 783
- Forks
- 169
- Ø Merge
- 2 T. 21 Std.
- Gemergte PRs (30 T.)
- 1
Entwicklungsumgebung
Dieses Projekt bietet weder Dev-Container noch Dockerfile noch Beitragsleitfaden – die Einrichtung liegt bei Ihnen. Beginnen Sie mit der README; die allgemeinen Schritte stehen in unserem Leitfaden für den ersten Beitrag.
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus williballenthin/python-evtx
-
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 25/100
-
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 35/100
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 42/100
williballenthin/python-evtx#94 · 2 Kommentare ·
-
bug
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 35/100
williballenthin/python-evtx#85 · 2 Kommentare ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 42/100
Alle Issues in williballenthin/python-evtx
Ähnliche Issues
-
type-bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag
-
push_gh-pages fails on master branch on forksEvtl. vergeben Ein verknüpfter Pull Request ist offen oder bereits gemergt. Offengood first issue infrastructure
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 76/100
kuznia-rdzeni/coreblocks#1135 ·
-
good first issue server
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 85/100
Maintainer antworten meist innerhalb von 1 Tag
-
documentation priority: high
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
a-lex-s/mealie-telegram-bot#54 ·
Maintainer antworten meist innerhalb von 1 Tag