tag:blogger.com,1999:blog-71783461588789315882024-02-20T17:00:54.642-08:00APPGate Research BlogAppgatehttp://www.blogger.com/profile/14486895464481874102noreply@blogger.comBlogger1125tag:blogger.com,1999:blog-7178346158878931588.post-21059189169113324972020-02-28T07:20:00.000-08:002020-03-02T05:58:59.021-08:00BraveStarr – A Fedora 31 netkit telnetd remote exploit<p><a href="https://twitter.com/ronaldhuizer">Ronald Huizer</a></p>
<p align="center"><img src="https://raw.githubusercontent.com/immunityinc/bravestarr/master/assets/bravestarr.gif" /></p>
<blockquote>
<p><em>It’s not worth doing something unless you were doing something that someone, somewhere, would much rather you weren’t doing.</em></p>
</blockquote>
<blockquote>
<p> <em>Sir Terence David John Pratchett</em></p>
</blockquote>
<h1 id="introduction"><span class="header-section-number">1</span> Introduction</h1>
<p>In this post we will present a vulnerability in the Fedora 31 version of netkit-telnet-0.17 telnetd which has been present for a long time, and which is remotely exploitable. Other Linux distributions or Unix systems may or may not be vulnerable, however the aim of this post is to present an interesting vulnerability and corresponding <a href="https://github.com/immunityinc/bravestarr/blob/master/bravestarr.py">exploit</a> and not to create a taxonomy of vulnerable versions. We will leave this to others.</p>
<p>The exploit presented is a proof-of-concept one: it will show a working basis that defeats the default Fedora 31 mitigations such as PIE, ASLR, and non-executable pages. The exploit will however not bypass SELinux, and further research is needed to do so. A proper exploit would also need to be far more reliable. We believe both can be done, but to reduce the amount of time spent on weaponizing a now disclosed vulnerability, and to mitigate the impact of installations that – god forbid – still run telnetd, we will also leave this to others.</p>
<h1 id="source-code-overview"><span class="header-section-number">2</span> Source code overview</h1>
<p>As the vulnerability and exploitation are somewhat involved, we will discuss the telnetd code base in a manner that first provides understanding of the internals before we switch to discussing the vulnerability and expoitation. To keep this document more or less clear, not all parts of the source code have been listed here. For those who want to pursue a deeper understanding of the code, the Fedora 31 netkit-telnet-0.17 tarball is available <a href="https://github.com/immunityinc/bravestarr/blob/master/assets/netkit-telnet-0.17.tgz?raw=true">here</a>.</p>
<h2 id="glossary-of-important-names-variables-and-conventions"><span class="header-section-number">2.1</span> Glossary of important names, variables, and conventions</h2>
<p>The telnet protocol is outlined in <a href="http://tools.ietf.org/html/rfc854">RFC 854</a>, but for readers that are not keen on reading through this document, we will summarize the most frequently used abbrevations below.</p>
<ul>
<li>IAC: Interpret as Command. An escape character that tells the next sequence is a command sequence rather than data.</li>
<li>AO: Abort output. A command that discards further terminal output of the current running process.</li>
<li>SB: Suboption Begin. A command that marks the beginning of a suboption sequence.</li>
<li>SE: Suboption End. A command that marks the end of a suboption sequence.</li>
</ul>
<p>The source code contains many global variables that are frequently used. To ease the reading burden, below the reader can find a glossary of the ones we will encounter during our discussion.</p>
<ul>
<li><code>int net</code>: the client socket, which data is read from and sent to.</li>
<li><code>char netibuf[BUFSIZ]</code>: the network input buffer. Data read from the client is stored here.</li>
<li><code>int ncc</code>: the number of bytes read from the client into <code>netibuf</code>.</li>
<li><code>char *netip</code>: the network input pointer. Tracks where in <code>netibuf</code> we are processing.</li>
<li><code>char netobuf[BUFSIZE+NETSLOP]</code>: the network output buffer. Data to send to the client is stored here.</li>
<li><code>char *nbackp</code>: marks the start of unsent data in <code>netobuf</code>.</li>
<li><code>char *nfrontp</code>: marks where to add data to <code>netobuf</code>.</li>
<li><code>char *neturg</code>: marks a single byte to be sent as urgent data in <code>netobuf</code>.</li>
<li><code>char ptyibuf[BUFSIZ]</code>: the pty input buffer. Data read from the login process pty is stored here.</li>
<li><code>int pcc</code>: the number of bytes read from the pty into <code>ptyibuf</code>.</li>
<li><code>char *ptyip</code>: the pty input pointer. Tracks where in <code>ptyibuf</code> we are processing.</li>
<li><code>char ptyobuf[BUFSIZ+NETSLOP]</code>: the pty output buffer. Data to be sent to the login process pty is stored here.</li>
<li><code>char *pbackp</code>: marks the start of unsent data in <code>ptyobuf</code>.</li>
<li><code>char *pfrontp</code>: marks where to add data to <code>ptyobuf</code>.</li>
</ul>
<p>Finally, we would like to draw attention to some conventions used throughout this document. We will denote the integer range 1,…,10 using the inclusive <code>[1,10]</code> notation, the exclusive <code>(0,11)</code> notation, or a combination of the two such as <code>[1,11)</code>.</p>
<h2 id="the-main-processing-loop"><span class="header-section-number">2.2</span> The main processing loop</h2>
<p>We’ll discuss the vulnerability in a top-down fashion, and therefore we will begin by looking at the function that drives network communication after a client connection has been accepted.</p>
<p>The processing of a client network request to telnetd is handled in the <code>ttloop</code> function. In spite of it being named <code>ttloop</code>, this function is in fact a single iteration over receiving and processing client data. On line <a href="#ttloop-148">148</a> client input is read into <code>netibuf</code> and the number of bytes read is kept in the global <code>int</code> variable <code>ncc</code>. The current instance of the daemon will terminate immediately on <a href="#ttloop-149">read errors</a> or an <a href="#ttloop-152">EOF</a> from the client. If data is read <code>netip</code> is initialized to the start of <code>netibuf</code> at <a href="#ttloop-157">157</a> and the main state machine <code>telrcv</code> is called at <a href="#ttloop-158">158</a>. Note that <code>telrcv</code> will decrement <code>ncc</code> upon consuming network input, and at <a href="#ttloop-159">159</a> if there is still data remaining afterwards the pty output buffer will be reset and <code>telrcv</code> will be called again. We will see later that the only way <code>telrcv</code> returns with <code>ncc > 0</code> is if <code>ptyobuf</code> would overflow.</p>
<div class="sourceCode" id="ttloop" data-startFrom="139"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="ttloop-139" href="#ttloop-139" title="139"><span class="dt">void</span></a>
<a class="sourceLine" id="ttloop-140" href="#ttloop-140" title="140">ttloop(<span class="dt">void</span>)</a>
<a class="sourceLine" id="ttloop-141" href="#ttloop-141" title="141">{</a>
<a class="sourceLine" id="ttloop-142" href="#ttloop-142" title="142"></a>
<a class="sourceLine" id="ttloop-143" href="#ttloop-143" title="143"> DIAG(TD_REPORT, netoprintf(<span class="st">"td: ttloop</span><span class="sc">\r\n</span><span class="st">"</span>););</a>
<a class="sourceLine" id="ttloop-144" href="#ttloop-144" title="144"> </a>
<a class="sourceLine" id="ttloop-145" href="#ttloop-145" title="145"> <span class="cf">if</span> (nfrontp-nbackp) {</a>
<a class="sourceLine" id="ttloop-146" href="#ttloop-146" title="146"> netflush();</a>
<a class="sourceLine" id="ttloop-147" href="#ttloop-147" title="147"> }</a>
<a class="sourceLine" id="ttloop-148" href="#ttloop-148" title="148"> ncc = read(net, netibuf, <span class="kw">sizeof</span>(netibuf));</a>
<a class="sourceLine" id="ttloop-149" href="#ttloop-149" title="149"> <span class="cf">if</span> (ncc < <span class="dv">0</span>) {</a>
<a class="sourceLine" id="ttloop-150" href="#ttloop-150" title="150"> syslog(LOG_INFO, <span class="st">"ttloop: read: %m</span><span class="sc">\n</span><span class="st">"</span>);</a>
<a class="sourceLine" id="ttloop-151" href="#ttloop-151" title="151"> exit(<span class="dv">1</span>);</a>
<a class="sourceLine" id="ttloop-152" href="#ttloop-152" title="152"> } <span class="cf">else</span> <span class="cf">if</span> (ncc == <span class="dv">0</span>) {</a>
<a class="sourceLine" id="ttloop-153" href="#ttloop-153" title="153"> syslog(LOG_INFO, <span class="st">"ttloop: peer died: EOF</span><span class="sc">\n</span><span class="st">"</span>);</a>
<a class="sourceLine" id="ttloop-154" href="#ttloop-154" title="154"> exit(<span class="dv">1</span>);</a>
<a class="sourceLine" id="ttloop-155" href="#ttloop-155" title="155"> }</a>
<a class="sourceLine" id="ttloop-156" href="#ttloop-156" title="156"> DIAG(TD_REPORT, netoprintf(<span class="st">"td: ttloop read %d chars</span><span class="sc">\r\n</span><span class="st">"</span>, ncc););</a>
<a class="sourceLine" id="ttloop-157" href="#ttloop-157" title="157"> netip = netibuf;</a>
<a class="sourceLine" id="ttloop-158" href="#ttloop-158" title="158"> telrcv(); <span class="co">/* state machine */</span></a>
<a class="sourceLine" id="ttloop-159" href="#ttloop-159" title="159"> <span class="cf">if</span> (ncc > <span class="dv">0</span>) {</a>
<a class="sourceLine" id="ttloop-160" href="#ttloop-160" title="160"> pfrontp = pbackp = ptyobuf;</a>
<a class="sourceLine" id="ttloop-161" href="#ttloop-161" title="161"> telrcv();</a>
<a class="sourceLine" id="ttloop-162" href="#ttloop-162" title="162"> }</a>
<a class="sourceLine" id="ttloop-163" href="#ttloop-163" title="163">} <span class="co">/* end of ttloop */</span></a></code></pre></div>
<h2 id="the-main-state-machine"><span class="header-section-number">2.3</span> The main state machine</h2>
<p>The telnetd state machine that processes network input and drives the protocol actions is defined in the <code>telrcv</code> function.</p>
<p>Note that the source tarball contains preprocessor conditionals for <code>ENCRYPT</code> and <code>LINEMODE</code>. These are not defined in the default Fedora 31 build, and therefore have been removed from the source code listing.</p>
<p>As long as we still have network input at <a href="#telrcv-86">86</a>, then a byte is stored in <code>c</code> at <a href="#telrcv-88">88</a> and <code>ncc</code> is adjusted to reflect the consumed byte at <a href="#telrcv-89">89</a>. We will not discuss the entire state machine, but have mainly listed the code below to refer to in later parts of this document. For now take note of how the IAC AO sequence is handled, in particular the <a href="#telrcv-194"><code>netclear</code></a> call.</p>
<div class="sourceCode" id="telrcv" data-startFrom="82"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="telrcv-82" href="#telrcv-82" title="82"><span class="dt">void</span> telrcv(<span class="dt">void</span>) {</a>
<a class="sourceLine" id="telrcv-83" href="#telrcv-83" title="83"> <span class="dt">register</span> <span class="dt">int</span> c;</a>
<a class="sourceLine" id="telrcv-84" href="#telrcv-84" title="84"> <span class="dt">static</span> <span class="dt">int</span> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-85" href="#telrcv-85" title="85"></a>
<a class="sourceLine" id="telrcv-86" href="#telrcv-86" title="86"> <span class="cf">while</span> (ncc > <span class="dv">0</span>) {</a>
<a class="sourceLine" id="telrcv-87" href="#telrcv-87" title="87"> <span class="cf">if</span> ((&ptyobuf[BUFSIZ] - pfrontp) < <span class="dv">2</span>) <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-88" href="#telrcv-88" title="88"> c = *netip++ & <span class="bn">0377</span>;</a>
<a class="sourceLine" id="telrcv-89" href="#telrcv-89" title="89"> ncc--;</a></code></pre></div>
<div class="sourceCode" id="telrcv" data-startFrom="96"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="telrcv-96" href="#telrcv-96" title="96"> <span class="cf">switch</span> (state) {</a>
<a class="sourceLine" id="telrcv-97" href="#telrcv-97" title="97"> <span class="cf">case</span> TS_CR:</a>
<a class="sourceLine" id="telrcv-98" href="#telrcv-98" title="98"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-99" href="#telrcv-99" title="99"> <span class="co">/* Strip off \n or \0 after a \r */</span></a>
<a class="sourceLine" id="telrcv-100" href="#telrcv-100" title="100"> <span class="cf">if</span> ((c == <span class="dv">0</span>) || (c == <span class="ch">'\n'</span>)) {</a>
<a class="sourceLine" id="telrcv-101" href="#telrcv-101" title="101"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-102" href="#telrcv-102" title="102"> }</a>
<a class="sourceLine" id="telrcv-103" href="#telrcv-103" title="103"> <span class="co">/* FALL THROUGH */</span></a>
<a class="sourceLine" id="telrcv-104" href="#telrcv-104" title="104"></a>
<a class="sourceLine" id="telrcv-105" href="#telrcv-105" title="105"> <span class="cf">case</span> TS_DATA:</a>
<a class="sourceLine" id="telrcv-106" href="#telrcv-106" title="106"> <span class="cf">if</span> (c == IAC) {</a>
<a class="sourceLine" id="telrcv-107" href="#telrcv-107" title="107"> state = TS_IAC;</a>
<a class="sourceLine" id="telrcv-108" href="#telrcv-108" title="108"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-109" href="#telrcv-109" title="109"> }</a>
<a class="sourceLine" id="telrcv-110" href="#telrcv-110" title="110"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-111" href="#telrcv-111" title="111"><span class="co"> * We now map \r\n ==> \r for pragmatic reasons.</span></a>
<a class="sourceLine" id="telrcv-112" href="#telrcv-112" title="112"><span class="co"> * Many client implementations send \r\n when</span></a>
<a class="sourceLine" id="telrcv-113" href="#telrcv-113" title="113"><span class="co"> * the user hits the CarriageReturn key.</span></a>
<a class="sourceLine" id="telrcv-114" href="#telrcv-114" title="114"><span class="co"> *</span></a>
<a class="sourceLine" id="telrcv-115" href="#telrcv-115" title="115"><span class="co"> * We USED to map \r\n ==> \n, since \r\n says</span></a>
<a class="sourceLine" id="telrcv-116" href="#telrcv-116" title="116"><span class="co"> * that we want to be in column 1 of the next</span></a>
<a class="sourceLine" id="telrcv-117" href="#telrcv-117" title="117"><span class="co"> * printable line, and \n is the standard</span></a>
<a class="sourceLine" id="telrcv-118" href="#telrcv-118" title="118"><span class="co"> * unix way of saying that (\r is only good</span></a>
<a class="sourceLine" id="telrcv-119" href="#telrcv-119" title="119"><span class="co"> * if CRMOD is set, which it normally is).</span></a>
<a class="sourceLine" id="telrcv-120" href="#telrcv-120" title="120"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-121" href="#telrcv-121" title="121"> <span class="cf">if</span> ((c == <span class="ch">'\r'</span>) && his_state_is_wont(TELOPT_BINARY)) {</a></code></pre></div>
<div class="sourceCode" id="telrcv" data-startFrom="140"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="telrcv-140" href="#telrcv-140" title="140"> {</a></code></pre></div>
<div class="sourceCode" id="telrcv" data-startFrom="145"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="telrcv-145" href="#telrcv-145" title="145"> state = TS_CR;</a>
<a class="sourceLine" id="telrcv-146" href="#telrcv-146" title="146"> }</a>
<a class="sourceLine" id="telrcv-147" href="#telrcv-147" title="147"> }</a>
<a class="sourceLine" id="telrcv-148" href="#telrcv-148" title="148"> *pfrontp++ = c;</a>
<a class="sourceLine" id="telrcv-149" href="#telrcv-149" title="149"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-150" href="#telrcv-150" title="150"></a>
<a class="sourceLine" id="telrcv-151" href="#telrcv-151" title="151"> <span class="cf">case</span> TS_IAC:</a>
<a class="sourceLine" id="telrcv-152" href="#telrcv-152" title="152"> gotiac:</a>
<a class="sourceLine" id="telrcv-153" href="#telrcv-153" title="153"> <span class="cf">switch</span> (c) {</a>
<a class="sourceLine" id="telrcv-154" href="#telrcv-154" title="154"></a>
<a class="sourceLine" id="telrcv-155" href="#telrcv-155" title="155"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-156" href="#telrcv-156" title="156"><span class="co"> * Send the process on the pty side an</span></a>
<a class="sourceLine" id="telrcv-157" href="#telrcv-157" title="157"><span class="co"> * interrupt. Do this with a NULL or</span></a>
<a class="sourceLine" id="telrcv-158" href="#telrcv-158" title="158"><span class="co"> * interrupt char; depending on the tty mode.</span></a>
<a class="sourceLine" id="telrcv-159" href="#telrcv-159" title="159"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-160" href="#telrcv-160" title="160"> <span class="cf">case</span> IP:</a>
<a class="sourceLine" id="telrcv-161" href="#telrcv-161" title="161"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: recv IAC"</span>, c));</a>
<a class="sourceLine" id="telrcv-162" href="#telrcv-162" title="162"> interrupt();</a>
<a class="sourceLine" id="telrcv-163" href="#telrcv-163" title="163"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-164" href="#telrcv-164" title="164"> <span class="cf">case</span> BREAK:</a>
<a class="sourceLine" id="telrcv-165" href="#telrcv-165" title="165"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: recv IAC"</span>, c));</a>
<a class="sourceLine" id="telrcv-166" href="#telrcv-166" title="166"> sendbrk();</a>
<a class="sourceLine" id="telrcv-167" href="#telrcv-167" title="167"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-168" href="#telrcv-168" title="168"></a>
<a class="sourceLine" id="telrcv-169" href="#telrcv-169" title="169"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-170" href="#telrcv-170" title="170"><span class="co"> * Are You There?</span></a>
<a class="sourceLine" id="telrcv-171" href="#telrcv-171" title="171"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-172" href="#telrcv-172" title="172"> <span class="cf">case</span> AYT:</a>
<a class="sourceLine" id="telrcv-173" href="#telrcv-173" title="173"> DIAG(TD_OPTIONS,</a>
<a class="sourceLine" id="telrcv-174" href="#telrcv-174" title="174"> printoption(<span class="st">"td: recv IAC"</span>, c));</a>
<a class="sourceLine" id="telrcv-175" href="#telrcv-175" title="175"> recv_ayt();</a>
<a class="sourceLine" id="telrcv-176" href="#telrcv-176" title="176"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-177" href="#telrcv-177" title="177"></a>
<a class="sourceLine" id="telrcv-178" href="#telrcv-178" title="178"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-179" href="#telrcv-179" title="179"><span class="co"> * Abort Output</span></a>
<a class="sourceLine" id="telrcv-180" href="#telrcv-180" title="180"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-181" href="#telrcv-181" title="181"> <span class="cf">case</span> AO:</a>
<a class="sourceLine" id="telrcv-182" href="#telrcv-182" title="182"> {</a>
<a class="sourceLine" id="telrcv-183" href="#telrcv-183" title="183"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: recv IAC"</span>, c));</a>
<a class="sourceLine" id="telrcv-184" href="#telrcv-184" title="184"> ptyflush(); <span class="co">/* half-hearted */</span></a>
<a class="sourceLine" id="telrcv-185" href="#telrcv-185" title="185"> init_termbuf();</a>
<a class="sourceLine" id="telrcv-186" href="#telrcv-186" title="186"></a>
<a class="sourceLine" id="telrcv-187" href="#telrcv-187" title="187"> <span class="cf">if</span> (slctab[SLC_AO].sptr &&</a>
<a class="sourceLine" id="telrcv-188" href="#telrcv-188" title="188"> *slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE))</a>
<a class="sourceLine" id="telrcv-189" href="#telrcv-189" title="189"> {</a>
<a class="sourceLine" id="telrcv-190" href="#telrcv-190" title="190"> *pfrontp++ =</a>
<a class="sourceLine" id="telrcv-191" href="#telrcv-191" title="191"> (<span class="dt">unsigned</span> <span class="dt">char</span>)*slctab[SLC_AO].sptr;</a>
<a class="sourceLine" id="telrcv-192" href="#telrcv-192" title="192"> }</a>
<a class="sourceLine" id="telrcv-193" href="#telrcv-193" title="193"></a>
<a class="sourceLine" id="telrcv-194" href="#telrcv-194" title="194"> netclear(); <span class="co">/* clear buffer back */</span></a>
<a class="sourceLine" id="telrcv-195" href="#telrcv-195" title="195"> *nfrontp++ = (<span class="dt">char</span>)IAC;</a>
<a class="sourceLine" id="telrcv-196" href="#telrcv-196" title="196"> *nfrontp++ = (<span class="dt">char</span>)DM;</a>
<a class="sourceLine" id="telrcv-197" href="#telrcv-197" title="197"> neturg = nfrontp-<span class="dv">1</span>; <span class="co">/* off by one XXX */</span></a>
<a class="sourceLine" id="telrcv-198" href="#telrcv-198" title="198"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: send IAC"</span>, DM));</a>
<a class="sourceLine" id="telrcv-199" href="#telrcv-199" title="199"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-200" href="#telrcv-200" title="200"> }</a>
<a class="sourceLine" id="telrcv-201" href="#telrcv-201" title="201"></a>
<a class="sourceLine" id="telrcv-202" href="#telrcv-202" title="202"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-203" href="#telrcv-203" title="203"><span class="co"> * Erase Character and</span></a>
<a class="sourceLine" id="telrcv-204" href="#telrcv-204" title="204"><span class="co"> * Erase Line</span></a>
<a class="sourceLine" id="telrcv-205" href="#telrcv-205" title="205"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-206" href="#telrcv-206" title="206"> <span class="cf">case</span> EC:</a>
<a class="sourceLine" id="telrcv-207" href="#telrcv-207" title="207"> <span class="cf">case</span> EL:</a>
<a class="sourceLine" id="telrcv-208" href="#telrcv-208" title="208"> {</a>
<a class="sourceLine" id="telrcv-209" href="#telrcv-209" title="209"> cc_t ch;</a>
<a class="sourceLine" id="telrcv-210" href="#telrcv-210" title="210"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: recv IAC"</span>, c));</a>
<a class="sourceLine" id="telrcv-211" href="#telrcv-211" title="211"> ptyflush(); <span class="co">/* half-hearted */</span></a>
<a class="sourceLine" id="telrcv-212" href="#telrcv-212" title="212"> init_termbuf();</a>
<a class="sourceLine" id="telrcv-213" href="#telrcv-213" title="213"> <span class="cf">if</span> (c == EC) ch = *slctab[SLC_EC].sptr;</a>
<a class="sourceLine" id="telrcv-214" href="#telrcv-214" title="214"> <span class="cf">else</span> ch = *slctab[SLC_EL].sptr;</a>
<a class="sourceLine" id="telrcv-215" href="#telrcv-215" title="215"> <span class="cf">if</span> (ch != (cc_t)(_POSIX_VDISABLE))</a>
<a class="sourceLine" id="telrcv-216" href="#telrcv-216" title="216"> *pfrontp++ = (<span class="dt">unsigned</span> <span class="dt">char</span>)ch;</a>
<a class="sourceLine" id="telrcv-217" href="#telrcv-217" title="217"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-218" href="#telrcv-218" title="218"> }</a>
<a class="sourceLine" id="telrcv-219" href="#telrcv-219" title="219"></a>
<a class="sourceLine" id="telrcv-220" href="#telrcv-220" title="220"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-221" href="#telrcv-221" title="221"><span class="co"> * Check for urgent data...</span></a>
<a class="sourceLine" id="telrcv-222" href="#telrcv-222" title="222"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-223" href="#telrcv-223" title="223"> <span class="cf">case</span> DM:</a>
<a class="sourceLine" id="telrcv-224" href="#telrcv-224" title="224"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: recv IAC"</span>, c));</a>
<a class="sourceLine" id="telrcv-225" href="#telrcv-225" title="225"> SYNCHing = stilloob(net);</a>
<a class="sourceLine" id="telrcv-226" href="#telrcv-226" title="226"> settimer(gotDM);</a>
<a class="sourceLine" id="telrcv-227" href="#telrcv-227" title="227"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-228" href="#telrcv-228" title="228"></a>
<a class="sourceLine" id="telrcv-229" href="#telrcv-229" title="229"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-230" href="#telrcv-230" title="230"><span class="co"> * Begin option subnegotiation...</span></a>
<a class="sourceLine" id="telrcv-231" href="#telrcv-231" title="231"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-232" href="#telrcv-232" title="232"> <span class="cf">case</span> SB:</a>
<a class="sourceLine" id="telrcv-233" href="#telrcv-233" title="233"> state = TS_SB;</a>
<a class="sourceLine" id="telrcv-234" href="#telrcv-234" title="234"> SB_CLEAR();</a>
<a class="sourceLine" id="telrcv-235" href="#telrcv-235" title="235"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-236" href="#telrcv-236" title="236"></a>
<a class="sourceLine" id="telrcv-237" href="#telrcv-237" title="237"> <span class="cf">case</span> WILL:</a>
<a class="sourceLine" id="telrcv-238" href="#telrcv-238" title="238"> state = TS_WILL;</a>
<a class="sourceLine" id="telrcv-239" href="#telrcv-239" title="239"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-240" href="#telrcv-240" title="240"></a>
<a class="sourceLine" id="telrcv-241" href="#telrcv-241" title="241"> <span class="cf">case</span> WONT:</a>
<a class="sourceLine" id="telrcv-242" href="#telrcv-242" title="242"> state = TS_WONT;</a>
<a class="sourceLine" id="telrcv-243" href="#telrcv-243" title="243"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-244" href="#telrcv-244" title="244"></a>
<a class="sourceLine" id="telrcv-245" href="#telrcv-245" title="245"> <span class="cf">case</span> DO:</a>
<a class="sourceLine" id="telrcv-246" href="#telrcv-246" title="246"> state = TS_DO;</a>
<a class="sourceLine" id="telrcv-247" href="#telrcv-247" title="247"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-248" href="#telrcv-248" title="248"></a>
<a class="sourceLine" id="telrcv-249" href="#telrcv-249" title="249"> <span class="cf">case</span> DONT:</a>
<a class="sourceLine" id="telrcv-250" href="#telrcv-250" title="250"> state = TS_DONT;</a>
<a class="sourceLine" id="telrcv-251" href="#telrcv-251" title="251"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-252" href="#telrcv-252" title="252"></a>
<a class="sourceLine" id="telrcv-253" href="#telrcv-253" title="253"> <span class="cf">case</span> EOR:</a>
<a class="sourceLine" id="telrcv-254" href="#telrcv-254" title="254"> <span class="cf">if</span> (his_state_is_will(TELOPT_EOR)) doeof();</a>
<a class="sourceLine" id="telrcv-255" href="#telrcv-255" title="255"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-256" href="#telrcv-256" title="256"></a>
<a class="sourceLine" id="telrcv-257" href="#telrcv-257" title="257"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-258" href="#telrcv-258" title="258"><span class="co"> * Handle RFC 10xx Telnet linemode option additions</span></a>
<a class="sourceLine" id="telrcv-259" href="#telrcv-259" title="259"><span class="co"> * to command stream (EOF, SUSP, ABORT).</span></a>
<a class="sourceLine" id="telrcv-260" href="#telrcv-260" title="260"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-261" href="#telrcv-261" title="261"> <span class="cf">case</span> xEOF:</a>
<a class="sourceLine" id="telrcv-262" href="#telrcv-262" title="262"> doeof();</a>
<a class="sourceLine" id="telrcv-263" href="#telrcv-263" title="263"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-264" href="#telrcv-264" title="264"></a>
<a class="sourceLine" id="telrcv-265" href="#telrcv-265" title="265"> <span class="cf">case</span> SUSP:</a>
<a class="sourceLine" id="telrcv-266" href="#telrcv-266" title="266"> sendsusp();</a>
<a class="sourceLine" id="telrcv-267" href="#telrcv-267" title="267"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-268" href="#telrcv-268" title="268"></a>
<a class="sourceLine" id="telrcv-269" href="#telrcv-269" title="269"> <span class="cf">case</span> ABORT:</a>
<a class="sourceLine" id="telrcv-270" href="#telrcv-270" title="270"> sendbrk();</a>
<a class="sourceLine" id="telrcv-271" href="#telrcv-271" title="271"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-272" href="#telrcv-272" title="272"></a>
<a class="sourceLine" id="telrcv-273" href="#telrcv-273" title="273"> <span class="cf">case</span> IAC:</a>
<a class="sourceLine" id="telrcv-274" href="#telrcv-274" title="274"> *pfrontp++ = c;</a>
<a class="sourceLine" id="telrcv-275" href="#telrcv-275" title="275"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-276" href="#telrcv-276" title="276"> }</a>
<a class="sourceLine" id="telrcv-277" href="#telrcv-277" title="277"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-278" href="#telrcv-278" title="278"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-279" href="#telrcv-279" title="279"></a>
<a class="sourceLine" id="telrcv-280" href="#telrcv-280" title="280"> <span class="cf">case</span> TS_SB:</a>
<a class="sourceLine" id="telrcv-281" href="#telrcv-281" title="281"> <span class="cf">if</span> (c == IAC) {</a>
<a class="sourceLine" id="telrcv-282" href="#telrcv-282" title="282"> state = TS_SE;</a>
<a class="sourceLine" id="telrcv-283" href="#telrcv-283" title="283"> }</a>
<a class="sourceLine" id="telrcv-284" href="#telrcv-284" title="284"> <span class="cf">else</span> {</a>
<a class="sourceLine" id="telrcv-285" href="#telrcv-285" title="285"> SB_ACCUM(c);</a>
<a class="sourceLine" id="telrcv-286" href="#telrcv-286" title="286"> }</a>
<a class="sourceLine" id="telrcv-287" href="#telrcv-287" title="287"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-288" href="#telrcv-288" title="288"></a>
<a class="sourceLine" id="telrcv-289" href="#telrcv-289" title="289"> <span class="cf">case</span> TS_SE:</a>
<a class="sourceLine" id="telrcv-290" href="#telrcv-290" title="290"> <span class="cf">if</span> (c != SE) {</a>
<a class="sourceLine" id="telrcv-291" href="#telrcv-291" title="291"> <span class="cf">if</span> (c != IAC) {</a>
<a class="sourceLine" id="telrcv-292" href="#telrcv-292" title="292"> <span class="co">/*</span></a>
<a class="sourceLine" id="telrcv-293" href="#telrcv-293" title="293"><span class="co"> * bad form of suboption negotiation.</span></a>
<a class="sourceLine" id="telrcv-294" href="#telrcv-294" title="294"><span class="co"> * handle it in such a way as to avoid</span></a>
<a class="sourceLine" id="telrcv-295" href="#telrcv-295" title="295"><span class="co"> * damage to local state. Parse</span></a>
<a class="sourceLine" id="telrcv-296" href="#telrcv-296" title="296"><span class="co"> * suboption buffer found so far,</span></a>
<a class="sourceLine" id="telrcv-297" href="#telrcv-297" title="297"><span class="co"> * then treat remaining stream as</span></a>
<a class="sourceLine" id="telrcv-298" href="#telrcv-298" title="298"><span class="co"> * another command sequence.</span></a>
<a class="sourceLine" id="telrcv-299" href="#telrcv-299" title="299"><span class="co"> */</span></a>
<a class="sourceLine" id="telrcv-300" href="#telrcv-300" title="300"></a>
<a class="sourceLine" id="telrcv-301" href="#telrcv-301" title="301"> <span class="co">/* for DIAGNOSTICS */</span></a>
<a class="sourceLine" id="telrcv-302" href="#telrcv-302" title="302"> SB_ACCUM(IAC);</a>
<a class="sourceLine" id="telrcv-303" href="#telrcv-303" title="303"> SB_ACCUM(c);</a>
<a class="sourceLine" id="telrcv-304" href="#telrcv-304" title="304"> subpointer -= <span class="dv">2</span>;</a>
<a class="sourceLine" id="telrcv-305" href="#telrcv-305" title="305"></a>
<a class="sourceLine" id="telrcv-306" href="#telrcv-306" title="306"> SB_TERM();</a>
<a class="sourceLine" id="telrcv-307" href="#telrcv-307" title="307"> suboption();</a>
<a class="sourceLine" id="telrcv-308" href="#telrcv-308" title="308"> state = TS_IAC;</a>
<a class="sourceLine" id="telrcv-309" href="#telrcv-309" title="309"> <span class="cf">goto</span> gotiac;</a>
<a class="sourceLine" id="telrcv-310" href="#telrcv-310" title="310"> }</a>
<a class="sourceLine" id="telrcv-311" href="#telrcv-311" title="311"> SB_ACCUM(c);</a>
<a class="sourceLine" id="telrcv-312" href="#telrcv-312" title="312"> state = TS_SB;</a>
<a class="sourceLine" id="telrcv-313" href="#telrcv-313" title="313"> }</a>
<a class="sourceLine" id="telrcv-314" href="#telrcv-314" title="314"> <span class="cf">else</span> {</a>
<a class="sourceLine" id="telrcv-315" href="#telrcv-315" title="315"> <span class="co">/* for DIAGNOSTICS */</span></a>
<a class="sourceLine" id="telrcv-316" href="#telrcv-316" title="316"> SB_ACCUM(IAC);</a>
<a class="sourceLine" id="telrcv-317" href="#telrcv-317" title="317"> SB_ACCUM(SE);</a>
<a class="sourceLine" id="telrcv-318" href="#telrcv-318" title="318"> subpointer -= <span class="dv">2</span>;</a>
<a class="sourceLine" id="telrcv-319" href="#telrcv-319" title="319"></a>
<a class="sourceLine" id="telrcv-320" href="#telrcv-320" title="320"> SB_TERM();</a>
<a class="sourceLine" id="telrcv-321" href="#telrcv-321" title="321"> suboption(); <span class="co">/* handle sub-option */</span></a>
<a class="sourceLine" id="telrcv-322" href="#telrcv-322" title="322"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-323" href="#telrcv-323" title="323"> }</a>
<a class="sourceLine" id="telrcv-324" href="#telrcv-324" title="324"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="telrcv-325" href="#telrcv-325" title="325"></a>
<a class="sourceLine" id="telrcv-326" href="#telrcv-326" title="326"> <span class="cf">case</span> TS_WILL:</a>
<a class="sourceLine" id="telrcv-327" href="#telrcv-327" title="327"> willoption(c);</a>
<a class="sourceLine" id="telrcv-328" href="#telrcv-328" title="328"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-329" href="#telrcv-329" title="329"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-330" href="#telrcv-330" title="330"></a>
<a class="sourceLine" id="telrcv-331" href="#telrcv-331" title="331"> <span class="cf">case</span> TS_WONT:</a>
<a class="sourceLine" id="telrcv-332" href="#telrcv-332" title="332"> wontoption(c);</a>
<a class="sourceLine" id="telrcv-333" href="#telrcv-333" title="333"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-334" href="#telrcv-334" title="334"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-335" href="#telrcv-335" title="335"></a>
<a class="sourceLine" id="telrcv-336" href="#telrcv-336" title="336"> <span class="cf">case</span> TS_DO:</a>
<a class="sourceLine" id="telrcv-337" href="#telrcv-337" title="337"> dooption(c);</a>
<a class="sourceLine" id="telrcv-338" href="#telrcv-338" title="338"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-339" href="#telrcv-339" title="339"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-340" href="#telrcv-340" title="340"></a>
<a class="sourceLine" id="telrcv-341" href="#telrcv-341" title="341"> <span class="cf">case</span> TS_DONT:</a>
<a class="sourceLine" id="telrcv-342" href="#telrcv-342" title="342"> dontoption(c);</a>
<a class="sourceLine" id="telrcv-343" href="#telrcv-343" title="343"> state = TS_DATA;</a>
<a class="sourceLine" id="telrcv-344" href="#telrcv-344" title="344"> <span class="cf">continue</span>;</a>
<a class="sourceLine" id="telrcv-345" href="#telrcv-345" title="345"></a>
<a class="sourceLine" id="telrcv-346" href="#telrcv-346" title="346"> <span class="cf">default</span>:</a>
<a class="sourceLine" id="telrcv-347" href="#telrcv-347" title="347"> syslog(LOG_ERR, <span class="st">"telnetd: panic state=%d</span><span class="sc">\n</span><span class="st">"</span>, state);</a>
<a class="sourceLine" id="telrcv-348" href="#telrcv-348" title="348"> printf(<span class="st">"telnetd: panic state=%d</span><span class="sc">\n</span><span class="st">"</span>, state);</a>
<a class="sourceLine" id="telrcv-349" href="#telrcv-349" title="349"> exit(<span class="dv">1</span>);</a>
<a class="sourceLine" id="telrcv-350" href="#telrcv-350" title="350"> }</a>
<a class="sourceLine" id="telrcv-351" href="#telrcv-351" title="351"> }</a>
<a class="sourceLine" id="telrcv-352" href="#telrcv-352" title="352">}</a></code></pre></div>
<p>A quick overview of the <code>telrcv</code> state machine is shown below.</p>
<p><img src="https://raw.githubusercontent.com/immunityinc/bravestarr/master/assets/telrcv.png" /></p>
<h3 id="option-handling"><span class="header-section-number">2.3.1</span> Option handling</h3>
<p>An important part of the telnet protocol is option negotiation. This is done through IAC DO, IAC DONT, IAC WILL, and IAC WONT command sequences. The meaning of these sequences is as follows:</p>
<ul>
<li>DO: requests the peer to use a certain negotiable option.</li>
<li>DONT: requests the peer to not use a certain negotiable option.</li>
<li>WILL: notifies the peer we will use a certain negotiable option.</li>
<li>WONT: notifies the peer we will not use a certain negotiable option.</li>
</ul>
<p>Within the scope of our discussion, we will use only use IAC DO commands, which result in a call to <a href="#telrcv-337"><code>dooption</code></a> in the state machine. The code can be found below, and the most important thing to note is that unknown option codes will be replied to with <a href="#dooption-907"><code>send_wont</code></a> which repeats the requested option code. This means that a sequence such as IAC DO U, where U is an unknown option, will be replied to with IAC WONT U.</p>
<div class="sourceCode" id="dooption" data-startFrom="789"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="dooption-789" href="#dooption-789" title="789"><span class="dt">void</span> dooption(<span class="dt">int</span> option) {</a>
<a class="sourceLine" id="dooption-790" href="#dooption-790" title="790"> <span class="dt">int</span> changeok = <span class="dv">0</span>;</a>
<a class="sourceLine" id="dooption-791" href="#dooption-791" title="791"></a>
<a class="sourceLine" id="dooption-792" href="#dooption-792" title="792"> <span class="co">/*</span></a>
<a class="sourceLine" id="dooption-793" href="#dooption-793" title="793"><span class="co"> * Process client input.</span></a>
<a class="sourceLine" id="dooption-794" href="#dooption-794" title="794"><span class="co"> */</span></a>
<a class="sourceLine" id="dooption-795" href="#dooption-795" title="795"></a>
<a class="sourceLine" id="dooption-796" href="#dooption-796" title="796"> DIAG(TD_OPTIONS, printoption(<span class="st">"td: recv do"</span>, option));</a>
<a class="sourceLine" id="dooption-797" href="#dooption-797" title="797"></a>
<a class="sourceLine" id="dooption-798" href="#dooption-798" title="798"> <span class="cf">if</span> (will_wont_resp[option]) {</a>
<a class="sourceLine" id="dooption-799" href="#dooption-799" title="799"> will_wont_resp[option]--;</a>
<a class="sourceLine" id="dooption-800" href="#dooption-800" title="800"> <span class="cf">if</span> (will_wont_resp[option] && my_state_is_will(option))</a>
<a class="sourceLine" id="dooption-801" href="#dooption-801" title="801"> will_wont_resp[option]--;</a>
<a class="sourceLine" id="dooption-802" href="#dooption-802" title="802"> }</a>
<a class="sourceLine" id="dooption-803" href="#dooption-803" title="803"> <span class="cf">if</span> ((will_wont_resp[option] == <span class="dv">0</span>) && (my_want_state_is_wont(option))) {</a>
<a class="sourceLine" id="dooption-804" href="#dooption-804" title="804"> <span class="cf">switch</span> (option) {</a>
<a class="sourceLine" id="dooption-805" href="#dooption-805" title="805"> <span class="cf">case</span> TELOPT_ECHO:</a></code></pre></div>
<div class="sourceCode" id="dooption" data-startFrom="813"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="dooption-813" href="#dooption-813" title="813"> {</a>
<a class="sourceLine" id="dooption-814" href="#dooption-814" title="814"> init_termbuf();</a>
<a class="sourceLine" id="dooption-815" href="#dooption-815" title="815"> tty_setecho(<span class="dv">1</span>);</a>
<a class="sourceLine" id="dooption-816" href="#dooption-816" title="816"> set_termbuf();</a>
<a class="sourceLine" id="dooption-817" href="#dooption-817" title="817"> }</a>
<a class="sourceLine" id="dooption-818" href="#dooption-818" title="818"> changeok++;</a>
<a class="sourceLine" id="dooption-819" href="#dooption-819" title="819"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="dooption-820" href="#dooption-820" title="820"></a>
<a class="sourceLine" id="dooption-821" href="#dooption-821" title="821"> <span class="cf">case</span> TELOPT_BINARY:</a>
<a class="sourceLine" id="dooption-822" href="#dooption-822" title="822"> init_termbuf();</a>
<a class="sourceLine" id="dooption-823" href="#dooption-823" title="823"> tty_binaryout(<span class="dv">1</span>);</a>
<a class="sourceLine" id="dooption-824" href="#dooption-824" title="824"> set_termbuf();</a>
<a class="sourceLine" id="dooption-825" href="#dooption-825" title="825"> changeok++;</a>
<a class="sourceLine" id="dooption-826" href="#dooption-826" title="826"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="dooption-827" href="#dooption-827" title="827"></a>
<a class="sourceLine" id="dooption-828" href="#dooption-828" title="828"> <span class="cf">case</span> TELOPT_SGA:</a></code></pre></div>
<div class="sourceCode" id="dooption" data-startFrom="852"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="dooption-852" href="#dooption-852" title="852"> turn_on_sga = <span class="dv">0</span>;</a></code></pre></div>
<div class="sourceCode" id="dooption" data-startFrom="854"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="dooption-854" href="#dooption-854" title="854"> changeok++;</a>
<a class="sourceLine" id="dooption-855" href="#dooption-855" title="855"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="dooption-856" href="#dooption-856" title="856"></a>
<a class="sourceLine" id="dooption-857" href="#dooption-857" title="857"> <span class="cf">case</span> TELOPT_STATUS:</a>
<a class="sourceLine" id="dooption-858" href="#dooption-858" title="858"> changeok++;</a>
<a class="sourceLine" id="dooption-859" href="#dooption-859" title="859"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="dooption-860" href="#dooption-860" title="860"></a>
<a class="sourceLine" id="dooption-861" href="#dooption-861" title="861"> <span class="cf">case</span> TELOPT_TM:</a>
<a class="sourceLine" id="dooption-862" href="#dooption-862" title="862"> <span class="co">/*</span></a>
<a class="sourceLine" id="dooption-863" href="#dooption-863" title="863"><span class="co"> * Special case for TM. We send a WILL, but</span></a>
<a class="sourceLine" id="dooption-864" href="#dooption-864" title="864"><span class="co"> * pretend we sent a WONT.</span></a>
<a class="sourceLine" id="dooption-865" href="#dooption-865" title="865"><span class="co"> */</span></a>
<a class="sourceLine" id="dooption-866" href="#dooption-866" title="866"> send_will(option, <span class="dv">0</span>);</a>
<a class="sourceLine" id="dooption-867" href="#dooption-867" title="867"> set_my_want_state_wont(option);</a>
<a class="sourceLine" id="dooption-868" href="#dooption-868" title="868"> set_my_state_wont(option);</a>
<a class="sourceLine" id="dooption-869" href="#dooption-869" title="869"> <span class="cf">return</span>;</a>
<a class="sourceLine" id="dooption-870" href="#dooption-870" title="870"></a>
<a class="sourceLine" id="dooption-871" href="#dooption-871" title="871"> <span class="cf">case</span> TELOPT_LOGOUT:</a>
<a class="sourceLine" id="dooption-872" href="#dooption-872" title="872"> <span class="co">/*</span></a>
<a class="sourceLine" id="dooption-873" href="#dooption-873" title="873"><span class="co"> * When we get a LOGOUT option, respond</span></a>
<a class="sourceLine" id="dooption-874" href="#dooption-874" title="874"><span class="co"> * with a WILL LOGOUT, make sure that</span></a>
<a class="sourceLine" id="dooption-875" href="#dooption-875" title="875"><span class="co"> * it gets written out to the network,</span></a>
<a class="sourceLine" id="dooption-876" href="#dooption-876" title="876"><span class="co"> * and then just go away...</span></a>
<a class="sourceLine" id="dooption-877" href="#dooption-877" title="877"><span class="co"> */</span></a>
<a class="sourceLine" id="dooption-878" href="#dooption-878" title="878"> set_my_want_state_will(TELOPT_LOGOUT);</a>
<a class="sourceLine" id="dooption-879" href="#dooption-879" title="879"> send_will(TELOPT_LOGOUT, <span class="dv">0</span>);</a>
<a class="sourceLine" id="dooption-880" href="#dooption-880" title="880"> set_my_state_will(TELOPT_LOGOUT);</a>
<a class="sourceLine" id="dooption-881" href="#dooption-881" title="881"> (<span class="dt">void</span>)netflush();</a>
<a class="sourceLine" id="dooption-882" href="#dooption-882" title="882"> cleanup(<span class="dv">0</span>);</a>
<a class="sourceLine" id="dooption-883" href="#dooption-883" title="883"> <span class="co">/* NOT REACHED */</span></a>
<a class="sourceLine" id="dooption-884" href="#dooption-884" title="884"> <span class="cf">break</span>;</a></code></pre></div>
<div class="sourceCode" id="dooption" data-startFrom="891"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="dooption-891" href="#dooption-891" title="891"> <span class="cf">case</span> TELOPT_LINEMODE:</a>
<a class="sourceLine" id="dooption-892" href="#dooption-892" title="892"> <span class="cf">case</span> TELOPT_TTYPE:</a>
<a class="sourceLine" id="dooption-893" href="#dooption-893" title="893"> <span class="cf">case</span> TELOPT_NAWS:</a>
<a class="sourceLine" id="dooption-894" href="#dooption-894" title="894"> <span class="cf">case</span> TELOPT_TSPEED:</a>
<a class="sourceLine" id="dooption-895" href="#dooption-895" title="895"> <span class="cf">case</span> TELOPT_LFLOW:</a>
<a class="sourceLine" id="dooption-896" href="#dooption-896" title="896"> <span class="cf">case</span> TELOPT_XDISPLOC:</a>
<a class="sourceLine" id="dooption-897" href="#dooption-897" title="897"> <span class="cf">case</span> TELOPT_ENVIRON:</a>
<a class="sourceLine" id="dooption-898" href="#dooption-898" title="898"> <span class="cf">default</span>:</a>
<a class="sourceLine" id="dooption-899" href="#dooption-899" title="899"> <span class="cf">break</span>;</a>
<a class="sourceLine" id="dooption-900" href="#dooption-900" title="900"> }</a>
<a class="sourceLine" id="dooption-901" href="#dooption-901" title="901"> <span class="cf">if</span> (changeok) {</a>
<a class="sourceLine" id="dooption-902" href="#dooption-902" title="902"> set_my_want_state_will(option);</a>
<a class="sourceLine" id="dooption-903" href="#dooption-903" title="903"> send_will(option, <span class="dv">0</span>);</a>
<a class="sourceLine" id="dooption-904" href="#dooption-904" title="904"> }</a>
<a class="sourceLine" id="dooption-905" href="#dooption-905" title="905"> <span class="cf">else</span> {</a>
<a class="sourceLine" id="dooption-906" href="#dooption-906" title="906"> will_wont_resp[option]++;</a>
<a class="sourceLine" id="dooption-907" href="#dooption-907" title="907"> send_wont(option, <span class="dv">0</span>);</a>
<a class="sourceLine" id="dooption-908" href="#dooption-908" title="908"> }</a>
<a class="sourceLine" id="dooption-909" href="#dooption-909" title="909"> }</a>
<a class="sourceLine" id="dooption-910" href="#dooption-910" title="910"> set_my_state_will(option);</a>
<a class="sourceLine" id="dooption-911" href="#dooption-911" title="911">}</a></code></pre></div>
<h2 id="basis-of-the-vulnerability"><span class="header-section-number">2.4</span> Basis of the vulnerability</h2>
<p>The <code>netclear</code> and <code>nextitem</code> functions can scan past the end of <code>netobuf</code> and corrupt memory in the data segment under certain conditions. We will not yet discuss whether these conditions can be met, as this is complicated, but first focus on explaining the issue in these two functions.</p>
<h3 id="the-netclear-function"><span class="header-section-number">2.4.1</span> The netclear() function</h3>
<p>The <code>netclear</code> function, as depicted below, reworks the network output buffer <code>netobuf</code> to remove data that was already sent, as well as unwanted items. An item here is either a single data byte, or an IAC command sequence of two or more bytes. The <code>nextitem</code> function is used to partition <code>netobuf</code> into items. We will discuss the implementation of this function later.</p>
<p>At <a href="#netclear-277">277</a> <code>thisitem</code> is set to <code>netobuf</code> and at <a href="#netclear-280">280</a> items are skipped as long as they are smaller than <code>nbackp</code>. As mentioned before, <code>nbackp</code> points to the location in <code>netobuf</code> that has not already been sent over the network. Anything before <code>nbackp</code> can therefore be discarded. At <a href="#netclear-289">289</a> <code>good</code> is set to <code>netobuf</code> as well. This is the destination pointer for copying <code>netobuf</code> data that is retained, so this copy will be performed inline.</p>
<p>At <a href="#netclear-292">292</a> items will be processed from <code>thisitem</code> while it is smaller than <code>nfrontp</code>. Items that do not match the <code>wewant</code> macro will be skipped at <a href="#netclear-305">305</a>. An item that does match the <code>wewant</code> macro will first be coalesced to adjacent wanted items in the loop at <a href="#netclear-297">297</a> and then copied back to <code>good</code> at <a href="#netclear-301">301</a>.</p>
<div class="sourceCode" id="netclear" data-startFrom="267"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="netclear-267" href="#netclear-267" title="267"><span class="dt">void</span> netclear(<span class="dt">void</span>)</a>
<a class="sourceLine" id="netclear-268" href="#netclear-268" title="268">{</a>
<a class="sourceLine" id="netclear-269" href="#netclear-269" title="269"> <span class="dt">register</span> <span class="dt">char</span> *thisitem, *next;</a>
<a class="sourceLine" id="netclear-270" href="#netclear-270" title="270"> <span class="dt">char</span> *good;</a>
<a class="sourceLine" id="netclear-271" href="#netclear-271" title="271"><span class="pp">#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \</span></a>
<a class="sourceLine" id="netclear-272" href="#netclear-272" title="272"><span class="pp"> ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))</span></a>
<a class="sourceLine" id="netclear-273" href="#netclear-273" title="273"></a>
<a class="sourceLine" id="netclear-274" href="#netclear-274" title="274"><span class="pp">#if defined(ENCRYPT)</span></a>
<a class="sourceLine" id="netclear-275" href="#netclear-275" title="275"> thisitem = nclearto > netobuf ? nclearto : netobuf;</a>
<a class="sourceLine" id="netclear-276" href="#netclear-276" title="276"><span class="pp">#else</span></a>
<a class="sourceLine" id="netclear-277" href="#netclear-277" title="277"> thisitem = netobuf;</a>
<a class="sourceLine" id="netclear-278" href="#netclear-278" title="278"><span class="pp">#endif</span></a>
<a class="sourceLine" id="netclear-279" href="#netclear-279" title="279"></a>
<a class="sourceLine" id="netclear-280" href="#netclear-280" title="280"> <span class="cf">while</span> ((next = nextitem(thisitem)) <= nbackp) {</a>
<a class="sourceLine" id="netclear-281" href="#netclear-281" title="281"> thisitem = next;</a>
<a class="sourceLine" id="netclear-282" href="#netclear-282" title="282"> }</a>
<a class="sourceLine" id="netclear-283" href="#netclear-283" title="283"></a>
<a class="sourceLine" id="netclear-284" href="#netclear-284" title="284"> <span class="co">/* Now, thisitem is first before/at boundary. */</span></a>
<a class="sourceLine" id="netclear-285" href="#netclear-285" title="285"></a>
<a class="sourceLine" id="netclear-286" href="#netclear-286" title="286"><span class="pp">#if defined(ENCRYPT)</span></a>
<a class="sourceLine" id="netclear-287" href="#netclear-287" title="287"> good = nclearto > netobuf ? nclearto : netobuf;</a>
<a class="sourceLine" id="netclear-288" href="#netclear-288" title="288"><span class="pp">#else</span></a>
<a class="sourceLine" id="netclear-289" href="#netclear-289" title="289"> good = netobuf; <span class="co">/* where the good bytes go */</span></a>
<a class="sourceLine" id="netclear-290" href="#netclear-290" title="290"><span class="pp">#endif</span></a>
<a class="sourceLine" id="netclear-291" href="#netclear-291" title="291"></a>
<a class="sourceLine" id="netclear-292" href="#netclear-292" title="292"> <span class="cf">while</span> (nfrontp > thisitem) {</a>
<a class="sourceLine" id="netclear-293" href="#netclear-293" title="293"> <span class="cf">if</span> (wewant(thisitem)) {</a>
<a class="sourceLine" id="netclear-294" href="#netclear-294" title="294"> <span class="dt">int</span> length;</a>
<a class="sourceLine" id="netclear-295" href="#netclear-295" title="295"></a>
<a class="sourceLine" id="netclear-296" href="#netclear-296" title="296"> next = thisitem;</a>
<a class="sourceLine" id="netclear-297" href="#netclear-297" title="297"> <span class="cf">do</span> {</a>
<a class="sourceLine" id="netclear-298" href="#netclear-298" title="298"> next = nextitem(next);</a>
<a class="sourceLine" id="netclear-299" href="#netclear-299" title="299"> } <span class="cf">while</span> (wewant(next) && (nfrontp > next));</a>
<a class="sourceLine" id="netclear-300" href="#netclear-300" title="300"> length = next-thisitem;</a>
<a class="sourceLine" id="netclear-301" href="#netclear-301" title="301"> bcopy(thisitem, good, length);</a>
<a class="sourceLine" id="netclear-302" href="#netclear-302" title="302"> good += length;</a>
<a class="sourceLine" id="netclear-303" href="#netclear-303" title="303"> thisitem = next;</a>
<a class="sourceLine" id="netclear-304" href="#netclear-304" title="304"> } <span class="cf">else</span> {</a>
<a class="sourceLine" id="netclear-305" href="#netclear-305" title="305"> thisitem = nextitem(thisitem);</a>
<a class="sourceLine" id="netclear-306" href="#netclear-306" title="306"> }</a>
<a class="sourceLine" id="netclear-307" href="#netclear-307" title="307"> }</a>
<a class="sourceLine" id="netclear-308" href="#netclear-308" title="308"></a>
<a class="sourceLine" id="netclear-309" href="#netclear-309" title="309"> nbackp = netobuf;</a>
<a class="sourceLine" id="netclear-310" href="#netclear-310" title="310"> nfrontp = good; <span class="co">/* next byte to be sent */</span></a>
<a class="sourceLine" id="netclear-311" href="#netclear-311" title="311"> neturg = <span class="dv">0</span>;</a>
<a class="sourceLine" id="netclear-312" href="#netclear-312" title="312">} <span class="co">/* end of netclear */</span></a></code></pre></div>
<h3 id="the-nextitem-function"><span class="header-section-number">2.4.2</span> The nextitem() function</h3>
<p>The <code>nextitem</code> function is meant to find the item starting after the item pointed to by <code>current</code>.</p>
<p>The function performs no bounds checking, and can return pointers past the end of the region <code>current</code>. Offsets should be constrained to not extend past the end of the <code>current</code> region at <a href="#nextitem-232">232</a>, <a href="#nextitem-246">246</a>, and most notably <a href="#nextitem-240">240</a>. The latter over-indexation is the most dangerous one, as it means the function will keep scanning memory until it finds a IAC, SE sequence in memory and return a pointer to the byte after it.</p>
<div class="sourceCode" id="nextitem" data-startFrom="220"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="nextitem-220" href="#nextitem-220" title="220"><span class="dt">static</span></a>
<a class="sourceLine" id="nextitem-221" href="#nextitem-221" title="221"><span class="dt">char</span> *</a>
<a class="sourceLine" id="nextitem-222" href="#nextitem-222" title="222">nextitem(<span class="dt">char</span> *current)</a>
<a class="sourceLine" id="nextitem-223" href="#nextitem-223" title="223">{</a>
<a class="sourceLine" id="nextitem-224" href="#nextitem-224" title="224"> <span class="cf">if</span> ((*current&<span class="bn">0xff</span>) != IAC) {</a>
<a class="sourceLine" id="nextitem-225" href="#nextitem-225" title="225"> <span class="cf">return</span> current+<span class="dv">1</span>;</a>
<a class="sourceLine" id="nextitem-226" href="#nextitem-226" title="226"> }</a>
<a class="sourceLine" id="nextitem-227" href="#nextitem-227" title="227"> <span class="cf">switch</span> (*(current+<span class="dv">1</span>)&<span class="bn">0xff</span>) {</a>
<a class="sourceLine" id="nextitem-228" href="#nextitem-228" title="228"> <span class="cf">case</span> DO:</a>
<a class="sourceLine" id="nextitem-229" href="#nextitem-229" title="229"> <span class="cf">case</span> DONT:</a>
<a class="sourceLine" id="nextitem-230" href="#nextitem-230" title="230"> <span class="cf">case</span> WILL:</a>
<a class="sourceLine" id="nextitem-231" href="#nextitem-231" title="231"> <span class="cf">case</span> WONT:</a>
<a class="sourceLine" id="nextitem-232" href="#nextitem-232" title="232"> <span class="cf">return</span> current+<span class="dv">3</span>;</a>
<a class="sourceLine" id="nextitem-233" href="#nextitem-233" title="233"> <span class="cf">case</span> SB: <span class="co">/* loop forever looking for the SE */</span></a>
<a class="sourceLine" id="nextitem-234" href="#nextitem-234" title="234"> {</a>
<a class="sourceLine" id="nextitem-235" href="#nextitem-235" title="235"> <span class="dt">register</span> <span class="dt">char</span> *look = current+<span class="dv">2</span>;</a>
<a class="sourceLine" id="nextitem-236" href="#nextitem-236" title="236"></a>
<a class="sourceLine" id="nextitem-237" href="#nextitem-237" title="237"> <span class="cf">for</span> (;;) {</a>
<a class="sourceLine" id="nextitem-238" href="#nextitem-238" title="238"> <span class="cf">if</span> ((*look++&<span class="bn">0xff</span>) == IAC) {</a>
<a class="sourceLine" id="nextitem-239" href="#nextitem-239" title="239"> <span class="cf">if</span> ((*look++&<span class="bn">0xff</span>) == SE) {</a>
<a class="sourceLine" id="nextitem-240" href="#nextitem-240" title="240"> <span class="cf">return</span> look;</a>
<a class="sourceLine" id="nextitem-241" href="#nextitem-241" title="241"> }</a>
<a class="sourceLine" id="nextitem-242" href="#nextitem-242" title="242"> }</a>
<a class="sourceLine" id="nextitem-243" href="#nextitem-243" title="243"> }</a>
<a class="sourceLine" id="nextitem-244" href="#nextitem-244" title="244"> }</a>
<a class="sourceLine" id="nextitem-245" href="#nextitem-245" title="245"> <span class="cf">default</span>:</a>
<a class="sourceLine" id="nextitem-246" href="#nextitem-246" title="246"> <span class="cf">return</span> current+<span class="dv">2</span>;</a>
<a class="sourceLine" id="nextitem-247" href="#nextitem-247" title="247"> }</a>
<a class="sourceLine" id="nextitem-248" href="#nextitem-248" title="248">} <span class="co">/* end of nextitem */</span></a></code></pre></div>
<h3 id="data-segment-corruption-idea"><span class="header-section-number">2.4.3</span> Data segment corruption idea</h3>
<p>Given the code discussed above, if we could get <code>netobuf</code> to contain an opening IAC SB sequence but not a closing IAC SE sequence, the <code>nextitem</code> function would scan past the end of <code>netobuf</code> to find the closing sequence. In case this sequence is not present, this would result in accessing unmapped memory and thus a segmentation fault. However, in case such a sequence exists past the end of <code>netobuf</code>, the loop at <a href="#netclear-297">297</a> would terminate due to <code>next</code> being larger than <code>nfrontp</code> (which should not point past the end of <code>netobuf</code>), and result in <code>length</code> being too large. Depending on the position of <code>good</code> in <code>netobuf</code>, this can result in an out of bounds read (if <code>length</code> fits at <code>good</code> and just overindexes <code>thisitem</code>) or an out of bounds read and write (if <code>length</code> does not fit at <code>good</code>).</p>
<h2 id="network-output-buffer-control"><span class="header-section-number">2.5</span> Network output buffer control</h2>
<p>We have seen previously that we have a data segment overflow if we can create an IAC SB sequence in <code>netobuf</code> without a terminating IAC SE sequence. In order to do so, we will have to look more closely at primitives we have to control this buffer.</p>
<p>First we will discuss the ways in which we can have the daemon add data to the buffer and what parts of that data we control. Then we will look at how these functions work internally, and discuss how the buffer is flushed when full and how urgent data is handled. Finally we will use everything discussed so far to create an unterminated IAC SB sequence in the network output buffer.</p>
<h3 id="primitives-adding-data-to-the-network-output-buffer"><span class="header-section-number">2.5.1</span> Primitives adding data to the network output buffer</h3>
<p>We cannot add arbitrary data to <code>netobuf</code> directly. Instead the daemon will add to it based on the network input we provided in <code>netibuf</code>. To create a situation where we have an unterminated IAC SB sequence in <code>netobuf</code>, we need to see to what extent we can control parts of the data that is added to <code>netobuf</code>.</p>
<p>There are several ways in which data can be added to <code>netobuf</code> in the source code. The <code>output_data</code> and <code>output_datalen</code> functions in <code>utility.c</code> can be used to do so, as well as the <code>netoprintf</code> macro in <code>ext.h</code>. Finally the location the <code>nfrontp</code> pointer refers to is directly written to in <code>state.c</code> (handling the IAC AO sequence) and <code>telnetd.c</code> (data read from the pty).</p>
<div class="sourceCode" id="ext.h" data-startFrom="90"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="ext.h-90" href="#ext.h-90" title="90"><span class="pp">#define netoprintf output_data</span></a>
<a class="sourceLine" id="ext.h-91" href="#ext.h-91" title="91"><span class="dt">int</span> output_data(<span class="dt">const</span> <span class="dt">char</span> *, ...) __attribute((format (printf, <span class="dv">1</span>, <span class="dv">2</span>))); </a>
<a class="sourceLine" id="ext.h-92" href="#ext.h-92" title="92"><span class="dt">void</span> output_datalen(<span class="dt">const</span> <span class="dt">char</span> *, <span class="dt">int</span>);</a></code></pre></div>
<p>We will look at the call sites of <code>netoprintf</code>, <code>output_data</code>, and <code>output_datalen</code>. We can see the <code>netoprintf</code> macro is a simple wrapper around the <code>output_data</code> function. It is called in a lot of places in the source tree, but a lot of these places are for diagnostics. This means the build-time definition <code>DIAGNOSTICS</code> will have to be set, and telnetd itself would have to have been started with the <code>-D</code> command line option. This is not typically the case, so we will not consider such use. Below are the call-sites we identified:</p>
<dl>
<dt><code>state.c</code></dt>
<dd><ul>
<li><code>send_do</code>, <code>send_dont</code>, <code>send_will</code>, and <code>send_wont</code>. Each of these add 3 bytes to <code>netobuf</code>, of which we control the last byte.</li>
</ul>
</dd>
<dd><ul>
<li>IAC AO handling at in <a href="#telrcv-195">telrcv</a>.</li>
</ul>
</dd>
<dt><code>termstat.c</code></dt>
<dd>Only for <code>LINEMODE</code> defined compilations. This is not the case for Fedora 31.
</dd>
<dt><code>telnetd.c</code></dt>
<dd><ul>
<li>Once for every option in response to the <code>TELOPT_TSPEED</code>, <code>TELOPT_XDISPLOC</code>, <code>TELOPT_ENVIRON</code>, and <code>TELOPT_TTYPE</code> telnet options if the client supports them.</li>
</ul>
</dd>
<dd><ul>
<li>In the <code>_gettermname</code> function if the client supports <code>TELOPT_TTYPE</code>.</li>
</ul>
</dd>
<dd><ul>
<li>When writing the IAC DM sequence in the pty handling main loop.</li>
</ul>
</dd>
<dd><ul>
<li>When flow control is supported by the client in the pty handling main loop.</li>
</ul>
</dd>
<dd><ul>
<li>In the <code>recv_ayt</code> function. This is used in response to IAC AYT requests, and is interesting as it results in amplification.</li>
</ul>
</dd>
<dt><code>slc.c</code></dt>
<dd><ul>
<li>In the <code>end_slc</code> function used when handling the <code>TELOPT_LINEMODE</code> option in <code>state.c</code>. This needs a <code>LINEMODE</code> defined build, which is not the case on Fedora 31.</li>
</ul>
</dd>
</dl>
<p>Out of all these call sites, we have identified two that we can use for the development of the exploit in this article. We will rely on the <code>send_wont</code> call in <a href="#tlercv-337"><code>dooption</code></a> and the IAC DM addition at <code>nfrontp</code> in <a href="#telrcv-195"><code>telrcv</code></a>. Note that in the latter case there is no direct check to ensure this will not access memory past the end of <code>netobuf</code>. It is possible to crash the daemon using this, but that is outside the scope of the current document. The reason we picked these two call sites will become apparent later on. For now, remember that we will use two primitives for adding data to <code>netobuf</code>, one which will add a 3 byte IAC WONT sequence and then a byte we can control - provided it is not a valid option. And one which will add a 2 byte IAC DM sequence.</p>
<p>With these two primitives in mind, we will further look into how output data is buffered in <code>netobuf</code> and how a shortage of buffer space is handled.</p>
<h3 id="output-function-internals"><span class="header-section-number">2.5.2</span> Output function internals</h3>
<p>The <code>output_data</code> function is a simple wrapper around the <code>output_datalen</code> function that support format strings. It is listed below for completeness.</p>
<div class="sourceCode" id="output_data" data-startFrom="63"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="output_data-63" href="#output_data-63" title="63"><span class="dt">int</span></a>
<a class="sourceLine" id="output_data-64" href="#output_data-64" title="64">output_data(<span class="dt">const</span> <span class="dt">char</span> *format, ...)</a>
<a class="sourceLine" id="output_data-65" href="#output_data-65" title="65">{</a>
<a class="sourceLine" id="output_data-66" href="#output_data-66" title="66"> <span class="dt">va_list</span> args;</a>
<a class="sourceLine" id="output_data-67" href="#output_data-67" title="67"> <span class="dt">int</span> len;</a>
<a class="sourceLine" id="output_data-68" href="#output_data-68" title="68"> <span class="dt">char</span> *buf;</a>
<a class="sourceLine" id="output_data-69" href="#output_data-69" title="69"></a>
<a class="sourceLine" id="output_data-70" href="#output_data-70" title="70"> va_start(args, format);</a>
<a class="sourceLine" id="output_data-71" href="#output_data-71" title="71"> <span class="cf">if</span> ((len = vasprintf(&buf, format, args)) == -<span class="dv">1</span>)</a>
<a class="sourceLine" id="output_data-72" href="#output_data-72" title="72"> <span class="cf">return</span> -<span class="dv">1</span>;</a>
<a class="sourceLine" id="output_data-73" href="#output_data-73" title="73"> output_datalen(buf, len);</a>
<a class="sourceLine" id="output_data-74" href="#output_data-74" title="74"> va_end(args);</a>
<a class="sourceLine" id="output_data-75" href="#output_data-75" title="75"> free(buf);</a>
<a class="sourceLine" id="output_data-76" href="#output_data-76" title="76"> <span class="cf">return</span> (len);</a>
<a class="sourceLine" id="output_data-77" href="#output_data-77" title="77">} </a></code></pre></div>
<p>The <code>output_datalen</code> function itself does most of the work, and is more interesting. The source tarball shows that <code>netobuf</code> is <code>BUFSIZE+NETSLOP</code> bytes big, which is typically 8256 bytes. <code>nfrontp</code> points to the position in <code>netobuf</code> where data will be added to, so <code>nfrontp - netobuf</code> is the size of <code>netobuf</code> that is currently in use.</p>
<p>The while loop at <a href="#output_datalen-85">85</a> will partition the input in <code>buf</code> into chunks of at most <code>BUFSIZE</code> bytes and process these chunks in order. In case there is not enough space available to copy the current chunk into <code>netobuf</code>, the <code>netflush</code> function will be called to create space at <a href="#output_datalen-88">88</a>, and as flushing will adjust <code>nfrontp</code> the remaining space is also recalculated.</p>
<p>The chunk is then copied into the network output buffer at <a href="#output_datalen-94">94</a> and the related variables are updated.</p>
<div class="sourceCode" id="output_datalen" data-startFrom="79"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="output_datalen-79" href="#output_datalen-79" title="79"><span class="dt">void</span> </a>
<a class="sourceLine" id="output_datalen-80" href="#output_datalen-80" title="80">output_datalen(<span class="dt">const</span> <span class="dt">char</span> *buf, <span class="dt">int</span> len)</a>
<a class="sourceLine" id="output_datalen-81" href="#output_datalen-81" title="81">{ </a>
<a class="sourceLine" id="output_datalen-82" href="#output_datalen-82" title="82"> <span class="dt">int</span> remaining, copied;</a>
<a class="sourceLine" id="output_datalen-83" href="#output_datalen-83" title="83"> </a>
<a class="sourceLine" id="output_datalen-84" href="#output_datalen-84" title="84"> remaining = BUFSIZ - (nfrontp - netobuf);</a>
<a class="sourceLine" id="output_datalen-85" href="#output_datalen-85" title="85"> <span class="cf">while</span> (len > <span class="dv">0</span>) {</a>
<a class="sourceLine" id="output_datalen-86" href="#output_datalen-86" title="86"> <span class="co">/* Free up enough space if the room is too low*/</span></a>
<a class="sourceLine" id="output_datalen-87" href="#output_datalen-87" title="87"> <span class="cf">if</span> ((len > BUFSIZ ? BUFSIZ : len) > remaining) {</a>
<a class="sourceLine" id="output_datalen-88" href="#output_datalen-88" title="88"> netflush();</a>
<a class="sourceLine" id="output_datalen-89" href="#output_datalen-89" title="89"> remaining = BUFSIZ - (nfrontp - netobuf);</a>
<a class="sourceLine" id="output_datalen-90" href="#output_datalen-90" title="90"> }</a>
<a class="sourceLine" id="output_datalen-91" href="#output_datalen-91" title="91"></a>
<a class="sourceLine" id="output_datalen-92" href="#output_datalen-92" title="92"> <span class="co">/* Copy out as much as will fit */</span></a>
<a class="sourceLine" id="output_datalen-93" href="#output_datalen-93" title="93"> copied = remaining > len ? len : remaining;</a>
<a class="sourceLine" id="output_datalen-94" href="#output_datalen-94" title="94"> memmove(nfrontp, buf, copied);</a>
<a class="sourceLine" id="output_datalen-95" href="#output_datalen-95" title="95"> nfrontp += copied;</a>
<a class="sourceLine" id="output_datalen-96" href="#output_datalen-96" title="96"> len -= copied;</a>
<a class="sourceLine" id="output_datalen-97" href="#output_datalen-97" title="97"> remaining -= copied;</a>
<a class="sourceLine" id="output_datalen-98" href="#output_datalen-98" title="98"> buf += copied;</a>
<a class="sourceLine" id="output_datalen-99" href="#output_datalen-99" title="99"> }</a>
<a class="sourceLine" id="output_datalen-100" href="#output_datalen-100" title="100"> <span class="cf">return</span>;</a>
<a class="sourceLine" id="output_datalen-101" href="#output_datalen-101" title="101">}</a></code></pre></div>
<p>What remains is a closer look at the <code>netflush</code> function, as this handles what happens when there is not enough buffer space available. The attentive reader may already have noticed that <code>remaining</code> at <a href="#output_datalen-89">89</a> is recalculated rather than reset to <code>BUFSIZ</code>, which hints at the fact that <code>netflush</code> may not drain the network output buffer completely when called.</p>
<h3 id="netflush-internals"><span class="header-section-number">2.5.3</span> netflush internals</h3>
<p>The <code>netflush</code> implementation can be found below. If there is data that has not been written yet in <code>netobuf</code> – that is, all data between <code>nbackp</code> and <code>nfrontp</code> – it will be handled in the conditional code block at <a href="#netflush-325">325</a>. When no urgent data has been added to the network output buffer, data starting at <code>nbackp</code> is written to the network socket – see <a href="#netflush-362">362</a>. The amount sent is recorded, and <code>nbackp</code> is adjusted upward to reflect this data has been sent at <a href="#netflush-376">376</a>. If <code>nfrontp</code> is equal to <code>nbackp</code> all data will have been drained, and the network output buffer state is reinitialized at <a href="#netflush-384">384</a>. Note that <code>netobuf</code> will not be fully drained if <code>send</code> returns a smaller value than was requested.</p>
<p>We have not yet discussed the handling of urgent data, which is done at <a href="#netflush-352">352</a> if <code>neturg</code> is not <code>NULL</code>. Recall that <code>neturg</code> is a special purpose pointer within <code>netobuf</code> which marks the area of urgent data. Just as <span class="math inline">[<em>n</em><em>b</em><em>a</em><em>c</em><em>k</em><em>p</em>, <em>n</em><em>f</em><em>r</em><em>o</em><em>n</em><em>t</em><em>p</em>)</span> is the range of network output data that is yet to be written to the network socket, <span class="math inline">[<em>n</em><em>b</em><em>a</em><em>c</em><em>k</em><em>p</em>, <em>n</em><em>e</em><em>t</em><em>u</em><em>r</em><em>g</em>)</span> is the range of urgent data that is yet to be written to the network socket. Note that <code>netobuf</code> will not be fully drained if there is both urgent and normal data in <code>netobuf</code>, as the <code>send</code> call will only attempt to drain the urgent data.</p>
<div class="sourceCode" id="netflush" data-startFrom="320"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="netflush-320" href="#netflush-320" title="320"><span class="dt">void</span></a>
<a class="sourceLine" id="netflush-321" href="#netflush-321" title="321">netflush(<span class="dt">void</span>)</a>
<a class="sourceLine" id="netflush-322" href="#netflush-322" title="322">{</a>
<a class="sourceLine" id="netflush-323" href="#netflush-323" title="323"> <span class="dt">int</span> n;</a>
<a class="sourceLine" id="netflush-324" href="#netflush-324" title="324"></a>
<a class="sourceLine" id="netflush-325" href="#netflush-325" title="325"> <span class="cf">if</span> ((n = nfrontp - nbackp) > <span class="dv">0</span>) {</a>
<a class="sourceLine" id="netflush-326" href="#netflush-326" title="326"></a>
<a class="sourceLine" id="netflush-327" href="#netflush-327" title="327"><span class="pp">#if 0</span></a>
<a class="sourceLine" id="netflush-328" href="#netflush-328" title="328"><span class="co"> /* XXX This causes output_data() to recurse and die */</span></a>
<a class="sourceLine" id="netflush-329" href="#netflush-329" title="329"><span class="co"> DIAG(TD_REPORT,</span></a>
<a class="sourceLine" id="netflush-330" href="#netflush-330" title="330"><span class="co"> { netoprintf(</span><span class="st">"td: netflush %d chars</span><span class="sc">\r\n</span><span class="st">"</span><span class="co">, n);</span></a>
<a class="sourceLine" id="netflush-331" href="#netflush-331" title="331"><span class="co"> n = nfrontp - nbackp; /* update count */</span></a>
<a class="sourceLine" id="netflush-332" href="#netflush-332" title="332"><span class="co"> });</span></a>
<a class="sourceLine" id="netflush-333" href="#netflush-333" title="333"><span class="pp">#endif</span></a>
<a class="sourceLine" id="netflush-334" href="#netflush-334" title="334"></a>
<a class="sourceLine" id="netflush-335" href="#netflush-335" title="335"><span class="pp">#if defined(ENCRYPT)</span></a>
<a class="sourceLine" id="netflush-336" href="#netflush-336" title="336"> <span class="cf">if</span> (encrypt_output) {</a>
<a class="sourceLine" id="netflush-337" href="#netflush-337" title="337"> <span class="dt">char</span> *s = nclearto ? nclearto : nbackp;</a>
<a class="sourceLine" id="netflush-338" href="#netflush-338" title="338"> <span class="cf">if</span> (nfrontp - s > <span class="dv">0</span>) {</a>
<a class="sourceLine" id="netflush-339" href="#netflush-339" title="339"> (*encrypt_output)((<span class="dt">unsigned</span> <span class="dt">char</span> *)s, nfrontp-s);</a>
<a class="sourceLine" id="netflush-340" href="#netflush-340" title="340"> nclearto = nfrontp;</a>
<a class="sourceLine" id="netflush-341" href="#netflush-341" title="341"> }</a>
<a class="sourceLine" id="netflush-342" href="#netflush-342" title="342"> }</a>
<a class="sourceLine" id="netflush-343" href="#netflush-343" title="343"><span class="pp">#endif</span></a>
<a class="sourceLine" id="netflush-344" href="#netflush-344" title="344"> <span class="co">/*</span></a>
<a class="sourceLine" id="netflush-345" href="#netflush-345" title="345"><span class="co"> * if no urgent data, or if the other side appears to be an</span></a>
<a class="sourceLine" id="netflush-346" href="#netflush-346" title="346"><span class="co"> * old 4.2 client (and thus unable to survive TCP urgent data),</span></a>
<a class="sourceLine" id="netflush-347" href="#netflush-347" title="347"><span class="co"> * write the entire buffer in non-OOB mode.</span></a>
<a class="sourceLine" id="netflush-348" href="#netflush-348" title="348"><span class="co"> */</span></a>
<a class="sourceLine" id="netflush-349" href="#netflush-349" title="349"> <span class="cf">if</span> ((neturg == <span class="dv">0</span>) || (not42 == <span class="dv">0</span>)) {</a>
<a class="sourceLine" id="netflush-350" href="#netflush-350" title="350"> n = write(net, nbackp, n); <span class="co">/* normal write */</span></a>
<a class="sourceLine" id="netflush-351" href="#netflush-351" title="351"> } <span class="cf">else</span> {</a>
<a class="sourceLine" id="netflush-352" href="#netflush-352" title="352"> n = neturg - nbackp;</a>
<a class="sourceLine" id="netflush-353" href="#netflush-353" title="353"> <span class="co">/*</span></a>
<a class="sourceLine" id="netflush-354" href="#netflush-354" title="354"><span class="co"> * In 4.2 (and 4.3) systems, there is some question about</span></a>
<a class="sourceLine" id="netflush-355" href="#netflush-355" title="355"><span class="co"> * what byte in a sendOOB operation is the "OOB" data.</span></a>
<a class="sourceLine" id="netflush-356" href="#netflush-356" title="356"><span class="co"> * To make ourselves compatible, we only send ONE byte</span></a>
<a class="sourceLine" id="netflush-357" href="#netflush-357" title="357"><span class="co"> * out of band, the one WE THINK should be OOB (though</span></a>
<a class="sourceLine" id="netflush-358" href="#netflush-358" title="358"><span class="co"> * we really have more the TCP philosophy of urgent data</span></a>
<a class="sourceLine" id="netflush-359" href="#netflush-359" title="359"><span class="co"> * rather than the Unix philosophy of OOB data).</span></a>
<a class="sourceLine" id="netflush-360" href="#netflush-360" title="360"><span class="co"> */</span></a>
<a class="sourceLine" id="netflush-361" href="#netflush-361" title="361"> <span class="cf">if</span> (n > <span class="dv">1</span>) {</a>
<a class="sourceLine" id="netflush-362" href="#netflush-362" title="362"> n = send(net, nbackp, n-<span class="dv">1</span>, <span class="dv">0</span>); <span class="co">/* send URGENT all by itself */</span></a>
<a class="sourceLine" id="netflush-363" href="#netflush-363" title="363"> } <span class="cf">else</span> {</a>
<a class="sourceLine" id="netflush-364" href="#netflush-364" title="364"> n = send(net, nbackp, n, MSG_OOB); <span class="co">/* URGENT data */</span></a>
<a class="sourceLine" id="netflush-365" href="#netflush-365" title="365"> }</a>
<a class="sourceLine" id="netflush-366" href="#netflush-366" title="366"> }</a>
<a class="sourceLine" id="netflush-367" href="#netflush-367" title="367"> }</a>
<a class="sourceLine" id="netflush-368" href="#netflush-368" title="368"></a>
<a class="sourceLine" id="netflush-369" href="#netflush-369" title="369"> <span class="cf">if</span> (n == -<span class="dv">1</span>) {</a>
<a class="sourceLine" id="netflush-370" href="#netflush-370" title="370"> <span class="cf">if</span> (errno == EWOULDBLOCK || errno == EINTR)</a>
<a class="sourceLine" id="netflush-371" href="#netflush-371" title="371"> <span class="cf">return</span>;</a>
<a class="sourceLine" id="netflush-372" href="#netflush-372" title="372"> cleanup(<span class="dv">0</span>);</a>
<a class="sourceLine" id="netflush-373" href="#netflush-373" title="373"> <span class="co">/* NOTREACHED */</span></a>
<a class="sourceLine" id="netflush-374" href="#netflush-374" title="374"> }</a>
<a class="sourceLine" id="netflush-375" href="#netflush-375" title="375"></a>
<a class="sourceLine" id="netflush-376" href="#netflush-376" title="376"> nbackp += n;</a>
<a class="sourceLine" id="netflush-377" href="#netflush-377" title="377"><span class="pp">#if defined(ENCRYPT)</span></a>
<a class="sourceLine" id="netflush-378" href="#netflush-378" title="378"> <span class="cf">if</span> (nbackp > nclearto)</a>
<a class="sourceLine" id="netflush-379" href="#netflush-379" title="379"> nclearto = <span class="dv">0</span>;</a>
<a class="sourceLine" id="netflush-380" href="#netflush-380" title="380"><span class="pp">#endif</span></a>
<a class="sourceLine" id="netflush-381" href="#netflush-381" title="381"> <span class="cf">if</span> (nbackp >= neturg) {</a>
<a class="sourceLine" id="netflush-382" href="#netflush-382" title="382"> neturg = <span class="dv">0</span>;</a>
<a class="sourceLine" id="netflush-383" href="#netflush-383" title="383"> }</a>
<a class="sourceLine" id="netflush-384" href="#netflush-384" title="384"> <span class="cf">if</span> (nbackp == nfrontp) {</a>
<a class="sourceLine" id="netflush-385" href="#netflush-385" title="385"> nbackp = nfrontp = netobuf;</a>
<a class="sourceLine" id="netflush-386" href="#netflush-386" title="386"><span class="pp">#if defined(ENCRYPT)</span></a>
<a class="sourceLine" id="netflush-387" href="#netflush-387" title="387"> nclearto = <span class="dv">0</span>;</a>
<a class="sourceLine" id="netflush-388" href="#netflush-388" title="388"><span class="pp">#endif</span></a>
<a class="sourceLine" id="netflush-389" href="#netflush-389" title="389"> }</a>
<a class="sourceLine" id="netflush-390" href="#netflush-390" title="390"> <span class="cf">return</span>;</a>
<a class="sourceLine" id="netflush-391" href="#netflush-391" title="391">} <span class="co">/* end of netflush */</span></a></code></pre></div>
<h2 id="triggering-the-vulnerability"><span class="header-section-number">2.6</span> Triggering the vulnerability</h2>
<p>The methods to control <code>netobuf</code> content as described in the previous section are enough to craft a IAC SB sequence without a terminating IAC SE sequence in the content. We have several ways to make this happen, and will discuss the two most important ones. This is easiest to demonstrate by example, which we will do in the next sections.</p>
<p>Before doing so, we need to point out that the state machine will handle IAC DO sequences by calling <a href="#telrcv-337"><code>dooption</code></a>. Without looking at the dooption code, we note that an unknown option byte will always be replied to using <code>send_wont</code> resulting in a IAC WONT reply sequence. More specifically, an input sequence such as IAC DO SB would be replied to with an IAC WONT SB sequence, and an input sequence such as IAC DO IAC would be replied to with an IAC WONT IAC sequence.</p>
<h3 id="trigger-using-short-writes"><span class="header-section-number">2.6.1</span> Trigger using short writes</h3>
<p>The first way to trigger the vulnerability is by having <code>write</code> in <code>netflush</code> return before having written all data. We will discuss what happens here below in a simplified manner.</p>
<p>Suppose we have populated the prefix of <code>netobuf</code> with IAC WONT SB by sending IAC DO SB requests. We have then padded out the rest of the buffer up to the last byte. This leads to the following configuration:</p>
<pre><code> netobuf
+-------+-------+-------+-------+-------+-------+
| 0 | 1 | 2 | ... | 8190 | 8191 |
+-------+-------+-------+-------+-------+-------+
| IAC | WONT | SB | ... | ... | |
++------+-------+-------+-------+-------++------+
| |
+>nbackp +>nfrontp</code></pre>
<p>If we send an IAC DO IAC request, a call to <a href="#dooption-907"><code>send_wont</code></a> will call <code>output_datalen</code> to add the IAC WONT IAC sequence to <code>netobuf</code>. <a href="#output_datalen-88"><code>netflush</code></a> will be called to make space for these 3 bytes. Suppose <a href="#output_datalen-350"><code>write</code></a> sends less data than requested, let’s say 2 bytes in this case, the situation then becomes:</p>
<pre><code> netobuf
+-------+-------+-------+-------+-------+-------+
| 0 | 1 | 2 | ... | 8190 | 8191 |
+-------+-------+-------+-------+-------+-------+
| IAC | WONT | SB | ... | ... | |
+-------+-------++------+-------+-------++------+
| |
+>nbackp +>nfrontp</code></pre>
<p>At this point <code>output_datalen</code> would move past the <code>netflush</code> and recalculate <a href="#output_datalen-89"><code>remaining</code></a> to be 1 and <a href="#output_datalen-94">copy</a> 1 byte of data to <code>nfrontp</code>. The situation after the copy is as follows:</p>
<pre><code> netobuf
+-------+-------+-------+-------+-------+-------+
| 0 | 1 | 2 | ... | 8190 | 8191 |
+-------+-------+-------+-------+-------+-------+
| IAC | WONT | SB | ... | ... | IAC |
+-------+-------++------+-------+-------+-------++
| |
+>nbackp +>nfrontp</code></pre>
<p>As there are still 2 bytes of data to be copied by <code>output_datalen</code>, <a href="#output_datalen-88"><code>netflush</code></a> will be called again. If this time <code>write</code> will send out all remaining data, we end up with the following situation after the <code>netflush</code>:</p>
<pre><code> netobuf
+-------+-------+-------+-------+-------+-------+
| 0 | 1 | 2 | ... | 8190 | 8191 |
+-------+-------+-------+-------+-------+-------+
| IAC | WONT | SB | ... | ... | IAC |
++------+-------+-------+-------+-------+-------+
|
+>nfrontp
+>nbackp</code></pre>
<p>Finally <code>output_datalen</code> would copy the remaining 2 bytes of the IAC WONT IAC triplet, leading to:</p>
<pre><code> netobuf
+-------+-------+-------+-------+-------+
| 0 | 1 | 2 | 8190 | 8191 |
+-------+-------+-------+-------+-------+
| WONT | IAC | SB | ... | IAC |
++------+-------++------+-------+-------+
| |
+>nbackp +>nfrontp</code></pre>
<p>If this buffer were to be processed by <code>netclear</code> the call to <code>nextitem</code> would skip the WONT at index 0, and start processing the IAC we injected at index 1. As <code>nextitem</code> can over-index, if the byte at index 2 is a SB byte, we would end up with the situation we desired, that is, a IAC SB sequence in netobuf without a terminating IAC SE sequence.</p>
<h3 id="trigger-using-urgent-data"><span class="header-section-number">2.6.2</span> Trigger using urgent data</h3>
<p>Control over the non-blocking <code>write</code> return value is complicated, because it depends on several network related factors outside our control, related variables here are the transmit buffer size, the MTU, the TCP MSS, whether or not TSO is used and so on. This situation is exacerbated by a timer telnetd will use to close the connection after a certain amount of time. We need this time to fill the remote transmit buffer, making this even more convoluted.</p>
<p>Thankfully, there is another way in order to have <code>netflush</code> not drain all data, which involves the use of the <code>neturg</code> pointer. The process is outlined below.</p>
<ol type="1">
<li><p>We start with a situation similar as before, where we have sent a IAC DO SB request. This time we follow it by an IAC AO request, and then padding requests to increase <code>nfrontp</code> to 8191. The IAC AO is answered by a IAC DM sequence as we have seen before in <code>telrcv</code> and sets <code>neturg</code> to the address of the DM byte in <code>netobuf</code>.</p></li>
<li><p>We send a IAC DO IAC request. <a href="#telrcv-337">Remember</a> this will result in a call to <code>send_wont</code> producing IAC WONT IAC which ends up in <code>output_datalen</code> to add this triplet to <code>netobuf</code>. <code>netflush</code> will be called to make space for these 3 bytes. However, because <code>neturg</code> is not 0 <code>netflush</code> will now use <code>send</code> instead of <code>write</code> and <a href="#netflush-362">only drain 3 bytes of data</a>.</p></li>
<li><p>Now <code>output_datalen</code> will recalculate <a href="#output_datalen-89"><code>remaining</code> to be 1</a>, and copy 1 byte of data into <code>netobuf</code>.</p></li>
<li><p>At this point <code>netflush</code> will be called again, which will <a href="#netflush-364">send</a> 1 byte of urgent data. Note that this will increase <code>nbackp</code> to <code>neturg</code> which in turn leads <code>neturg</code> to be <a href="#netflush-382">set to 0</a>.</p></li>
<li><p>As no space has been freed at the tail of the buffer, 0 bytes will be copied, and <code>netflush</code> will be called again. Here it will use a regular blocking <code>write</code> and drain all the remaining data.</p></li>
<li><p>The remaining WONT IAC bytes will now be written to <code>netobuf</code>, leading to the sequence WONT IAC SB at the beginning.</p></li>
</ol>
<p>This is the configuration we are looking for in order to trigger the bug. The animation below illustrates the previous description:</p>
<p><img src="https://raw.githubusercontent.com/immunityinc/bravestarr/master/assets/anim.gif" style="width:100.0%" /></p>
<h1 id="exploitation"><span class="header-section-number">3</span> Exploitation</h1>
<p>Now that we have seen that we can indeed create a situation in netkit-telnetd that leads to memory corruption, we will investigate exploitability. In doing so we will develop a rudimentary proof-of-concept exploit that will spawn a root shell on a default Fedora 31 installation running netkit-telnetd. This proof of concept will not work against machines that have SELinux enabled, although we are fairly positive a working exploit can be developed against SELinux targets. We will briefly outline some ideas on this in our conclusion.</p>
<h2 id="cpc"><span class="header-section-number">3.1</span> Copy primitive control</h2>
<p>netkit-telnetd uses a huge number of global or file scoped variables, which includes <code>netobuf</code> which we overflow. The first issue we need to deal with is ensuring <code>nextitem</code> will find a IAC SE pair in order to survive. A tentative glance at the data section layout shows us the following sizes and names:</p>
<pre><code>+---------+--------+-------+---------+--------+-----+-----+---------+
| 8256 | 304 | 8 | 8 | 8 | 4 | 20 | 8192 |
+---------+--------+-------+---------+--------+-----+-----+---------+
| netobuf | slctab | netip | pfrontp | neturg | net | pad | netibuf |
+---------+--------+-------+---------+--------+-----+-----+---------+</code></pre>
<p>It seems opportune to use <code>netibuf</code> to embed the terminating IAC SE sequence. We can do this by simply sending a IAC SE pair, as <a href="#telrcv-153">telrcv</a> will not add anything to <code>netobuf</code> when it encounters an unexpected command byte in <code>TS_IAC</code> state.</p>
<p>If this IAC SE sequence is at the start of <code>netibuf</code> and we use the buffer layout described in the previous section, we would end up copying the range <code>[&netobuf[1], &netibuf[1]]</code> to <code>&netbuf[0]</code>. This because the first WONT byte in <code>netobuf</code> will not be wanted, and <code>good</code> will point to the start of the buffer. This is a rather inflexible primitive, so the first thing we will investigate is whether we can exert any control over both the destination and the size of the copy.</p>
<h3 id="destination-and-size-control"><span class="header-section-number">3.1.1</span> Destination and size control</h3>
<p>In order to control the destination of the copy, we need to control <code>good</code>. Respectively, to control the size of the copy we need to control at which offset in <code>netobuf</code> the IAC SE sequence occurs. Using the trigger we discussed, the WONT IAC sequence will always be written at the beginning of <code>netobuf</code>. Altough this cannot be avoided, remember that the real issue is the state desynchronization this creates by turning an IAC WONT SB sequence into a WONT IAC SB sequence.</p>
<p>We can propagate this state desynchronization further down <code>netobuf</code> by observing that just like we turned the IAC WONT SB sequence into WONT IAC SB we can turn the IAC WONT SB IAC WONT SB sequence into WONT IAC IAC WONT IAC SB sequence. Furthermore, this can be generalized, where we can turn a sequence with n-repetitions of IAC WONT SB into a sequence of a single WONT IAC, then (n-1) IAC WONT IAC repetitions, and finally a SB byte. Note that <code>good</code> will not be updated up until the IAC SB sequence, as the other sequences are not wanted.</p>
<p>This means that by adding more or fewer IAC WONT SB sequences and corresponding IAC WONT IAC sequences after desynchronization we control the position of <code>thisitem</code> within <code>netobuf</code> and also <code>length</code> in multiples of 3 at the time of the <a href="#netclear-301"><code>bcopy</code></a>.</p>
<p>First the situation we created in <code>netobuf</code> prior to sending two IAC WONT IAC sequences.</p>
<pre><code> netobuf
+------+------+------+------+------+------+------+------+------+
| 0 | 1 | 2 | 3 | 4 | 5 | ... | 8190 | 8191 |
+------+------+------+------+------+------+------+------+------+
| IAC | WONT | SB | IAC | WONT | SB | ... | ... | |
++-----+------+------+------+------+------+------+------++-----+
| |
+>nbackp +>nfrontp</code></pre>
<p>After these two IAC WONT IAC sequences have been added to <code>netobuf</code> the situation is as below. Note that when the buffer is processed the IAC IAC sequence will be treated as data, and the IAC SB sequence will result in the condition we were looking for, but this further into <code>netobuf</code>.</p>
<pre><code> netobuf
+------+------+------+------+------+------+------+------+------+
| 0 | 1 | 2 | 3 | 4 | 5 | ... | 8190 | 8191 |
+------+------+------+------+------+------+------+------+------+
| WONT | IAC | IAC | WONT | IAC | SB | ... | ... | IAC |
++-----+------+------+------+------++-----+------+------+------+
| |
+>nbackp +>nfrontp</code></pre>
<h2 id="limited-data-segment-infoleak"><span class="header-section-number">3.2</span> Limited data segment infoleak</h2>
<p>By controlling the source and length of the copy, we can start copying from the tail of <code>netobuf</code> until and including <code>&netibuf[2]</code> to the start of <code>netobuf</code>. This range contains the data we have seen <a href="#cpc">previously</a>. When <code>netobuf</code> is next flushed, this copied data will be written to our client socket.</p>
<p>This will include the <code>netip</code> pointer, which is guaranteed to point somewhere in <code>netibuf</code>. Using this leaked pointer and the knowledge of how much data we sent ourselves, we can calculate the start address of <code>netibuf</code>. We know that <code>netibuf</code> is two pages big, and we know whether <code>netip</code> is within the first or the second page based on how much data we sent was processed at the time of the information leak. By rounding the leaked <code>netip</code> value down to a page boundary and if necessary subtracting one page, we know the exact address of <code>netibuf</code> in memory. This bypasses PIE, and allows us to determine the other addresses of variables in the data segment if we know their distance from <code>netibuf</code>.</p>
<h2 id="write-primitive"><span class="header-section-number">3.3</span> Write primitive</h2>
<p>The next thing we’re interested in is a write primitive. One of the variables we leaked was <code>pfrontp</code>, which makes a good target as controlling it allows us to <a href="#telrcv-148">write arbitrary data</a> to addresses before <a href="#telrcv-87"><code>&ptyobuf[BUFSIZ-2]</code></a>. This allows us to corrupt arbitrary data in the data segment, so this primitive can easily be built upon to create more flexible information leaks and write primitives.</p>
<h2 id="escalation-of-privileges"><span class="header-section-number">3.4</span> Escalation of privileges</h2>
<p>The information leak, in combination with the write primitive to the data segment we discussed have tremendous potential for finding further primitives for escalation. Many of the simpler ones are context dependent, but we have included some ideas below.</p>
<ul>
<li>Corrupt the <code>environ</code> pointer and add LD_PRELOAD before login is executed. This will work well on systems where the attacker has local access.</li>
<li>Target the <code>doopt</code>, <code>dont</code>, <code>will</code>, or <code>wont</code> format strings for leaking or write primitives if fortify so allows.</li>
<li>Investigate more flexible write primitives and information leaks.</li>
<li>Overwrite <code>loginprg</code> and aim for command injection.</li>
</ul>
<h2 id="command-injection"><span class="header-section-number">3.5</span> Command injection</h2>
<p>One of the simplest ideas is seeing if we can influence how telnetd spawns the login process and control that. There are some drawbacks to this approach, in particular SELinux allowing in.telnetd to only execute the login binary, but for a proof of concept it is suitable due to its simplicity.</p>
<p>Executing the login process is done using the <code>start_login</code> function as seen below. On line <a href="#start_login-629">629</a> the global character pointer <code>loginprg</code> is used to determine what to execute. On the Fedora 31 build, this variable is far below <code>ptyobuf</code> on the data segment, and therefore we can write it.</p>
<p>The arguments passed to the command executed are not easily under our control, as they are constructed on the stack in <code>argv_stuff</code> using <code>addarg</code>. We note that on the default Fedora 31 build, what is executed use the following argv array:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb9-1" title="1">argv[<span class="dv">0</span>] = loginprg;</a>
<a class="sourceLine" id="cb9-2" title="2">argv[<span class="dv">1</span>] = <span class="st">"-h"</span>;</a>
<a class="sourceLine" id="cb9-3" title="3">argv[<span class="dv">2</span>] = host;</a>
<a class="sourceLine" id="cb9-4" title="4">argv[<span class="dv">3</span>] = <span class="st">"-p"</span>;</a>
<a class="sourceLine" id="cb9-5" title="5">argv[<span class="dv">4</span>] = name;</a></code></pre></div>
<p>Through the <a href="#start_login-668">USER</a> environment variable we have easy control over <code>name</code>, although it cannot start with a hyphen (this would be a security issue by itself). This variable can be set through <code>TELOPT_ENVIRON</code> or by corrupting the <code>environ</code> variable in the data segment. We can control <code>host</code> by overflowing <code>remote_host_name</code>. The “-h” parameter in bash does not take an argument, therefore <code>host</code> can be used to pass an additional parameter to bash. A good choice in this case is “-c” which will execute the command in <code>name</code>. This is because the ‘-h’ argument passed to bash will set the <code>hashing_enabled</code> flag, and the ‘-p’ argument will set the <code>privileged_mode</code> flag. The former flag is harmless, and the latter flag suppresses bash from changing the euid, which is convenient. Indeed, executing <code>bash -h -c -p bash</code> will spawn a new shell.</p>
<div class="sourceCode" id="start_login" data-startFrom="614"><pre class="sourceCode numberSource c numberLines lineAnchors"><code class="sourceCode c"><a class="sourceLine" id="start_login-614" href="#start_login-614" title="614"><span class="dt">void</span> start_login(<span class="dt">const</span> <span class="dt">char</span> *host, <span class="dt">int</span> autologin, <span class="dt">const</span> <span class="dt">char</span> *name) {</a>
<a class="sourceLine" id="start_login-615" href="#start_login-615" title="615"> <span class="kw">struct</span> argv_stuff avs;</a>
<a class="sourceLine" id="start_login-616" href="#start_login-616" title="616"> <span class="dt">char</span> *<span class="dt">const</span> *argvfoo;</a>
<a class="sourceLine" id="start_login-617" href="#start_login-617" title="617"> (<span class="dt">void</span>)autologin;</a>
<a class="sourceLine" id="start_login-618" href="#start_login-618" title="618"></a>
<a class="sourceLine" id="start_login-619" href="#start_login-619" title="619"> initarg(&avs);</a>
<a class="sourceLine" id="start_login-620" href="#start_login-620" title="620"></a>
<a class="sourceLine" id="start_login-621" href="#start_login-621" title="621"> <span class="co">/*</span></a>
<a class="sourceLine" id="start_login-622" href="#start_login-622" title="622"><span class="co"> * -h : pass on name of host.</span></a>
<a class="sourceLine" id="start_login-623" href="#start_login-623" title="623"><span class="co"> * </span><span class="al">WARNING</span><span class="co">: -h is accepted by login if and only if</span></a>
<a class="sourceLine" id="start_login-624" href="#start_login-624" title="624"><span class="co"> * getuid() == 0.</span></a>
<a class="sourceLine" id="start_login-625" href="#start_login-625" title="625"><span class="co"> * -p : don't clobber the environment (so terminal type stays set).</span></a>
<a class="sourceLine" id="start_login-626" href="#start_login-626" title="626"><span class="co"> *</span></a>
<a class="sourceLine" id="start_login-627" href="#start_login-627" title="627"><span class="co"> * -f : force this login, he has already been authenticated</span></a>
<a class="sourceLine" id="start_login-628" href="#start_login-628" title="628"><span class="co"> */</span></a>
<a class="sourceLine" id="start_login-629" href="#start_login-629" title="629"> addarg(&avs, loginprg);</a>
<a class="sourceLine" id="start_login-630" href="#start_login-630" title="630"> addarg(&avs, <span class="st">"-h"</span>);</a>
<a class="sourceLine" id="start_login-631" href="#start_login-631" title="631"> addarg(&avs, host);</a>
<a class="sourceLine" id="start_login-632" href="#start_login-632" title="632"><span class="pp">#if !defined(NO_LOGIN_P)</span></a>
<a class="sourceLine" id="start_login-633" href="#start_login-633" title="633"> addarg(&avs, <span class="st">"-p"</span>);</a>
<a class="sourceLine" id="start_login-634" href="#start_login-634" title="634"><span class="pp">#endif</span></a>
<a class="sourceLine" id="start_login-635" href="#start_login-635" title="635"><span class="pp">#ifdef BFTPDAEMON</span></a>
<a class="sourceLine" id="start_login-636" href="#start_login-636" title="636"> <span class="co">/*</span></a>
<a class="sourceLine" id="start_login-637" href="#start_login-637" title="637"><span class="co"> * Are we working as the bftp daemon? If so, then ask login</span></a>
<a class="sourceLine" id="start_login-638" href="#start_login-638" title="638"><span class="co"> * to start bftp instead of shell.</span></a>
<a class="sourceLine" id="start_login-639" href="#start_login-639" title="639"><span class="co"> */</span></a>
<a class="sourceLine" id="start_login-640" href="#start_login-640" title="640"> <span class="cf">if</span> (bftpd) {</a>
<a class="sourceLine" id="start_login-641" href="#start_login-641" title="641"> addarg(&avs, <span class="st">"-e"</span>);</a>
<a class="sourceLine" id="start_login-642" href="#start_login-642" title="642"> addarg(&avs, BFTPPATH);</a>
<a class="sourceLine" id="start_login-643" href="#start_login-643" title="643"> }</a>
<a class="sourceLine" id="start_login-644" href="#start_login-644" title="644"> <span class="cf">else</span></a>
<a class="sourceLine" id="start_login-645" href="#start_login-645" title="645"><span class="pp">#endif</span></a>
<a class="sourceLine" id="start_login-646" href="#start_login-646" title="646"> {</a>
<a class="sourceLine" id="start_login-647" href="#start_login-647" title="647"><span class="pp">#if defined (SecurID)</span></a>
<a class="sourceLine" id="start_login-648" href="#start_login-648" title="648"> <span class="co">/*</span></a>
<a class="sourceLine" id="start_login-649" href="#start_login-649" title="649"><span class="co"> * don't worry about the -f that might get sent.</span></a>
<a class="sourceLine" id="start_login-650" href="#start_login-650" title="650"><span class="co"> * A -s is supposed to override it anyhow.</span></a>
<a class="sourceLine" id="start_login-651" href="#start_login-651" title="651"><span class="co"> */</span></a>
<a class="sourceLine" id="start_login-652" href="#start_login-652" title="652"> <span class="cf">if</span> (require_SecurID) addarg(&avs, <span class="st">"-s"</span>);</a>
<a class="sourceLine" id="start_login-653" href="#start_login-653" title="653"><span class="pp">#endif</span></a>
<a class="sourceLine" id="start_login-654" href="#start_login-654" title="654"> <span class="cf">if</span> (*name==<span class="ch">'-'</span>) {</a>
<a class="sourceLine" id="start_login-655" href="#start_login-655" title="655"> syslog(LOG_ERR, <span class="st">"Attempt to login with an option!"</span>);</a>
<a class="sourceLine" id="start_login-656" href="#start_login-656" title="656"> name = <span class="st">""</span>;</a>
<a class="sourceLine" id="start_login-657" href="#start_login-657" title="657"> }</a>
<a class="sourceLine" id="start_login-658" href="#start_login-658" title="658"><span class="pp">#if defined (AUTHENTICATE)</span></a>
<a class="sourceLine" id="start_login-659" href="#start_login-659" title="659"> <span class="cf">if</span> (auth_level >= <span class="dv">0</span> && autologin == AUTH_VALID) {</a>
<a class="sourceLine" id="start_login-660" href="#start_login-660" title="660"><span class="pp"># if !defined(NO_LOGIN_F)</span></a>
<a class="sourceLine" id="start_login-661" href="#start_login-661" title="661"> addarg(&avs, <span class="st">"-f"</span>);</a>
<a class="sourceLine" id="start_login-662" href="#start_login-662" title="662"><span class="pp"># endif</span></a>
<a class="sourceLine" id="start_login-663" href="#start_login-663" title="663"> addarg(&avs, name);</a>
<a class="sourceLine" id="start_login-664" href="#start_login-664" title="664"> }</a>
<a class="sourceLine" id="start_login-665" href="#start_login-665" title="665"> <span class="cf">else</span></a>
<a class="sourceLine" id="start_login-666" href="#start_login-666" title="666"><span class="pp">#endif</span></a>
<a class="sourceLine" id="start_login-667" href="#start_login-667" title="667"> {</a>
<a class="sourceLine" id="start_login-668" href="#start_login-668" title="668"> <span class="cf">if</span> (getenv(<span class="st">"USER"</span>)) {</a>
<a class="sourceLine" id="start_login-669" href="#start_login-669" title="669"> addarg(&avs, getenv(<span class="st">"USER"</span>));</a>
<a class="sourceLine" id="start_login-670" href="#start_login-670" title="670"> <span class="cf">if</span> (*getenv(<span class="st">"USER"</span>) == <span class="ch">'-'</span>) {</a>
<a class="sourceLine" id="start_login-671" href="#start_login-671" title="671"> write(<span class="dv">1</span>,<span class="st">"I don't hear you!</span><span class="sc">\r\n</span><span class="st">"</span>,<span class="dv">19</span>);</a>
<a class="sourceLine" id="start_login-672" href="#start_login-672" title="672"> syslog(LOG_ERR,<span class="st">"Attempt to login with an option!"</span>);</a>
<a class="sourceLine" id="start_login-673" href="#start_login-673" title="673"> exit(<span class="dv">1</span>);</a>
<a class="sourceLine" id="start_login-674" href="#start_login-674" title="674"> }</a>
<a class="sourceLine" id="start_login-675" href="#start_login-675" title="675"> }</a>
<a class="sourceLine" id="start_login-676" href="#start_login-676" title="676"> }</a>
<a class="sourceLine" id="start_login-677" href="#start_login-677" title="677"> }</a>
<a class="sourceLine" id="start_login-678" href="#start_login-678" title="678"> closelog();</a>
<a class="sourceLine" id="start_login-679" href="#start_login-679" title="679"> <span class="co">/* execv() should really take char const* const *, but it can't */</span></a>
<a class="sourceLine" id="start_login-680" href="#start_login-680" title="680"> <span class="co">/*argvfoo = argv*/</span>;</a>
<a class="sourceLine" id="start_login-681" href="#start_login-681" title="681"> memcpy(&argvfoo, &avs.argv, <span class="kw">sizeof</span>(argvfoo));</a>
<a class="sourceLine" id="start_login-682" href="#start_login-682" title="682"> execv(loginprg, argvfoo);</a>
<a class="sourceLine" id="start_login-683" href="#start_login-683" title="683"></a>
<a class="sourceLine" id="start_login-684" href="#start_login-684" title="684"> syslog(LOG_ERR, <span class="st">"%s: %m</span><span class="sc">\n</span><span class="st">"</span>, loginprg);</a>
<a class="sourceLine" id="start_login-685" href="#start_login-685" title="685"> fatalperror(net, loginprg);</a>
<a class="sourceLine" id="start_login-686" href="#start_login-686" title="686">}</a></code></pre></div>
<h3 id="rewriting-the-data-segment"><span class="header-section-number">3.5.1</span> Rewriting the data segment</h3>
<p>As the data segment write primitive we have can only be used once, the easiest option is to write from <code>loginprg</code> all the way up to <code>remote_host_name</code> to get to the command execution outlined previously. This means that we will corrupt the variables on the data segment between <code>loginprg</code> and <code>remote_host_name</code> along the way. In order to avoid undesired side-effects, we have to inspect all variables we have overwritten for consistency. As this work is a proof-of-concept, we will set all memory values between <code>loginprg</code> and <code>remote_host_name</code> to 0, and have cherry-picked the ones that lead to undesired side-effects. This results in the list of values given below, where each entry will be handled specifically. Note that for more reliable exploitation we would have to exhaustively inspect all variables in this range.</p>
<pre><code>(gdb) x/gx &loginprg
0xe0f8 <loginprg>: 0x000000000000a247
(gdb) x/wx &ptyslavefd
0xe2c8 <ptyslavefd>: 0xffffffff
(gdb) x/gx &environ
0xe700 <environ>: 0x0000000000000000
(gdb) x/gx &LastArgv
0x100c0 <LastArgv>: 0x0000000000000000
(gdb) x/64bx &remote_host_name
0x10100 <remote_host_name>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10108 <remote_host_name+8>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10110 <remote_host_name+16>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10118 <remote_host_name+24>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10120 <remote_host_name+32>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10128 <remote_host_name+40>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10130 <remote_host_name+48>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x10138 <remote_host_name+56>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00</code></pre>
<ul>
<li>loginprg: overwritten to point to the command string to execute.</li>
<li>ptyslavefd: overwritten to the original value of <code>ptyslavefd</code>. If this value is incorrect, the <code>login_tty</code> function will call <code>fatalperror</code>.</li>
<li>environ: overwritten to make the <code>getenv("USER")</code> call work in <code>start_login</code> as we need to control the last argument to <code>execv()</code>.</li>
<li>LastArgv: overwritten to make the <code>setproctitle</code> function work.</li>
<li>remote_host_name: overwritten with <code>-c\0</code></li>
</ul>
<p>One more part of this memory is used to contain a temporary scratch area that stores data we want to set pointers to. This includes the LastArgv and environ arrays, the strings inside of them, and the new value <code>loginprg</code> points to. There is not enough space to hold all the temporary data our exploit uses, but the variables that come after <code>state_rcsid</code> are not directly relevant to the code paths traversed in our exploit.</p>
<pre><code>(gdb) x/56bx &state_rcsid
0xe140 <state_rcsid>: 0x24 0x49 0x64 0x3a 0x20 0x73 0x74 0x61
0xe148 <state_rcsid+8>: 0x74 0x65 0x2e 0x63 0x2c 0x76 0x20 0x31
0xe150 <state_rcsid+16>: 0x2e 0x31 0x32 0x20 0x31 0x39 0x39 0x39
0xe158 <state_rcsid+24>: 0x2f 0x31 0x32 0x2f 0x31 0x32 0x20 0x31
0xe160 <state_rcsid+32>: 0x39 0x3a 0x34 0x31 0x3a 0x34 0x34 0x20
0xe168 <state_rcsid+40>: 0x64 0x68 0x6f 0x6c 0x6c 0x61 0x6e 0x64
0xe170 <state_rcsid+48>: 0x20 0x45 0x78 0x70 0x20 0x24 0x00 0x00</code></pre>
<h1 id="conclusion"><span class="header-section-number">4</span> Conclusion</h1>
<p>We have presented a working exploit against Fedora 31 netkit-telnet-0.17 telnetd. Mitigations such as ASLR and PIE have been bypassed by using the bug primitive to create an information leak. Mitigations such as non-executable pages, and theoretically CFI have been bypassed by attacking metadata to change the executable that telnetd executes to log the remote user into the system.</p>
<p>SELinux has not been bypassed, as the current SELinux profile will not allow <code>in.telnetd</code> to execute anything else than the <code>login_exec_t</code> type. This means it is useless to change <code>loginprg</code> to anything that does not belong to this type. One idea would be to see if we can change arguments passed to <code>loginprg</code> to add a <code>-f</code> flag. Another idea is trying to gain full control over the execution path in order to pass arbitrary arguments to the <code>execv</code> call. Both of these ideas need more flexible information leaks and write primitives. We have verified at least a more flexible information leak exists that will disclose more of the data segment, but information leaks and write primitives that can write to arbitrary address in memory have not yet been pursued. We will leave this as future work to others.</p>
<p>Finally I would like to extend my personal gratitude to everyone that has proof-read this document and added suggestions and corrections. Most of you do not want to be named, but it should be known that your efforts were appreciated. Thank you.</p>rhuizerhttp://www.blogger.com/profile/15708593611320953227noreply@blogger.com2