Jul 25, 2026·8 min read·4 visits
Oh My Posh rendered raw control characters from untrusted sources like directory names and Git commit logs, allowing attackers to hijack system clipboards or execute arbitrary commands upon terminal directory navigation.
Oh My Posh prior to version 29.35.1 is vulnerable to terminal escape sequence injection. Dynamic strings from directory names or Git repository metadata are written to the prompt output without neutralization, enabling unauthenticated remote code execution, clipboard hijacking, or terminal DoS when users navigate to malicious folders.
Oh My Posh is a popular, highly customizable prompt engine used across multiple shells (such as Bash, Zsh, Fish, PowerShell, Command Prompt, and NuShell) to render rich CLI themes. To display context-aware metadata, the engine queries the operating system and Version Control Systems (VCS) to extract active directory paths, Git branch details, and environment status. This dynamic prompt generation operates by formatting and writing text segments directly to standard output (stdout), which is subsequently parsed and rendered by the host terminal emulator.\n\nA terminal escape sequence injection vulnerability (tracked as GHSA-FWJX-9P69-H25H) exists in Oh My Posh prior to version 29.35.1. The flaw stems from the engine writing dynamic strings retrieved from untrusted external sources directly into the prompt rendering buffer without neutralizing raw control characters. An attacker who controls one of these data sources—such as by creating a folder with a malicious name or distributing a Git repository containing an engineered commit subject—can inject arbitrary ANSI or Operating System Command (OSC) control sequences.\n\nWhen a victim navigates to a directory containing the malicious payload or performs standard shell interactions inside an affected folder, Oh My Posh automatically processes the untrusted string. The terminal emulator receives the raw escape sequences, parses them as control directives, and performs automated actions on behalf of the user. This breaks the boundary between dynamic console text and terminal process control, allowing attackers to perform silent clipboard modifications, screen spoofing, or terminal denial-of-service.
The root cause of this vulnerability lies in the character-by-character rendering loop implemented within the terminal writer subsystem of Oh My Posh. In command-line environments, prompt engines write styled text using terminal control codes like ANSI escape sequences. Terminal emulators continually scan the standard output stream for special initiator bytes (such as the C0 Escape character \x1b or C1 control bytes) to alter text color, change the window title, or access advanced host integration protocols.\n\nPrior to the security patch, the character-writing routine did not inspect, sanitize, or filter dynamic text parsed from external context providers before printing to the stdout stream. This processing flow took place within the write(s rune) function in src/terminal/writer.go. While Oh My Posh's internal formatting sequences bypassed this routine through dedicated high-level styling helpers, any text representing the dynamic data of active prompt segments—such as path components, Git log responses, and command outputs—passed straight through to the lower-level writer verbatim.\n\nBecause the host terminal emulator's parsing engine cannot distinguish between layout styling emitted intentionally by Oh My Posh and rogue control sequences embedded in untrusted text fields, the stream became a direct vector for command execution. By placing control codes like the Escape character (\x1b) and the Bell character (\x07) inside directory names or Git metadata, an attacker can bypass the layout constraints and direct the terminal to parse and execute internal commands.\n\nmermaid\ngraph LR\n A["Untrusted Source (Git Log / Folder Name)"] --> B["Oh My Posh Rendering Loop (write())"]\n B --> C["Raw standard output (stdout) with unchecked C0/C1 runes"]\n C --> D["Terminal Emulator Stream Parser"]\n D --> E["Action Trigger (Clipboard Injection / Window Title Spoof)"]\n
To understand the implementation flaw, consider the vulnerable logic within the character writer in src/terminal/writer.go. Prior to the patch, the write function processed character runes without performing sanitization checks on control characters, appending them directly to the active prompt string builder.\n\ngo\n// Vulnerable code in src/terminal/writer.go\nfunc write(s rune) {\n\tif isInvisible {\n\t\t// ...\n\t\treturn\n\t}\n\n\tif isHyperlink {\n\t\tbuilder.WriteRune(s) // Vulnerable write of untrusted link data\n\t\treturn\n\t}\n\n\t// ...\n\tbuilder.WriteRune(s) // Vulnerable write of raw segment runes\n}\n\n\nBecause builder.WriteRune(s) executes on all runes parsed from segment strings, any raw control bytes like \x1b (ESC) or \x07 (BEL) were stored inside the output stream. The security patch introduced in commit edcf3c88f3fb582e84358b385c49d33d04c04224 mitigates this vulnerability by adding an inline sanitization check, isControlRune(s), to drop dangerous character classes before they can reach the builder.\n\ngo\n// Patched code in src/terminal/writer.go\nfunc write(s rune) {\n\tif isInvisible {\n\t\treturn\n\t}\n\n\t// segment content (directory names, git metadata, environment variables,\n\t// command output) is potentially attacker-controlled and never passes through\n\t// this function when it's Oh My Posh's own styling; drop C0/C1 control runes\n\t// (ESC, BEL, CSI, OSC, ...) so they can't be interpreted as escape sequences\n\t// by the terminal, including inside a hyperlink target.\n\tif isControlRune(s) {\n\t\treturn\n\t}\n\n\tif isHyperlink {\n\t\tbuilder.WriteRune(s)\n\t\treturn\n\t}\n\n\tbuilder.WriteRune(s)\n}\n\n// isControlRune reports whether s is a C0 (0x00-0x1F), DEL (0x7F), or C1\n// (0x80-0x9F) control character. These are the bytes a terminal can interpret\n// as the start of an escape sequence; no legitimate rendered segment content needs them.\nfunc isControlRune(s rune) bool {\n\tif s == '\\n' {\n\t\treturn false\n\t}\n\n\treturn s <= 0x1f || (s >= 0x7f && s <= 0x9f)\n}\n\n\nThis boundary check drops all control characters within the C0 range (0x00 - 0x1F), the delete character (0x7F), and the C1 range (0x80 - 0x9F). These control character sets are responsible for initiating terminal instructions like CSI (Control Sequence Introducer) and OSC (Operating System Command). By stripping these bytes, the emulator parses the remaining string as safe, literal text.
Exploitation of GHSA-FWJX-9P69-H25H is highly reliable and can be achieved through two primary vectors: Git metadata poisoning and directory name injection. In a typical scenario, an attacker designs a payload that leverages Operating System Command 52 (OSC 52), which allows standard terminal programs to write to the system clipboard.\n\nTo craft a malicious Git repository, an attacker creates an empty repository and structures a commit subject with the necessary terminal command escape sequences. The sequence uses \033] to open an OSC control channel, 52;c; to direct a clipboard copy, a base64-encoded command string, and the \007 (BEL) byte to close the channel.\n\nbash\n# Construct a malicious commit payload\n# OSC 0 sets the terminal title to "HACKED"\n# OSC 52 writes "echo PWNED" into the clipboard buffer\nprintf 'feat: \033]0;HACKED\007\033]52;c;ZWNobyBQV05FRA==\007 update' > msg.txt\n\ngit commit --allow-empty -F msg.txt\n\n\nWhen a victim navigates to this directory, Oh My Posh executes internal commands to build the Git status segment (such as extracting the last commit title via git log). This output is fed into the rendering engine, passing the raw control bytes to stdout. As the terminal emulator processes the stream, the OSC 52 sequence executes, instantly writing the command echo PWNED to the host's system clipboard. If the user subsequently performs a paste operation, the payload executes inside their active shell.\n\nA secondary attack vector targets the file system directly on Unix-based systems. Because macOS and Linux filesystems permit the use of raw control characters within directory names, an attacker can create nested directories containing injection sequences. When the victim navigates into the compromised folder structure using the cd command, the path rendering segment processes the raw escape sequences, triggering the payload execution.
The security implications of GHSA-FWJX-9P69-H25H are significant due to the scope change (S:C in CVSS metrics) inherent to terminal escape sequence injections. Because the execution transitions from the prompt layout parser to the host terminal emulator, the attack bypasses shell boundaries and impacts the underlying operating system. The vulnerability has been assigned a CVSS v3.1 score of 6.3.\n\nThe primary operational risk is silent clipboard manipulation. Attackers can overwrite the victim's host clipboard with complex, malicious shell scripts. Given that developers and system administrators frequently paste commands, configuration snippets, and credentials, an attacker can reliably achieve arbitrary command execution. This technique does not require user validation beyond standard navigation or paste actions.\n\nAdditionally, attackers can execute terminal-based denial of service (DoS) and visual spoofing. By sending terminal control sequences that manipulate terminal windows (such as OSC 0 or OSC 2), attackers can change window titles to spoof running programs. They can also use ANSI sequence loops to corrupt terminal buffers, rendering the command line unresponsive and forcing the victim to terminate their session, potentially losing unsaved work.
To remediate this vulnerability, users must update Oh My Posh immediately to version 29.35.1 or newer. Updates can be applied via major package managers across supported environments. For systems where binary updates cannot be executed immediately, administrators should implement defensive configuration changes to limit the impact.\n\nbash\n# Update via Homebrew (macOS/Linux)\nbrew update && brew upgrade oh-my-posh\n\n# Update via winget (Windows)\nwinget upgrade JanDeDobbeleer.OhMyPosh\n\n\nIf upgrading is not immediately possible, users can apply configuration-level workarounds. Modifying active Oh My Posh configuration JSON templates to remove dynamic segments—specifically the git and path blocks—removes the primary attack surfaces. Additionally, terminal emulators should be configured to disable clipboard integration. In tmux, this can be done by adding set -s set-clipboard off to the .tmux.conf file, and in iTerm2, by disabling the option 'Applications in terminal may access clipboard'.\n\nAn evaluation of the fix implemented in version 29.35.1 indicates that while it successfully blocks the execution of dangerous control sequences, some structural limitations remain. Specifically, the newline character \\n is explicitly exempted in isControlRune. An attacker can exploit this exception to inject line breaks into directory names or Git logs. This allows for prompt-spoofing attacks, where a modified output pushes the authentic prompt out of view and displays a fake console layout to capture input or credentials. Security teams should remain aware of these residual layout manipulation vectors.
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
oh-my-posh JanDeDobbeleer | < 29.35.1 | 29.35.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-150 |
| Attack Vector | Local |
| CVSS Score | 6.3 |
| EPSS Score | N/A (No associated CVE) |
| Impact | Scope Change, Clipboard Hijacking, Potential Code Execution |
| Exploit Status | Proof-of-Concept (PoC) |
| KEV Status | Not Listed |
The application processes inputs containing control sequences and outputs them to a terminal stream without proper neutralization, allowing arbitrary terminal command execution.
A high-severity algorithmic-complexity vulnerability in the Node.js library Shescape leads to a Quadratic-Time Denial of Service (DoS) when flag protection is enabled. By supplying inputs containing repetitive control characters and hyphens, remote attackers can trigger an inefficient nested loop in Shescape's argument-composition logic, blocking the Node.js single-threaded event loop and causing total application Denial of Service.
A multi-vector vulnerability advisory in the OmniFaces JavaServer Faces (JSF) utility library covers flaws leading to unauthenticated arbitrary file access, memory exhaustion (DoS), cross-site scripting (XSS), and WebSocket push channel hijacking.
A critical-severity template injection vulnerability in Oh My Posh allows for unauthenticated arbitrary command execution. This issue occurs when a user navigates their shell into a directory whose name contains malicious Go template syntax, which is subsequently parsed and executed by the prompt engine.
A prototype pollution vulnerability (CWE-1321) in the Quasar framework's utility function 'extend' allows unauthenticated remote attackers to modify the global Object.prototype. This vulnerability can lead to application-level logic bypasses, denial of service, or potentially arbitrary code execution depending on downstream implementation.
A critical connection-level Denial of Service (DoS) vulnerability exists in the Yamux stream multiplexer implementation of py-libp2p (versions <= 0.6.0). The flaw allows unauthenticated or authenticated peers to permanently stall a Yamux multiplexed connection by transmitting a single malformed 12-byte header claiming an oversized payload while withholding the payload bytes.
An unauthenticated remote resource exhaustion vulnerability in Amazon aws-smithy-http-server enables denial-of-service (DoS) attacks. Affected versions do not enforce connection limits or header timeouts, allowing standard Slowloris techniques to block server operations.