Opened 6 years ago

Last modified 6 years ago

#5998 new Bug

Multiple Integer Overflows

Reported by: tacixat Owned by: jordan
Priority: Normal Milestone: None Set
Component: libtransmission Version: 2.84
Severity: Normal Keywords: CVE-2015-7308, integer overflow, buffer overflow, infinite loop
Cc:

Description

I've attempted to contact info [at] transmissionbt and someone through IRC. Submitting this here because no response.

Most of these vulnerabilities are found around the usage of a macro tr_renew which is a wrapper for realloc. In many cases buffers are expanded by this function without checking for int/size_t overflows. This results in writes outside the bounds of buffers as well as infinite loops. A fix for most these problems is to update the macro for tr_renew to return null if the new size is less than the current size of the buffer as no cases were observed where tr_renew was intentionally used to shrink a buffer. Mitre has assigned CVE-2015-7308 for all of these.

ptrArrayInsert INTEGER + BUFFER OVERFLOWS

(tr_ptrArrayInsert @ libtransmission/ptarray.c)

Root cause - tr_ptrArrayInsert is a function for inserting elements into an array. When the array runs out of space it is reallocated by multiplying the number of elements by 2 then calling tr_renew.

The number of elements (t->n_alloc) is an integer, so when it exceeds 0x7FFFFFFF it will become negative. When passed to MAX(FLOOR, t->n_alloc * 2) it will return FLOOR (32) and realloc the array to be 32 elements in length.

An out of bounds write can then occur by either direct assignment (eg. t->items[pos] = ptr) or if a position is provided by memmove past the bounds of the realloc’d array.

The function tr_ptrArrayInsert is called by two functions, trPtrArrayAppend and trPtrArrayInsertSorted. These two functions are called by many other functions making for a large attack surface.

tr_ptrArrayAppend called by:

  • action_callback_PUSH
  • allocateBandwidth
  • annouceMore
  • append_new_quark
  • atomPulse
  • deleteLocalData
  • extract_parts_from_multipart
  • getBloackRequestPeers
  • rebuildWebseedArray
  • rechokeUploads
  • tau_session_get_tracker
  • tr_bandwidthAllocate
  • tr_sessionGetNextQueuedTorrents
  • tr_tracker_udp_announce
  • tr_tracker_udp_scrape
  • tr_variantParseBenc

tr_ptrArrayInsertSorted called by:

  • createBitTorrentPeer
  • deleteLocalData
  • ensureAtomExists
  • initiateConnection
  • loadBlocklists
  • tr_announcerRemoveTorrent
  • tr_bandwidthSetParent
  • tr_cacheWriteBlock
  • tr_perrMgrAddIncoming

tr_renew INTEGER + BUFFER OVERFLOWS

(tr_renew @ libtransmission/utils.h)

The macro tr_renew is an unchecked wrapper for realloc. In almost every instance this is used to expand a buffers by either adding to the buffer size (eg. += 4096) or doubling the buffer size (eg. *= 2u). None of these uses check for overflow. Exploitation relies on the system's size_t value. In cases where it is 16 or 32 bit an attack is feasible. All the following functions in this section utilize tr_renew in an insecure way.

tr_strip_posisitional_args @ libtransmission/utils.c

This function takes a string and strips characters out of it. A buffer is allocated that is 2x strlen of the input. If the string is >= MAX(size_t) / 2 the bufsize will wrap resulting in a too small allocation. When the string is copied into the buf this will result in buffer overflow. The only place this is called is in utils-test.c and those strings are fixed, but it is proven that this function is exploitable by adding a custom test with a large string and observing an out of bounds write.

tr_blocklistFileSetContent @ libtransmission/blocklist.c

In the while loop starting with tr_sys_file_read_line, as lines are read the buffer ranges is expanded by 4096 bytes at a time. In the case of a blocklist that is larger than a system’s size_t this will result in an integer overflow, leading to a shrink when tr_renew (realloc) is called, and subsequent write beyond the bounds of the buffer. In the case where size_t is 32 bits this would require a ~100 GB blocklist file. However, since transmission accepts gzipped files, a file this size (with repetitive entries) could gzip compress to a couple hundred MB. This attack is a lot more feasible against systems with a 16 bit size_t.

torrentCallScript @ libtransmission/torrent.c

In the #ifdef _WIN32 section of this function the env array is iterated over. If one of the env items is > (size_t - env_block_size - 1) this will lead to an integer overflow, resulting in a shrinking reallocation, and a resulting buffer overflow. The attack vectors are the torrentName and tor->currentDir.

tier_announce_event_push @ libtransmission/announcer.c

If the tier->announce_event_count is larger than the allocation then the tier->announce_event_alloc grows by 4 elements. If this value grows large enough then tr_renew (realloc) is called shrinking the buffer, this leads to an out of bounds write.

requestListAdd @ libtransmission/peer-mgr.c

s->requestCount+1 is checked against s->requestAlloc, if it is gte then it is grown by 128 elements. If this grows sufficiently large it will integer overflow, leading to a shrinking tr_renew and an out of bounds write due to memmove.

trVariantWalk @ libtransmission/variant.c

In the switch case TR_VARIANT_TYPE_LIST, if v != node->v, tr_renew is called increasing the stackAlloc by a factor of 2. If this case were hit many times the integer could wrap resulting in an out of bounds write.

INFINITE LOOPS / DOS

(tr_renew @ libtransmission/utils.h)

containerReserve @ libtransmission/variant.c

Using specially crafted values (eg. needed == MAX(size_t)) then the loop while(n < needed) n *= 2u; can continue infinitely. Taking needed = 0xFFFFFFFF as an example, no value * 2 will be odd, since this is also the largest value, when multiplied by two to satisfy n > needed it will wrap around and continue the loop.

tr_sys_dir_get_current @ libtransmission/file-posix.c

In the do while loop size is continuously increased by 2048 until tmp is large enough to store the CWD. This is unlikely to be exploitable due to system limits on path size, but on a system with a path size > MAX(size_t) a specially crafted value could result in infinite loop. For example, a system where size_t == unsigned short, a path length greater than 63488 would result in infinite loop. At 29 iteration the size would be (4096 + 2048 * 29 == 63488), at 30 iterations the size would be (4096 + 2048 * 30 == 0), this would then start the process over, continuously.

Change History (1)

comment:1 Changed 6 years ago by cfpp2p

tr_renew was introduced 8 years ago at r2231

This results in writes outside the bounds of buffers as well as infinite loops.

I don't see this happening in the real world, but I think what is being said is that it is conceptually possible by some yet unknown intentional maliciousness.

To produce the unwanted effects there would have to be a nearly impossibly large number of torrents, peers, and trackers. What is listed, and then the associated huge arrays, strings, blocklists, etc. would most likely result in many other undesired effects permeating transmission.

I don't know if the listed situations could ever be accomplished without bypassing the logical structure of transmission by altering the source itself. Related to attack and transmission function I'm not sure of what exactly is meant.

Not to say that overflows or infinite loops are not possible as I myself can currently crash from overflow with real test torrents on #5736. Another possible overflow I've investigated is #5732 but like the tr_renew scenarios provided, I can not produce the unwanted effects in a testing environment on unaltered source.

Whether tr_renew presents any real security threat deserving of https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-7308 is at this point, I think, a matter of opinion.

Note: See TracTickets for help on using tickets.