Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

Infinite loop in ChunkHeader._load_strings / _load_templates on a crafted chunk (cyclic next_offset)

Aberta Para iniciantes
#101 0 comentários 0 reações 0 responsáveis Ver no GitHub

@memx13 já está trabalhando nisso.

Desde 23/10/2026.

  • #102 de @memx13 — aberto

Avaliação

Dificuldade
2/5
Tempo estimado
1-3 horas
Facilidade para iniciantes
78/100
Tipo de issue
Bug
Clareza
Claramente especificada
Status de atividade
Ativa
Stack de tecnologia
python
Domínio
backend, security

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

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de williballenthin/python-evtx

Todas as issues de williballenthin/python-evtx

Issues semelhantes

Mais issues de Python

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.