Back to Home

Evil merge in Git: malware in merge commit

The article describes an attack via evil merge in Git: injection of obfuscated code into merge commit. Payload uses blockchain for C2. A detector in Go is presented for automated repository scanning.

Malicious evil merge: 3.5 months in the code
Advertisement 728x90

Evil Merge: Malicious Code Hidden in Git Merge Commits

Malicious, obfuscated code was discovered in a project's vite.config.js file. It wasn't introduced in a pull request (PR), but directly within a merge commit during conflict resolution. GitHub's PR view only shows the diff from the feature branch, ignoring changes made in the merge commit itself. Analysis using git log revealed a merge commit with identical parent commits but altered file content.

MD5 hashes of the parent commits matched:

  • Parent 1: aa82acb0c335430d8300b6cb306dc824 (clean)
  • Parent 2: aa82acb0c335430d8300b6cb306dc824 (clean)
  • Merge: 2a54754defae4d13aab39f256738dbbf (modified)

In a three-way merge, Git should produce an unchanged result when the parent files are identical. Any differences indicate manual editing during the merge process.

Google AdInline article slot

Injection and Obfuscation Mechanism

The malicious payload was injected into two files across different modules. The contributor added the payload when clicking the merge button in the PR. The code executed during npm run dev and npm run build for 3.5 months.

Obfuscation techniques included:

  • Reconstructing strings like require, module, constructor by scrambling them with a numeric seed.
  • Using Function instead of eval to bypass static analysis.
  • A custom decoder using a character substitution table.

The payload contained no URLs or IP addresses. Instead, it:

Google AdInline article slot
  • Queried the latest transaction of a TRON wallet.
  • Extracted a BSC transaction hash.
  • Made a JSON-RPC eth_getTransactionByHash call to a BSC node.
  • XOR-decoded the code from the transaction's input field.

Aptos was used as a fallback if TRON was unavailable. Execution used child_process.spawn with detached: true and stdio: 'ignore', and a 30-second timer to counter watch-mode detection.

Reasons It Went Unnoticed

  • GitHub does not display diffs for merge commits in PRs.
  • git log doesn't reveal changes; you need git diff <parent1>..<merge>.
  • The code was shifted beyond the 200th column using whitespace.
  • SAST tools failed due to lack of signatures: no eval, URLs, or base64.

This technique is known as an evil merge — a merge that introduces changes not present in either parent (gitglossary(7)). It was described by a Git maintainer in 2013, but few detection tools exist.

Evil Merge Detector: Automated Scanner

A detector written in Go compares the expected three-way merge result (via a common ancestor) with the actual merge commit. Discrepancies signal manual edits.

Google AdInline article slot

Usage:

evilmerge scan /path/to/repo

Options:

  • --format=sarif for GitHub Code Scanning.
  • Severity levels: manual conflict resolutions vs. edits without conflicts.

Integrations:

  • GitHub Action:

```yaml

- uses: fimskiy/Evil-merge-detector@v1

with:

fail-on: warning

```

  • GitHub App: PR checks and retrospective history scans.
  • Templates for GitLab, Bitbucket, and self-hosted setups (pre-receive hooks).

Mitigations and Workflow Vulnerabilities

Attacks via contributor access: legitimate commits followed by injection during a merge. The PR itself remains clean.

Recommendations:

  • Use a linear history (squash/rebase).
  • Implement pre-receive hooks on the server.
  • On GitHub: enable "Dismiss stale reviews" to require re-review if the PR branch changes.

GitHub confirmed this is intentional design, with possible future restrictions. Current measures focus on prevention, not detection.

Key Takeaways

  • Evil merge is the official term for manual edits in merge commits.
  • The payload uses blockchain (TRON/BSC/Aptos) for command-and-control without URLs/IPs.
  • The Go-based detector scans history and integrates into CI/CD.
  • 3.5 months of undetected activity in a project with CI and code review.
  • GitHub has no immediate plans to change the merge workflow.

— Editorial Team

Advertisement 728x90

Read Next