Skip to content

Add YARA rule for CVE-2025-48384 (git submodule CR injection)#1540

Open
arpitjain099 wants to merge 1 commit into
chainguard-dev:mainfrom
arpitjain099:feat/yara-cve-2025-48384
Open

Add YARA rule for CVE-2025-48384 (git submodule CR injection)#1540
arpitjain099 wants to merge 1 commit into
chainguard-dev:mainfrom
arpitjain099:feat/yara-cve-2025-48384

Conversation

@arpitjain099
Copy link
Copy Markdown

Summary

Adds a YARA rule that detects the .gitmodules exploitation primitive for CVE-2025-48384 (GHSA-vwqx-4fm8-6qc9), the recent git submodule carriage-return vulnerability.

git strips a trailing CR (0x0D) when reading a config value, but does not quote that CR when writing the value back. Because of this asymmetry a submodule whose path ends in a CR is checked out to a different location than the one git validated. Combined with a symlink pointing into .git/hooks, an attacker can land a post-checkout hook that executes during git clone --recursive of an untrusted repository. The rule flags the malicious config artifact rather than the runtime behavior, which is what malcontent sees when it scans a repo's .gitmodules.

Fixes #1127.

Placement and convention

I followed the existing exploit-rule convention. The rule lives at rules/impact/exploit/cve-2025-48384.yara, next to the other CVE/exploit rules in that directory (cve.yara, pwnkit.yara, and so on), and uses the same rule name: <severity> tag form the repo uses elsewhere. Severity is high, matching the advisory's CVSS 8.0 / High rating. Metadata mirrors the style already in the tree (description, cve, reference, advisory, repository, date).

The issue pointed at a Yara-Rules basis for this CVE. I used the same idea (a submodule path/url value carrying a CR) but reworked the strings and condition to be tighter on false positives and to fix a couple of escaping issues in the original regex.

Detection logic

The rule requires a [submodule " section and then one of two signals:

  • An embedded CR in the middle of a path/url value (a CR followed by more value text rather than a line terminator). This is the unambiguous smuggling shape and cannot occur in a benign LF or CRLF file, so it stands on its own.
  • A path/url value with a trailing CR, but only when the file also contains at least one bare-LF line ending. Mixed line endings are the on-disk fingerprint of a CR injected into an otherwise-LF .gitmodules. This guard is what keeps a uniformly CRLF (Windows) .gitmodules from matching, which a naive trailing-CR check gets wrong.

There is a filesize < 64KB bound since .gitmodules files are small.

False-positive considerations

  • A clean LF .gitmodules does not match.
  • A uniformly CRLF .gitmodules (Windows checkout, every line ends \r\n) does not match, thanks to the bare-LF guard on the trailing-CR branch.
  • A file with path = ...\r that is not inside a submodule section does not match.
  • One residual edge remains: a CRLF .gitmodules that also contains a stray bare-LF line (for example a blank line saved as LF) can still trip the trailing-CR branch. A single path = x\r\n line is byte-identical between the malicious lone-CR case and one line of a CRLF file, so this ambiguity is inherent to detecting the trailing-CR variant from file bytes alone. I kept the trailing-CR branch because the advisory describes the primitive as a trailing CR, and the embedded-CR branch alone would miss it; the bare-LF guard reduces the FP surface to this narrow mixed-ending case. Open to dropping the trailing-CR branch and detecting only the embedded-CR shape if you would prefer zero residual FPs over completeness.

Validation

I did not add an integration test under tests/, because that path needs a sample committed to chainguard-sandbox/malcontent-samples plus make refresh-sample-testdata to generate the authoritative .simple expectation, which I cannot do from a fork. Happy to follow up with a sample PR to the samples repo if you want the rule covered there.

What I did run locally with the repo's pinned yara-x (v1.17.0, the yr binary the Makefile downloads):

  • make yara-x-compile passes with the rule added (whole rules/ tree, warnings enabled).
  • make yara-x-fmt produces no diff, so the rule is already in canonical format.
  • yr scan against hand-built samples: the rule fires on the PoC-style quoted trailing-CR path, an unquoted trailing-CR path, a trailing-CR url, an embedded mid-value CR, and a multi-submodule mixed-ending file; and it does not fire on a clean LF file, a single-line CRLF file, a realistic multi-submodule CRLF file, or a non-.gitmodules file containing path = ...\r.

References

Detects a .gitmodules submodule path or url value that carries a smuggled
carriage return, the exploitation primitive for CVE-2025-48384. git strips a
trailing CR when reading a config value but does not quote it when writing, so
a submodule whose path ends in CR is checked out to a different location than
the one validated; paired with a symlink into .git/hooks this runs a
post-checkout hook during a recursive clone.

Placed under rules/impact/exploit/ alongside the existing CVE exploit rules.
The rule matches an embedded CR mid-value on its own (zero false positives on
benign LF or CRLF files) and a trailing CR only when the file also has bare-LF
line endings, the on-disk fingerprint of a CR injected into an otherwise-LF
.gitmodules, so a uniformly CRLF Windows checkout does not match.

Refs: GHSA-vwqx-4fm8-6qc9
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Git's CVE-2025-48384 - YARA rule

1 participant