Labs

Services

  • Development

    Web applications, mobile applications, Backend & distributed systems, API design & integration, database design & scaling
  • AI

    Model training & fine-tuning, LLM application design, Agentic tooling & knowledge integration
  • Security

    Penetration testing, Red team & adversary emulation, Attack surface discovery & exposure management
  • Infrastructure

    Cloud architecture, Containerization & platform engineering, CI/CD pipelines & release engineering, Observability & SRE

Analysis

When a Carriage Return Escapes

CVE-2025-48384 is the kind of bug that hides in plain sight: a one‑character discrepancy between how Git reads and writes configuration values, compounded by the trust boundary of submodules and the automaticity of hooks. The canonical record in the MITRE CVE Record assigns the identifier; the upstream project, Git, disclosed fixes and guidance via the project’s GitHub advisory and a coordinated release announcement on the Git team’s Git security vulnerabilities blog post. The U.S. government’s Known Exploited Vulnerabilities catalog later listed the issue as actively exploited, which raised the stakes for timely patching across developer workstations and CI infrastructure.

The bug’s mechanics are straightforward yet subtle. In his detailed write‑up, the vulnerability’s discoverer David Leadbeater explained that Git’s configuration parser removes trailing carriage return and line feed characters as it reads lines, while the serializer fails to quote values that end with a carriage return, making that trailing character disappear on re‑read; the resulting mismatch corrupts the value at precisely the moment it is trusted for submodule initialization, opening a path to misdirected writes and hook execution, as illustrated in the research note on dgl.cx. The fix landed as a small but decisive change to the configuration writer, adding a simple condition to quote any value containing a carriage return so that the reader can no longer drop it; the upstream patch is visible in the Git history in the config: quote values containing CR character commit.

To understand how a configuration quote–unquote defect leads to code running on the client after a clone, trace the normal flow for submodules. The submodule layout is governed by the contents of .gitmodules, where each submodule’s logical path is stored; when a submodule is initialized, Git records a worktree path for it and later spawns a checkout. The vulnerability hinges on a malicious .gitmodules that places a submodule under a path whose last character is a literal carriage return. On read, the parser drops the trailing control character; on write, the serializer, prior to the fix, omitted quotes around that value. When the later checkout reads the now‑unquoted value back in, the carriage return has gone missing and Git directs the submodule’s files into a different location than the one earlier validated. If an attacker has created a symlink from that altered location into the submodule’s hooks directory and planted an executable post‑checkout file in the submodule, the hook runs as soon as the checkout completes. This chain is not theoretical; the advisory describes this exact sequence and the commit message spells it out, including the post‑checkout trigger and the reliance on a symlink into the hooks directory.

The platform nuance matters. Leadbeater’s explanation notes that Windows filesystems typically disallow control characters in filenames, which short‑circuits the path‑confusion primitive there, while Unix‑like systems that permit such characters in pathnames remain exposed; the upstream tests bundled with the patch marked the exploit scenario as inapplicable on Windows, which aligns with the discoverer’s observation and focuses remediation urgency on macOS and Linux. The clarity of this delineation is reinforced by the field analysis from Datadog’s security research team, which describes the bug as an arbitrary file‑write leading to code execution on non‑Windows systems when users clone with recursive submodules, and emphasizes how ordinary developer behavior such as following README instructions to use the recursive flag can become the attacker’s foothold, as documented in the Datadog Security Labs analysis.

What failed under the hood can be stated precisely. As NIST later summarized in the canonical record, Git strips trailing CRLF when reading configuration, but does not quote values ending in a carriage return when writing, and the subsequent read loses that trailing character. The submodule initializer thus calculates and stores a worktree path that does not match what the later checkout will use, because the checkout reads a subtly different value after the CR has been elided. The change that corrects this behavior is captured in the configuration writer: if any character in the value is a carriage return, quote the value so that the reader preserves the trailing CR, erasing the read–write asymmetry. The patch also added tests that simulate the exploit, including creating a symlink from the sanitized path to .git/modules/<name>/hooks and verifying that the post‑checkout hook fails to execute after the fix, a regression guard that will travel with Git’s test suite going forward.

Scope and exposure are broad across release lines. The patched releases span eight maintenance trains, indicating that every supported series required backporting; the project recorded the fix sets in the v2.43.7 line and then merged them into newer lines, with the v2.43.7 notes enumerating the CVEs taken, including 48384, and serving as the canonical pointer for downstream packagers to pick up all related patches in one stop, as captured in the v2.43.7 release notes. Distributions reflected this quickly; Red Hat’s issue tracker registered the identifier with version guidance and tracked the packaging updates for enterprise customers, providing a second point of confirmation for affected versions and fixed builds on the Red Hat CVE page.

Because this is a client‑side supply‑chain risk rather than a long‑lived server process issue, the threat model centers on developer habits and CI defaults. Any workflow that clones untrusted repositories with submodules and does so recursively is a viable target profile. The Git team’s announcement encouraged upgrading immediately across all users, and the advisory outlined an interim workaround of avoiding recursive submodule clones in untrusted contexts until an upgrade can be performed. The configuration file that governs submodules is well documented and the relevant semantics are not obscure, which is partly why the exploit path is so compelling; a crafted .gitmodules is valid syntax and looks benign to a casual reviewer even as its trailing control character sets up the later misdirection.

Public proof‑of‑concept code emerged within days, and exploitation did not remain hypothetical for long. The inclusion of the identifier in the U.S. Known Exploited Vulnerabilities catalog on 2025‑08‑25 came with an agency due date of 2025‑09‑15 for mitigation, a strong policy signal that the flaw was seen in the wild. Independent national authorities also assessed the risk as active, with the United Kingdom’s healthcare cyber operations center issuing an alert that explicitly references exploitation and urges updates on the NHS England cyber‑alert. The combination of coordinated upstream releases, downstream distribution updates, and governmental exploitation warnings is unusual for client‑side Git bugs and speaks to the practical ease of weaponization once a crafted repository is in circulation.

Severity scoring reflects the awkward blend of high impact and nontrivial setup. The GitHub advisory’s CVSS v3.1 base score is 8.1 with a vector of AV:N/AC:H/PR:L/UI:R/S:C/C:H/I:H/A:H, which balances the need for user interaction and symlink preconditions against the high‑impact outcome once the chain is satisfied; the National Vulnerability Database displays the CNA‑supplied metrics as 8.0 in its current presentation of the record, a minor difference that likely reflects rounding or template variations between data feeds rather than a substantive reassessment. The weakness taxonomy underlines the nature of the fault, combining Improper Link Resolution Before File Access with an Interpretation Conflict pattern, mapping neatly onto “link following” via symlink and the reader–writer mismatch in configuration handling.

Two aspects are particularly instructive for system designers and maintainers. First, the vulnerability exploited an interface asymmetry rather than a single function’s defect: the parser reasonably trims CRLF on input, and the serializer reasonably omits quotes in common cases, but their combination across a trust boundary produced a time‑of‑check/time‑of‑use mismatch on a string literal. Second, the attack’s decisive step is not an overwrite of executable code but an abuse of the file‑system plumbing that Git itself uses: a symlink and a valid hook, coupled with the project’s own post‑checkout semantics. That combination is why this issue crosses the line from a mere file‑write quirk to a practical code‑execution vector when developer defaults like recursive submodule cloning are in play.

The chronology is unambiguous. On 2025‑07‑08 the Git project shipped coordinated security releases that included fixes for this issue, as recorded in the release announcement. On 2025‑07‑09 Leadbeater published a technical write‑up that unpacked the root cause and showed how a one‑line change in the configuration writer eliminated the primitive, giving implementers a concrete mental model of the risk. On 2025‑07‑10 Datadog published independent analysis and detection guidance, including confirmation of working proof‑of‑concept code. On 2025‑08‑25 the U.S. KEV listing documented active exploitation and set a federal remediation deadline. These dates line up across the project’s and researchers’ publications and the U.S. government’s catalog entry, and they jointly explain the speed at which distributions and platform vendors moved to ship updated Git binaries.

Operationally, the mitigation guidance is crisp and narrow. Upgrade Git to any of the patched versions enumerated by the project and distributions, and do so on developer machines and any build agents that pull code from the open internet. In environments where an upgrade must be staged, avoid cloning with the recursive submodules flag when the provenance of the repository is not trusted, and read .gitmodules with fresh skepticism, paying attention to control characters that are not visible in default editors. Because this chain ultimately relies on a hook firing, organizations can also review their policies around local hooks in automated build environments and consider standardizing how hooks are injected and executed to reduce ambient risk, using Git’s documented hook mechanisms as the reference for what should and should not be present.

Nothing about the root cause is unique to Git. Any system that both parses and serializes configuration across a trust boundary is at risk when the reader and writer are not perfectly symmetric in how they handle edge characters, especially control characters, Unicode normalization, or platform‑specific path semantics. The fact that a single added condition in a serializer cleared the fault speaks both to how easy a prevention step could have been and how long such a discrepancy can hide when the serialization code appears to do “the obvious thing” and the parser appears to do “the obvious thing” in isolation.

The enduring lesson is that the seams between components are often more dangerous than the components themselves. Here, a config parser and a config writer, both apparently defensible in isolation, combined with submodule initialization and hook semantics to produce an exploitable path. When security reviews instrument these seams—by tracing an untrusted value from ingestion through any write‑back and subsequent re‑read into a privileged action—they are more likely to surface the kinds of interpretation conflicts and link‑following pitfalls that turned a carriage return into a code path.

References

More Analysis

Past Work

Companies We've Worked For & Who Use Our Software

Google Fairfax ASRC Mandrivia Linux Mozilla

Contact

Our schedule’s currently full but drop us a line and we’ll see what we can do.