Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

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

未关闭 适合新手
#101 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

@memx13 已经在做这个了。

开始于 2026年10月23日。

  • #102 来自 @memx13 —— 未关闭

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
78/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
python
领域
backend, security

调研方向

阅读 Evtx/Evtx.py,重点查看 ChunkHeader._load_strings、_load_templates 以及 Nodes.py 中的 next_offset 字段。先在超时限制下重现提供的循环偏移概念验证,然后检查两个加载器是否会遇到重复偏移。循环链能够返回而不是一直挂起,并且回归测试覆盖循环情况后,即为完成。

由索引模型根据 Issue 内容生成。

描述

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)
主要语言
Python
星标
783
派生
169
平均合并
2 天 21 小时
30 天内合并 PR
1

环境准备

这个项目没有提供开发容器、Dockerfile 或贡献指南,环境需要你自己搭建:先看它的 README,通用步骤见我们的新手贡献指南。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

williballenthin/python-evtx 的其他 Issue

查看 williballenthin/python-evtx 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。