Why was this disclosed before the hole was patched in the stable release?
It's only been 18 days since the bug was reported to upstream, which is much shorter than typical vulnerability disclosure deadlines. The upstream commit (https://github.com/gnachman/iTerm2/commit/a9e745993c2e2cbb30...) has way less information than this blog post, so I think releasing this blog post now materially increases the chance that this will be exploited in the wild.
Update: The author was able to develop an exploit by prompting an LLM with just the upstream commit, but I still think this blog post raises the visibility of the vulnerability.
If publicly accessible AI model with very cheap fee can find it, it's very natural to assume the attackers had found it already by the same method.
Poof went the operating system!
Thanks, saved me some reading time.
https://blog.mozilla.org/security/2019/10/09/iterm2-critical...
If I wrote my own version of cat in C, simply reading and displaying a single TXT character at a time, wouldn't I see the same behavior?
alias cat
cat='strings -a --unicode=hex'I don't know what to do with this. I think there's this problematic tension between the expectation that on one hand, basic OS-level tools should remain simple and predictable; but on the other hand, that of course we want to have pretty colors, animations, and endless customization in the terminal.
And of course, we're now adding AI agents into the mix, so that evil text file might just need to say "disregard previous instructions and...".
Like why doesn't `println` in a modern language like rust auto-escape output to a terminal, and require a special `TerminalStr` to output a raw string.
LLM is a tool, but people still need to know — what where how.
It is a problem in iterm, Apple's overlay, not in the cat program. Program. At least from Reading the article. That's what I got
The problem with this is that the credible information "there's a bug in widely used tool x" will soon (if not already) be enough to trigger massive token expenditure of various others that will then also discover the bug, so this will often effectively amount to disclosure.
I guess the only winning move is to also start using AI to rapidly fix the bugs and have fast release cycles... Which of course has a host of other problems.
From the article,
>trust failure
And from you,
>...of course we want to have pretty colors...
And from me, [allegorically] sounds oddly like a certain immigration problem America has been arguing about.
And back to the subject-matter rigor that HN demands, none of this matters when you've got competent engineers that understand security and good management that keeps it all together.
But there's a fool born every minute, even in tech, so we (I was a security sales engineer) get to keep scamming companies into buying whatever we promise will solve all your problems!
I think the real solution is that you shouldn't try to bolt colors, animations, and other rich interactivity features onto a text-based terminal protocol. You should design it specifically as a GUI protocol to begin with, with everything carefully typed and with well-defined semantics, and avoid using hacks to layer new functionality on top of previously undefined behavior. That prevents whatever remote interface you have from misinterpreting or mixing user-provided data with core UI code.
But that flies in the face of how we actually develop software, as well as basic economics. It will almost always be cheaper to adapt something that has widespread adoption into something that looks a little nicer, rather than trying to get widespread adoption for something that looks a little nicer.
If we can get that to raise a red flag with people (and agents), people won’t be trying to put control instructions alongside user content (without considering safeguards) as much.
Consider cat. It's short for concatenate. It concatenates the files based to it as arguments and writes them to stdout, that may or may not be redirected to a file. If it didn't pass along terminal escapes, it would fail at its job of accurate concatenation.
Now I don't mean to dismiss your idea, I do think you are on the right track. The question is just how to do this cleanly given the very entrenched assumptions that lead us where we are.
https://utcc.utoronto.ca/~cks/space/blog/sysadmin/OnTerminal...
This is usually knowable.
It's a different question whether cat should be doing that, though – it's an extremely low level tool. What's wrong with `less`? (Other than the fact that some Docker images seem to not include it, which is pretty annoying and raises the question as to whether `docker exec` should be filtering escape sequences...)
In a previous post about AI-discovered bugs in Vim and Emacs, we looked at how seemingly harmless workflows could cross a surprising line into code execution. This time we wanted to push that idea even further: is cat readme.txt safe?
It turns out that it is NOT, if you use iTerm2.
That looks insane until you understand what iTerm2 is trying to do for a legitimate feature, how it uses the PTY, and what happens when terminal output is able to impersonate one side of that feature's protocol.
We'd like to acknowledge OpenAI for partnering with us on this project.
iTerm2 has an SSH integration feature that gives it a richer understanding of remote sessions. To make that work, it does not just "blindly type commands" into a remote shell. Instead, it bootstraps a tiny helper script on the remote side called the conductor.
The rough model is:
iTerm2 launches SSH integration, usually through it2ssh.
iTerm2 sends a remote bootstrap script, the conductor, over the existing SSH session.
That remote script becomes the protocol peer for iTerm2.
iTerm2 and the remote conductor exchange terminal escape sequences to coordinate things like:
discovering the login shell
checking for Python
changing directories
uploading files
running commands
The important point is that there is no separate network service. The conductor is just a script running inside the remote shell session, and the protocol is carried over normal terminal I/O.
A terminal used to be a real hardware device: a keyboard and screen connected to a machine, with programs reading input from that device and writing output back to it.
A terminal emulator like iTerm2 is the modern software version of that hardware terminal. It draws the screen, accepts keyboard input, and interprets terminal control sequences.
But the shell and other command-line programs still expect to talk to something that looks like a real terminal device. That is why the OS provides a PTY, or pseudoterminal. A PTY is the software stand-in for the old hardware terminal, and it sits between the terminal emulator and the foreground process.
In a normal SSH session:
iTerm2 writes bytes to the PTY
the foreground process is ssh
ssh forwards those bytes to the remote machine
the remote conductor reads them from its stdin
So when iTerm2 wants to "send a command to the remote conductor," what it actually does locally is write bytes to the PTY.
The SSH integration protocol uses terminal escape sequences as its transport.
Two pieces matter here:
DCS 2000p is used to hook the SSH conductor
OSC 135 is used for pre-framer conductor messages
At source level, DCS 2000p causes iTerm2 to instantiate a conductor parser. Then the parser accepts OSC 135 messages like:
begin <id>
command output lines
end <id> <status> r
unhook
So a legitimate remote conductor can talk back to iTerm2 entirely through terminal output.
The bug is a trust failure. iTerm2 accepts the SSH conductor protocol from terminal output that is not actually coming from a trusted, real conductor session. In other words, untrusted terminal output can impersonate the remote conductor.
That means a malicious file, server response, banner, or MOTD can print:
a forged DCS 2000p hook
forged OSC 135 replies
and iTerm2 will start acting like it is in the middle of a real SSH integration exchange. That is the exploit primitive.
The exploit file contains a fake conductor transcript.
When the victim runs:
iTerm2 renders the file, but the file is not just text. It contains:
a fake DCS 2000p line that announces a conductor session
fake OSC 135 messages that answer iTerm2's requests
Once the hook is accepted, iTerm2 starts its normal conductor workflow. In upstream source, Conductor.start() immediately sends getshell(), and after that succeeds it sends pythonversion().
So the exploit does not need to inject those requests. iTerm2 issues them itself, and the malicious output only has to impersonate the replies.
The fake OSC 135 messages are minimal but precise.
They do this:
Start a command body for getshell
Return lines that look like shell-discovery output
End that command successfully
Start a command body for pythonversion
End that command with failure
Unhook
This is enough to push iTerm2 down its normal fallback path. At that point, iTerm2 believes it has completed enough of the SSH integration workflow to move on to the next step: building and sending a run(...) command.
The forged DCS 2000p hook contains several fields, including attacker-controlled sshargs.
That value matters because iTerm2 later uses it as command material when it constructs the conductor's run ... request.
The exploit chooses sshargs so that when iTerm2 base64-encodes:
run
the last 128-byte chunk becomes:
ace/c+aliFIo
That string is not arbitrary. It is chosen because it is both:
valid output from the conductor encoding path
a valid relative pathname
In a legitimate SSH integration session, iTerm2 writes base64-encoded conductor commands to the PTY, and ssh forwards them to the remote conductor. In the exploit case, iTerm2 still writes those commands to the PTY, but there is no real SSH conductor. The local shell receives them as plain input instead.
That is why the session looks like this when recorded:
getshell appears as base64
pythonversion appears as base64
then a long base64-encoded run ... payload appears
the last chunk is ace/c+aliFIo
Earlier chunks fail as nonsense commands. The final chunk works if that path exists locally and is executable.
You can reproduce the original file-based PoC with genpoc.py:
This creates:
ace/c+aliFIo, an executable helper script
readme.txt, a file containing the malicious DCS 2000p and OSC 135 sequences
The first fools iTerm2 into talking to a fake conductor. The second gives the shell something real to execute when the final chunk arrives.
For the exploit to work, run cat readme.txt from the directory containing ace/c+aliFIo, so the final attacker-shaped chunk resolves to a real executable path.
Mar 30: We reported the bug to iTerm2.
Mar 31: The bug was fixed in commit a9e745993c2e2cbb30b884a16617cd5495899f86.
At the time of writing, the fix has not yet reached stable releases.
When the patch commit landed, we tried to rebuild the exploit from scratch using the patch alone. The prompts used for that process are in prompts.md, and the resulting exploit is genpoc2.py, which works very similarly to genpoc.py.
No posts