CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

CVE-2025-67746
CVSS 1.3|EPSS 0.04%

Terminal Deception: Inside CVE-2025-67746 in Composer

Amit Schendel
Amit Schendel
Senior Security Researcher•December 30, 2025•5 min read
No Known ExploitNot in KEV

Executive Summary (TL;DR)

Composer, the ubiquitous PHP dependency manager, was failing to sanitize remote package metadata before printing it to the console. This allowed attackers to inject ANSI escape sequences into package names or descriptions. While the CVSS score is a measly 1.3, the practical implication is that a malicious package could rewrite your terminal history, hide critical security warnings during a `composer audit`, or even crash your terminal emulator. The fix involves a hefty regex to strip control characters.

A deep dive into how ANSI sequence injection allows malicious PHP packages to manipulate your terminal output, hiding warnings and spoofing success messages.

The Hook: Don't Believe Everything You Read

We tend to treat our terminals as the ultimate source of truth. When ls says a file is there, it's there. When git says a branch is clean, it's clean. And when composer tells us it's installing a package, we believe it. But what happens when the text appearing on your black-and-green screen isn't coming from the system, but directly from a malicious actor on the other side of the internet?

Enter CVE-2025-67746. It’s a classic tale of "Trust, but verify," except Composer skipped the verify part. For years, the PHP dependency manager has been dutifully fetching package metadata—names, descriptions, abandonment warnings—and piping them directly to your stdout. It’s a feature, right? You want to see formatted text. You want colorful output.

But here's the kicker: standard output isn't just a text stream; it's an interpreter. Modern terminal emulators are complex beasts that parse ANSI escape codes to change colors, move the cursor, and clear lines. By trusting remote input, Composer inadvertently gave package maintainers remote control over your cursor. It’s like inviting a stranger into your house to paint the walls, and then being surprised when they paint over your windows.

The Flaw: The Magic of Escape Codes

At its core, this vulnerability is a specific flavor of CWE-74: Improper Neutralization of Injection. The injection here isn't SQL or HTML; it's ANSI (American National Standards Institute) escape sequences. These are the byte sequences that tell your terminal, "Make the next word red," or "Move the cursor up three lines and delete everything to the right."

Composer fetches data from repositories (like Packagist or private repos). This data includes strings like package descriptions. Before version 2.2.26 (or 2.9.3), Composer treated these strings as sacred, immutable text. It took the bytes from the JSON metadata and passed them straight to the IO interface, which dumped them to your screen.

If I control a package repository, or if I can convince you to use my fork of a library, I can put anything in that description. If I put \033[2J, your terminal clears the screen. If I put \033[1A, your cursor moves up. By chaining these commands, I can effectively rewrite history—literally overwriting the lines Composer just printed.

The Code: Regex to the Rescue

The fix isn't rocket science, but it is necessary plumbing. The maintainers had to introduce a sanitization layer that sits between the data and the display. In src/Composer/IO/ConsoleIO.php, they added a sanitize() method. This method is now called on almost everything that hits the console—audit reports, package descriptions, and even interactive questions.

Here is the regex beast they unleashed to kill the injection:

// The pattern identifies CSI, OSC, and general ESC sequences
$escapePattern = '\x1B\[[\x30-\x3F]*[\x20-\x2F]*[\x40-\x7E]|\x1B\].*?(?:\x1B\\\\|\x07)|\x1B.';
 
// Applying the filter to strip control characters and escape codes
$pattern = $allowNewlines 
    ? "{{$escapePattern}|[\x01-\x09\x0B\x0C\x0E-\x1A]|\r(?!\n)}u" 
    : "{{$escapePattern}|[\x01-\x1A]}u";
 
return preg_replace($pattern, '', $message);

[!NOTE] Notice the specific handling of \r(?!\n). A naked Carriage Return is a favorite tool for attackers because it moves the cursor to the start of the current line without advancing, allowing them to overwrite existing text. This regex nukes it unless it's paired with a newline.

The patch ensures that write(), writeError(), and overwrite() methods filter their input. It transforms potentially active content into harmless plain text.

The Exploit: Jedi Mind Tricks

You might be looking at the CVSS score of 1.3 and thinking, "Why do I care? It's not RCE." You're right, it's not Remote Code Execution. It's Remote Reality Execution. The danger here is Social Engineering and obfuscation.

Imagine you run composer audit in your CI pipeline or local machine. Composer detects a critical vulnerability in library-x. Normally, it prints:

Found 1 vulnerability:
- library-x: Critical RCE (CVE-202X-XXXX)

Now, imagine the attacker who maintains library-x updates their composer.json description to include ANSI codes. They inject a sequence that moves the cursor up two lines, clears the text, and prints green text saying "No vulnerabilities found."

Your terminal output now looks like this:

[+] Audit passed. No vulnerabilities found.

You commit the code. You deploy. You get hacked. The exploit didn't break your server; it broke your trust in your tools. It tricked the human operator. In a world where we rely on automated scanners and quick glances at terminal logs, the ability to make red warnings look like green success messages is a potent weapon.

The Fix: Scrubbing the Input

The mitigation is straightforward: Update. The PHP community is generally fast at rolling out these things.

If you are on the 2.2 LTS branch: Ensure you are on 2.2.26 or higher.

If you are on the current 2.x branch: Ensure you are on 2.9.3 or higher.

Run the magic command:

composer self-update

If you are stuck on an old version for some legacy reason (we've all been there), you are vulnerable to any package source you have configured. If you only use Packagist, the risk is lower because Packagist likely does its own sanitization or vetting, but if you use private Satis instances or pull from Git repositories directly, you are trusting every byte of metadata those sources provide.

Official Patches

ComposerComposer 2.9.3 Release Notes
ComposerComposer 2.2.26 Release Notes

Fix Analysis (2)

Technical Appendix

CVSS Score
1.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N
EPSS Probability
0.04%
Top 88% most exploited

Affected Systems

Composer 2.0.0 < 2.2.26Composer 2.3.0 < 2.9.3

Affected Versions Detail

ProductAffected VersionsFixed Version
Composer
Composer
>= 2.0.0, < 2.2.262.2.26
Composer
Composer
>= 2.3.0, < 2.9.32.9.3
AttributeDetail
CWE IDCWE-74
Attack VectorNetwork (Package Metadata)
CVSS Score1.3 (Low)
ImpactIntegrity (Output Manipulation)
Exploit StatusPoC Possible (No Active Exploitation)
Affected ComponentComposer ConsoleIO

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1036Masquerading
Defense Evasion
T1491Defacement
Impact
CWE-74
Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection')

The software constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as commands when sent to a downstream component.

Vulnerability Timeline

Vulnerability Timeline

Vulnerability Discovered
2025-01-XX
Patches Released (2.2.26, 2.9.3)
2025-02-XX
CVE-2025-67746 Assigned
2025-02-XX

References & Sources

  • [1]GitHub Security Advisory: ANSI Injection in Composer

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.