Infinite loop in ChunkHeader._load_strings / _load_templates on a crafted chunk (cyclic next_offset)
Avaliação
- Dificuldade
- 2/5
- Tempo estimado
- 1-3 horas
- Facilidade para iniciantes
- 78/100
Direção de pesquisa
Leia Evtx/Evtx.py, com foco em ChunkHeader._load_strings e _load_templates e nos campos next_offset em Nodes.py. Comece reproduzindo, com um timeout, a prova de conceito fornecida sobre offsets cíclicos e, em seguida, verifique se os dois carregadores encontram offsets repetidos. O trabalho estará concluído quando as cadeias cíclicas retornarem em vez de ficarem travadas e os testes de regressão cobrirem os casos de ciclo.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
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)
- Linguagem predominante
- Python
- Estrelas
- 785
- Forks
- 170
- Merge médio
- 2d 21h
- PRs com merge (30d)
- 1
Preparar o ambiente
Este projeto não oferece contêiner de desenvolvimento, Dockerfile nem guia de contribuição, então a configuração fica por sua conta: comece pelo README e veja nosso guia da primeira contribuição para os passos gerais.
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de williballenthin/python-evtx
-
Dificuldade 5/5 Mais de uma semana Facilidade para iniciantes 25/100
-
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 35/100
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 42/100
williballenthin/python-evtx#94 · 2 comentários ·
-
parse data from dirty chunksAbertabug
Dificuldade 4/5 3-5 dias Facilidade para iniciantes 35/100
williballenthin/python-evtx#85 · 2 comentários ·
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 42/100
Todas as issues de williballenthin/python-evtx
Issues semelhantes
-
component:benchmark
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
agentic-os-org/ANOLISA#9177 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 88/100
distrochooser/distrochooser#520 ·
Mantenedores costumam responder em até 2 dias
-
Status: Needs confirmation Status: Needs triage
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 64/100
Mantenedores costumam responder em até 1 dia
-
[BUG] Published Docker images lack the redis extra: HEADROOM_COMPRESS_SESSION_STORE stops startupTalvez já em andamento @AlessandroSpallina assumiu hoje. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
headroomlabs-ai/headroom#4513 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 1/5 Menos de uma hora Facilidade para iniciantes 65/100
codespell-project/codespell#4060 ·
Mantenedores costumam responder em até 1 dia