Don’t Trust This Title: Abusing Terminal Emulators with ANSI Escape Characters

January 6, 2022 Eviatar Gerzi


One day, while I was working on OpenShift, a Kubernetes distribution by RedHat focused on developer experience and application security, I noticed that I was able to inject ANSI escape characters to components in the web application. When a user reads the data with the injected ANSI escape characters, it executes the injected commands — in my case, changing the color of the terminal. I thought it was interesting but wasn’t sure if it was something serious.

Back then, my knowledge about ANSI escape characters was mostly about changing colors. I wondered if there was a vulnerability in the parsing of the colors that could lead to executing arbitrary commands. Beyond my initial curiosity, I didn’t immediately pursue further investigation.

When the time came to start new research, I remembered the issue I saw in OpenShift and thought it could be interesting to investigate. This is what led me to learn about ANSI escape characters. Frankly, I didn’t understand what I was getting into. I found that there were many types of ANSI escape characters codes, which made me decide that this research could be very interesting.

In this blog post, I am going to show you how research on terminal emulators took a different turn when I found a remote denial of service (DoS) vulnerability by abusing a Windows system call indirectly. We will show how this issue further affects other components, which may surprise you. This research led us to a total of nine vulnerabilities in different terminals. Buckle up; we are starting now.


This research led to:

Terminal Emulators

Almost everyone who works with a computer has had the chance to use a terminal emulator. A terminal emulator (Figure 1) is a computer program that mimics a video terminal with access to a local or distant host. This is the black window that you typically see in hacker movies. The terminal emulator provides us with only a screen and a set of commands to run on the system. It removes all the overhead of the graphics and makes things faster and efficient in case we need to do specific tasks that don’t require the use of graphics. We can look at it as a text-based system for navigating through the operating system.

PuTTY Terminal Emulator

Figure 1 – PuTTY Terminal Emulator


Terminal emulators have been around since the beginning of operating systems. Before PCs, people used terminals, which were physical machines that looked like PCs and were used in universities in the 1970s. One of them was the VT100. Terminals had a monitor and keyboard, but they were linked to a large mainframe in server space.

Today we still have terminals — for example, the default CMD terminal in Windows and bash and sh (bourne shell) in Linux. But there are a lot more like PuTTY, MobaXterm, Terminator and so on. Which brings us to the question: What would happen if there were a critical vulnerability in one of them?

The problem is that it can affect anyone who uses terminals to connect to a remote server. This can range from private users to IT admins, developers and so on, meaning — lots of users.

Searching for Attack Vectors

I started by checking the kind of attacks that have been executed against terminals by searching for related CVEs. This provides a historical view of potential risk and influences the development of new approaches, including new attack vectors.

The standard way to access a terminal begins when a user starts a terminal and connects to a server, so the attack vectors that I focused on were targeting the client:

  1. Assuming we are inside a server, create a malicious string inside a file that exploits a vulnerability in the terminal, when printed, to run code unintentionally by the user. When the user connects to the terminal and opens this file, it will run an arbitrary command. Another result can be remote code execution on the client’s host.
  2. A vulnerability in the connection between that client terminal to the SSH server will eventually allow running code on the client’s host.

I decided to start with the first attack vector because it sounded easier. I began with an open-source terminal like PuTTY (0.74) because this is a common terminal that I can debug easily.

How Terminals Can Be Abused

There is an interesting advisory from 2003 by Digital Defense Incorporated about terminal emulator security issues, which did a good review of the issues we are facing. Some of these security issues are also described in the “A Blast From the Past” article, so I won’t cover all of them — only the one I used in my research.

In my case, I wanted to find a problem in the way the terminal parses text, which led me to learn about ANSI escape characters.

ANSI Escape Characters

These are special characters that the terminal won’t read as regular text. It will be read like a command. It will usually start with the ASCII escape character ESC (0x1B in hexadecimal) and followed by a specific set of arguments. These are control characters that can change text attributes (colors), move the cursor position, change the window title and so on. It is divided into categories that are elaborated here.

For example, there is the OSC (Operating System Command) sequences category with commands beginning with ESC ], followed by a set of commands, and ends with BEL (Bell 0x7) or ST (String Terminator 0x9C) sign (Figure 2).

We can change the window title of our terminal by printing this string: \e]0;Title\a.
Analyze of OSC sequence

Figure 2 – Analysis of OSC sequence

Notice that it won’t work in some terminals because of the .bashrc file that loads the title every time your press ENTER, so you will need to disable it from this file first.

You might think, “It’s only a title; what are you going to do, title me?” Well, notice these eight vulnerabilities from 2003 (Figure 3) in the window title that allowed code execution on the terminal. Because of the similarity of the terminals, one vulnerability could affect multiple terminals.

Vulnerabilities-in-the-modification of window title

Figure 3 – Vulnerabilities in the modification of window title (taken from MITRE)

Learning from the Past: Code Execution by Modifying the Windows Title

I was searching for a bug in ANSI characters that respond to a change of the window’s title. One of the things I did was check how the previous bugs were exploited. One of them, CVE-2015-8971 (found by Nicolas Braud-Santoni), was a bug in Terminology 0.7.0 that didn’t filter new line (\n) escape character when changing the window title. It allowed you to modify the window title and then re-insert it into the terminal’s input buffer, resulting in arbitrary terminal input, which then caused code execution. The malicious string (Figure 4) could be inserted into a file, and when the user opened the file, it would load this string:

\e]2;echo 'evil'\n\a\e]2;?\a

Analyze of code execution vulnerability in window title

Figure 4 – Analysis of code execution vulnerability in window title

When this malicious string is being printed, the string echo ‘evil’\n gets written to the user’s terminal’s input buffer, resulting in that command being executed by the user’s shell.

From Changing Window Title to DoS

The code execution vulnerability gave me several ideas to try. I opened a terminal to a remote Linux server and tried some weird ANSI combinations, logical hacks, corruptions, you name it — but none of them worked on the window title. At this point, I became a little bit dejected because it seemed that my other option was to move forward to another attack vector although I felt that the potential of exploiting ANSI escape characters is larger. I just needed to find the way.

It’s when I needed to go to a meeting, seconds from locking the machine when I thought to run a piece of code that expressed my feelings. I wrote a code that changes the window title … all the time using an infinite loop:

perl -e 'while(1){print "\e]0;pwn\a"};'

I told the terminal, “take that,” and left for the meeting.

When I came back from the meeting, something was strange. Everything was frozen, except for my mouse, which is also known as the “White Screen of Death” (WSOD). I couldn’t work. The only option was to restart the computer. I was intrigued by why changing the terminal title affected the whole computer, making it impossible to do anything.

Analyzing SetWindowText Behavior

Remember that I was working on with PuTTY; I checked what function is being used to change the window title and found that there is a function do_osc (in terminal.c) that processes the OSC sequences and in one of the conditions calls win_set_title that will eventually call wintw_set_title (Figure 5).

Figure 5 – wintw_set_title function from window.c in PuTTY project

We can see that the title is being changed by the macro SetWindowText (Figure 6), which can be SetWindowTextW or SetWindowTextA:

Figure 6 – SetWindowText macro from WinUser.h in PuTTY project

In our case, PuTTY used SetWindowTextA (Figure 7).

Figure 7 – Monitoring SetWindowTextA with API monitor

But the same behavior also happened with SetWindowTextW; therefore, I will use SetWindowText in the rest of the post.

The role of SetWindowText from the documentation is to:

Change the text of the specified window’s title bar (if it has one).

To understand more easily how SetWindowText is working I wrote a small GUI C++ that changes the title:

while (1) {

When I ran it, the computer was extremely slow. It felt like the machine was frozen. It was much worse than what happened with PuTTY. When PuTTY changed the title, it was over the network, which caused it to be a little bit slower than my program which run without the network overhead. With my local application, you got WSOD immediately, and nothing worked. You were forced to restart the computer.

I continued to analyze and record the tracing:

Figure 8 – Tracing of SetWindowTextW

It’s hard to tell what object is causing the performance issue because this function eventually calls win32u.NtUserMessageCall and goes to the kernel:

Figure 9 – Call to NtUserMessageCall from IDA

One thing that I noticed was the call to WindowProc. Microsoft recommends avoiding bottlenecks in the window procedure, but it is not relevant to this issue because in our case, it affects the entire computer.

It seems to be performing extensive actions that cause everything to slow down. We reported it to Microsoft. You can see their answer in the summary of the SetWindowText Attack section below. TL;DR: They were able to reproduce it and opened it as a bug to their development team.

We reported this issue to PuTTY. It was fixed from version 0.75 and was assigned CVE-2021-33500. You can read more about this bug from PuTTY’s report.

Affected Terminals and GdiDrawString

We tested all the famous terminals for Windows to see if they are also affected by this DoS. We noticed that most of them are vulnerable. We also found that it is not only because of the SetWindowText function, but there was also a similar function that behaved similarly.

In one of the cases, I tested the MobaXterm terminal, and I was surprised that it didn’t use SetWindowText function to change the window title but, rather, a function named GdipDrawString. The interesting thing in this case is that it didn’t affect the whole computer like SetWindowText. It affected only the application, which eventually crashed.

Figure 10 – Monitoring GdiDrawString with API monitor

The GdipDrawString is one of the text functions exposed by the Windows GDI+, which is also documented here. It seems that the use of graphic functions to write text multiple times can cause DoS if it isn’t being controlled. We reported it to MobaXterm that fixed this issue quickly (CVE-2021-28847).

Another interesting finding is that I couldn’t cause DoS with Windows default terminals (cmd, Powershell, Windows Terminal and WSL). They were using the SetConsoleTitle function, which according to Microsoft, is no longer a part of their ecosystem roadmap. They preferred users to use the virtual terminal sequences for maximum compatibility in cross-platform scenarios. This could expand the attack surface because it will require the programmer to make sure no one abuses these terminal sequences. A small example can be when someone writes something like that:

printf("\x1b\x5d\x30\x3b%s\x07", userTitle);

\x1b\x5d\x30\x3b → The start of a title ESC ] 0 ;.

\x07 → The end of a title.

If the attacker has control of the userTitle and can call it multiple times, it can cause DoS.

Here is a summary of our findings in the Windows terminals:

App Category OS DoS CVE
Customized C++ app Local App Windows Yes

SetWindowText → affects the whole computer

GdipDrawString → affects only the application

PuTTY Terminal Yes – the whole computer  


Fixed version: 0.75

MobaXterm Yes – only the application,

it calls GdipDrawString, not SetWindowText



Fixed version: 21.0 Preview3

MinTTY Yes  


Fixed version: 3.4.6

Cygwin Yes – uses MinTTY See MinTTY
Git Yes – uses MinTTY See MinTTY

Fixed version: v2.30.1

PowerShell No – uses SetConsoleTitle  
Cmd No – uses SetConsoleTitle  
Windows Terminal No – uses SetConsoleTitle  
Visual Studio Code No – uses Cmd  
WSL No  
ZOC Yes – only the application CVE-2021-32198 (won’t fix)
XSHELL Yes CVE-2021-42095

Table 1 – Summary of findings in Windows terminals

More Surprises: Creating DoS from Your Browser

We understand that the root problem is with the graphic functions, specifically with the function SetWindowText and not with the terminal. It means that if we can control any application to run an extensive use of SetWindowText (or GdipDrawString), we can create DoS. The implications of it are far beyond the terminal world as we will see next.

Almost every GUI application will use some graphic function like SetWindowText, but it doesn’t mean we can control it. On what other application can we control the window title passively? You guessed right: browsers!

An attack vector can be to send a link to a malicious website that changes the window title multiple times and will cause a denial of service. Some browsers, as we will see next, don’t consider denial-of-service attacks as a security vulnerability because causing memory-based hang ups are possible in different ways and not only changing the title. I suppose this is true, but I tried to cause memory-based hang ups by loading a page with JavaScript code that stores infinite variable data, and it didn’t affect the browsers that I checked. As you will see next, some of the results were strange.

Hanging Up Some Browsers

I started to check the most common browsers: Chrome, Edge and Firefox.

I created a simple HTML file that changes the title an infinite number of times. On the first attempt with Chrome, everything worked normally. The reason was because when I changed the title multiple times with the same string, Chrome had a check to see if you are using the same title. If it sees the same title, it won’t call SetWindowText. After adding a small fix to my HTML, each time changing the title, things started to get wild:

function myFunction() {	
  var i = 0;
  while(1) {
	 if (i % 2 == 0) {
	    document.title = "A";
		i = 1;
	 } else {
	    document.title = "B";
		i = 0;

  var x = document.title;
  document.getElementById("demo").innerHTML = x;

The whole browser got stuck, became blurred, and the only option was to terminate it. The same behavior happened with Edge.

Figure 11 – Monitoring SetWindowTextW with API monitor

But notice that only the browser got stuck, not the computer. This is probably because modern browsers are based on sandboxes. The interesting thing is that this attack didn’t work on Firefox and Internet Explorer. Firefox even wrote that the page is slowing down (Figure 12), but you can still work on other tabs.

Figure 12 – FireFox tab crash

Why did it happen only on Chrome and Edge and not on Firefox and Internet Explorer? The common thing with Chrome and Edge is that they are built on Chromium but … wait … if this is the reason, does it mean that any browser-based Chromium is affected by that? Yes! I checked any browser that I could find; all of them are based on Chromium, and on all of them it happened (see Table 2).

App Category Chromium Based OS DoS
Internet Explorer Browser  



No – it crashes only one tab but the browser can still work








Yes – Only the entire browser


Table 2 – Summary of findings in Windows terminals

BSOD from the Browser

The most shocking thing I found was that the SetWindowText attack on Chromium-based browsers not only hangs the browser but … wait for it … causes a BSOD\WSOD! (Figure 13). We have found that we could reproduce this behavior just on VMs, so it looks like it’s a resource- related issue.

Figure 13 – Bluescreen after browser DoS

It turns out that after the browsers start to change the title multiple times , and you maximize and minimize the window, the virtual machine starts to hang and after a couple of minutes gets a BSOD. I was able to reproduce it in every Chromium-based browser. The reproducing of this issue wasn’t trivial; in the first try I needed two separate windows, but if I involved one window with my local application — the one that ran SetWindowText multiple times — I could reproduce it. The strange thing is that I couldn’t reproduce it by just running my local application — it seems that the browser did more stuff that affected the virtual machine rather than just changing the Window.


We notified the affected vendors about this issue, and we opened a case on the root browser Chromium.

According to Google:

Thanks for the report. DoS issues are treated as abuse or stability issues rather than security vulnerabilities. For more details, see: .

Therefore, I submitted a case about a crash, which they were able to reproduce. They did mention that they were able to reproduce it also on Linux:

Note: Issue is not observed on Mac but is observed on Linux.

We also reported it to VMware because it might be related to their driver vm3dmp.sys. Because it was not so easy to reproduce, they weren’t able to do it, and this is their response:

We have reviewed the issue again. We were not able to reproduce the crash in the latest versions of WS 16.1.2 build-17966106 and Chrome 92.0.4515.131. We view that the behavior you observed might be depended on chrome version used as we didn’t see any BSOD issues on our end. Hence, we consider this as not a bug.


Other Applications Might be Affected by This

We saw that the abuse of the window title by terminals and browsers can cause a severe denial of service and can be used as a remote attack. Are there other applications that might be vulnerable? Yes, on every application that you can control some text changing, that will eventually call SetWindowText or GdipDrawString, possibly causing damage.

Think about all the messaging applications (Slack, Teams, Skype, etc.). What happens if one of your contacts changes its name? It might call SetWindowText, and if it can call it multiple times, theoretically, in the worst case, it can cause a denial of service to anyone who is in his contact list and has the application open in his Windows.

Putting the Theory to the Test

I was curious to see if this is something that is possible in some way or another. I thought to check it on the messaging application Slack for Windows.

The first thing I did was check what would happen if I changed my name on my computer. Does Slack in my contacts computer see this change immediately and call SetWindowText? You can see below (Figure 14) that it does!

Figure 14 – Monitoring SetWindowTextW by Slack with API monitor

Great. All we have to do is to change my Slack profile many times until it causes a DoS to my contact (or contacts, if you do it in an irresponsible manner).

A little bit of reverse engineering (there is also documentation), and I found the REST API that changes the profile settings: ../api/users.profile.set.

But after only three times; it failed because of a rate limit (Figure 15).

Figure 15 – Slack rate limit responds

I didn’t find a way to bypass the rate limit but bypassing it will place you in the next step when you might see if the changes are fast enough to cause a denial of service.

Summary for the SetWindowText Attack

The extensive use of a function related to graphics seems to be with potential for DoS. There might be other functions that can be controlled like that. In our case, the SetWindowText attack was able to cause a denial of service from a remote server.

The root problem is with this function. In my tests, I also checked it on Windows 7, and there wasn’t an issue like in Windows 10.

The security group from Vivaldi Technologies wrote to me about this issue in their browser:

This is a design limitation of Windows 10; it does not limit application memory usage, and simply uses pagefile (virtual memory) when it runs out of RAM. This is slower to respond because it must be read from disk.

I also notified Microsoft about it, and they answered:

Thank you again for your report. Our team was able to reproduce this issue, but it does not meet our bar for servicing with an immediate security update. While this results in a denial of service condition, this can only be triggered locally and is the result of resource exhaustion. An attacker would not be able to trigger any additional vulnerable conditions or retrieve information that would be beneficial in other attacks on the system.
We will be closing this case, but we have opened a bug with our development team, and they may consider addressing this in a future release of Windows

It is important to mention that while Microsoft wrote that it is triggered locally, it can be triggered remotely, depending on how it was used in a program. For example, it is possible to create a malicious file on a remote server with a window title change command that when opening it from a terminal, will cause a denial of service. It is something I was able to do and reported to the terminal vendors who fixed it.

Let’s continue with our journey and see what else we can do with ANSI escape characters.

Spoofing Data on Kubernetes and OpenShift

Remember the ANSI escape characters issue that I mentioned in the beginning of the article on OpenShift, a RedHat open-source container application platform based on the Kubernetes? I noticed something strange while I created projects. As a regular user, I had an option to create a project with a “Display Name” field (Figure 16). I noticed that this “Display Name” field doesn’t filter ANSI escape characters.

Figure 16 – Create project form in OpenShift

I could add ANSI escape characters that will change the terminal window title, paint the terminal with whatever colors I choose, delete that display and so on.

When a user connects to the terminal and runs the command oc get projects. it will trigger the injected ANSI escape characters. I showed in the PoC how I can spoof that project’s data and inject the data that I want.


We reported it to Red Hat. They confirmed this vulnerability and also mentioned (thanks to Sam Fowler and the team) that there is a larger impact:

All free text fields (and there are many) in the OpenShift and Kubernetes APIs are susceptible to this, not only Project `displayNames`. For instance this affects the `message` field of an Event (a core k8s object). Those that can create Events can also inject command sequences that are unescaped when printed in a terminal emulator with `oc` or `kubectl`

We checked it and confirmed that this also happens in Kubernetes, and they suggested reporting it to the upstream (Kubernetes). The problem is with the filtering of the JSON objects. It didn’t happen when trying to create such request with YAML objects because kubectl rejects it because of invalid characters. We reported it to Kubernetes, and their security team asked to open it as a public bug:

We’ve decided we’re comfortable handling this through a public issue. Please go ahead and open an issue, otherwise I can follow up.

They also mentioned that they “will treat it as a normal (non-security) bug for now”.

I opened it as a public bug, and here is an example PoC for spoofing data in Kubernetes Event.


A few days after reporting it, a Kubernetes developer, Paco Xu, mentioned that it also affects the kubectl log. On  19.11.2021 they assigned it with CVE-2021-25743.

It is time to talk about the last thing we found on this research.

Bypassing the Bracket Paste Mode

One mechanism that is not very well known is the bracket paste mode (another good explanation) that was created to protect against pasted text. When copying a command like echo ‘Hello’ and pasting it in your terminal, the terminal will execute the command. If the bracket paste mode is enabled, the code won’t run. Rather, it will wrap the code with ESC [ 200 ~ in the beginning and close it with ESC [ 201 ~. Your code will look like that: ESC[200~<code>ESC[201~. The code won’t be executed when pasting it to the terminal.

Why do we need it? Because of copy\paste attacks. These attacks are showing you a legitimate command to copy, while some HTML tricks hide a malicious command. There is a nice proof of concept that was created by this website.

To enable the bracket paste mode, you can run in your terminal: echo -ne “\e[?2004h”
To disable it: echo -ne “\e[?2004l”

When I saw it, I was wondering, “What if my code will start with ESC [ 200 ~ and then my malicious code?” My code will look like that (notice it has two lines; the second line is new line):

ESC[201~clear; echo "Bypass Bracketed Paste Mode"

The bracket paste mode will convert it to:

ESC[200~ESC[201~clear; echo "Bypass Bracketed Paste Mode"ESC[201~

In this case, it will be closed in the beginning and allow our command to escape from this bracket paste mode. When someone pastes it, it will escape and run the command in the terminal:


We found three terminals affected by this vulnerability, MinTTY (CVE-2021-31701), Xshell (CVE-2021-37326) and ZOC (CVE-2021-40147). They were fixed after the report. Notice that Git-Bash for Windows and Cygwin were also affected because they are using MinTTY.

App Fixed Version CVE
MinTTY 3.4.7


Git for Windows 2.31.1
Xshell Build 0077


ZOC 8.02.2



Table 3



Preventing SetWindowText

Almost all the terminals affected by the SetWindowText vulnerability fixed this issue. But it can be prevented by configuring the terminal to prevent the user from changing the title of the window. In each terminal, these settings can look different.

In PuTTY you can prevent it by ticking the box “Disable remote-controlled window title changing” (Figure 17).

Figure 17 – PuTTY Disable remote-controlled window title changing box

In MobaXterm, you can prevent it by right clicking on the session and choosing “Edit Session.” Under the “Bookmark setting,” tick the “Lock terminal title” box (Figure 18).

Figure 18 – MobaXterm locking terminal title setting

Not all the terminals have such settings.

Preventing Bracket Paste Mode

We reported all the vulnerabilities that we found to the terminals’ vendors, and they fixed them accordingly — but this mode might still be vulnerable. The best way to deal with it is to copy code from an unknown source, paste it first on some text editor, make sure it doesn’t contain a malicious code and paste it in the terminal.


In this blog post, we had a glimpse of ANSI escape characters. We discovered that they are not just used for colors but also for commands. We also discovered how something like changing a Windows title has severe effects on the whole operating system and how, in the past, there were also code execution vulnerabilities.

We saw how ANSI escape characters from DevOps systems like OpenShift or Kubernetes have a connection through terminals and how it can affect the user of the terminal.

The ANSI escape characters weren’t the only things that we checked. The bracket paste mode, for example — something that is less known — was also vulnerable.

There are a lot more things to be discovered, such as the connection between the client and the server, other complex ANSI escape characters and so on. We encourage you to use the mitigations we proposed to help reduce the attack surface.



Previous Article
Attacking RDP from Inside: How we abused named pipes for smart-card hijacking, unauthorized file system access to client machines and more
Attacking RDP from Inside: How we abused named pipes for smart-card hijacking, unauthorized file system access to client machines and more

In this blog post we are going to discuss the details of a vulnerability in Windows Remote Desktop Services...

Next Article
Hook Heaps and Live Free
Hook Heaps and Live Free

I wanted to write this blog post to talk a bit about Cobalt Strike, function hooking and the Windows heap. ...