miniupnpc 2.0.20170421 Denial Of Service
- Details
- Written by: khalil shreateh
- Category: Vulnerabilities
- Hits: 959
Author: <github.com/tintinweb>
Ref: https://github.com/tintinweb/pub/tree/master/pocs/cve-2017-8798
Version: 0.6
Date: May 1st, 2017
Tag:
Author: <github.com/tintinweb>
Ref: https://github.com/tintinweb/pub/tree/master/pocs/cve-2017-8798
Version: 0.6
Date: May 1st, 2017
Tag: miniupnp miniupnpc getHTTPResponse chunked encoding integer signedness error
Overview
--------
Name: miniupnpc
Vendor: Thomas Bernard
References: * http://miniupnp.free.fr/ [1]
Version: v2.0 [2]
Latest Version: v2.0.20170421 [2][3]
Other Versions: >= v1.4.20101221 [2] (released 21/12/2010; ~6 years ago)
Platform(s): cross
Technology: c
Vuln Classes: CWE-196, CWE-190
Origin: remote
Min. Privs.: ---
CVE: CVE-2017-8798
Description
---------
quote website [1]
>UPnP IGD client lightweight library and UPnP IGD daemon
>The UPnP protocol is supported by most home adsl/cable routers and
Microsoft Windows 2K/XP. The aim of the MiniUPnP project is to bring a
free software solution to support the "Internet Gateway Device" part of
the protocol. The MediaServer/MediaRenderer UPnP protocol (DLNA) is also
becoming very popular but here we are talking about IGD. ReadyMedia
(formely known as MiniDLNA) is a UPnP Media Server using some UPnP code
from MiniUPnPd.
miniupnp is part of many applications / embedded network devices
* P2P File Sharing software
* Network Device Firmware
* Blockchain clients
* ...
Summary
-------
*TL;DR - one-click crash miniupnpc based applications on your network*
#### Integer signedness error in miniupnpc allows remote attackers to
cause a denial of service condition via specially crafted HTTP response
An integer signedness error was found in miniupnp's `miniwget` allowing
an unauthenticated remote entity typically located on the
local network segment to trigger a heap corruption or an access violation
in miniupnp's http response parser when processing a specially crafted
chunked-encoded response to a request for the xml root description url.
To exploit this vulnerability, an attacker only has to provide a
chunked-encode HTTP response with a negative chunk length to upnp
clients requesting a resource on the attackers webserver. Upnp clients
can easily be instructed to request resources on the attackers webserver
by answering SSDP discovery request or by issueing SSDP service
notifications (low complexity, integral part of the protocol).
* remote, unauthenticated, `ACCESS_VIOLATION_READ` and heap corruption
* (confirmed) DoS; (unconfirmed) other impacts
see attached PoC
see proposed patch
Details
-------
The vulnerable component is a HTTP file download method called
`miniwget` (precisely `getHTTPResponse`) that fails to properly handle
invalid chunked-encoded HTTP responses. The root cause is a bounds check
that mistakenly casts an unsigned attacker-provided chunksize to signed
int leading to an incorrect decision on the destination heap buffer size
when copying data from the server response to an internal buffer. The
attacker controls both the size of the internal buffer as well as the
number of bytes to copy. In order for this attack to succeed, the number
of bytes to copy must be negative.
attacker controls:
* `int content_length`
* `unsigned int chunksize`
* `bytestocopy` if `(int) chunksize` is negative (or at least < `n-i` ~ 1900 bytes)
* length of `content_buf` if `bytestocopy` is negative
In the end, the attacker controls
* `realloc(content_buf, content_length)`
* `memcpy(content_buf+x, http_response, chunksize)`
client (miniupnpc) server (poc.py)
| |
| |
| SSDP: Discovery - M-SEARCH |
1. | --------------------------------------> |
| |
| SSDP: Reply - Location Header |
2. | <-------------------------------------- |
| |
| GET (Location Header/xxxx.xml) |
3. | --------------------------------------> |
| |
| HTTP chunked-encoded reply |
4. | <-------------------------------------- |
| |
1. application performs SSDP discovery via M-SEARCH (multicast, local network
segment)
2. poc.py responds with the url to the xml root description requesting the
application to navigate to the malicious webserver.
3. application requests xml root description url (taken from reply to M-SEARCH,
Location Header) on malicious webserver (poc.py)
4. poc.py responds with a specially crafted http response triggering the heap
overwrite in miniupnp
#### Source
*Note:* Inline annotations are prefixed with //#!
*Note:* This is a stripped down version of the vulnerable code. For full details
see https://github.com/tintinweb/pub/tree/master/pocs/cve-2017-8798
`miniwget.c:236` [4]
* A) 1. to 3. is the parsing of the chunksize
* B) 4. to 5. integer signedness error
* C) 6. integer wrapping
* D) 7. to 9. destination buffer size
* E) 10. heap overwrite with size in bytestocopy
...
//#! 4)
//#! goal: a) bytestocopy becomes negative due to chunksize being negative
//#! b) content_length defines destination buffer size
//#! c) overwrite destination heap buffer content_buf[content_length] with bytestocopy bytes from request
//#! memcopy(content_buf[content_length], req_body, (unsigned)bytestocopy)
//#!
bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i); //#! 5) boom! - bytestocopy becomes chunksize since chunksize is negative (e.g. -1)
if((content_buf_used + bytestocopy) > content_buf_len) //#! 6) true, since bytestocopy is negative, wraps unsigned content_buf_used
{
char * tmp;
if(content_length >= (int)(content_buf_used + bytestocopy)) { //#! 7) content_length is attacker controlled.
content_buf_len = content_length; //#! 8) we want content_length to define our dst buffer size (e.g. 1)
} else { //#! if we dont hit this, content_buf_len would likely be ~2k
content_buf_len = content_buf_used + bytestocopy;
}
tmp = realloc(content_buf, content_buf_len); //#! 9) realloc to content_length bytes (e.g. 9000)
if(tmp == NULL) {
/* memory allocation error */
free(content_buf);
free(header_buf);
*size = -1;
return NULL;
}
content_buf = tmp;
}
memcpy(content_buf + content_buf_used, buf + i, bytestocopy); //#! 10) boom heap overwrite with bytesttocopy bytes (e.g. (unsigned)-1) to content_length (e.g. 9000) sized buffer
content_buf_used += bytestocopy; //#! (also an out of bounds ready since it has not been checked if buf holds enough bytes)
i += bytestocopy;
chunksize -= bytestocopy;
#### Taint Graph
basically all `miniwget*` and `UPNP_*` methods.
* getHTTPResponse (vulnerable)
* miniwget3
* miniwget2
* miniwget
* miniwget_getaddr
* UPNP_GetIGDFromUrl
* UPNP_GetValidIGD
* UPnP_selectigd
* UPNP_Get*
* UPNP_Check*
* UPNP_Delete*
* UPNP_Update*
* UPNP_Add*
#### Scenarios
The PoC can be configured for three scenarios:
##### 1) SCENARIO_CRASH_LARGE_MEMCPY
Similar to 3) attempts to smash the heap but likely fails with an
`ACCESS_VIOLATION_READ` when trying to read from an non-accessible
memory region.
details see [7]
##### 2) SCENARIO_CRASH_REALLOC_NULLPTR
Miniupnp v1.8 was missing an error check for `realloc` which can
be used to cause a DoS condition when making `realloc` fail while
allocating a large chunk of data. When `realloc` fails - because
the requested size of memory cannot be allocated - it returns a
`nullptr`. Miniupnp ~1.8 was missing a check for the `nullptr`
and tried to `memcpy` bytes from the attackers http response to
that `nullptr` which fails with an `ACCESS_VIOLATION`.
To provoke this scenario one must provide an arbitrarily large
`content_length` (e.g. `0x7fffffff` likely fails on 32 bits) and
make `memcpy` attempt to copy a byte to that location.
##### 3) SCENARIO_CRASH_1_BYTE_BUFFER
The idea is to create a small heap buffer and overwrite it with
a large chunk of data. This can be achieved by instructing
miniupnp to `realloc` `content_buf` to a size of `1 byte` by
providing a `content-length` of `1`. To overwrite this 1 byte
buffer the attacker provides a negative chunksize e.g.
`0x80000000`. Depending on the implementation of `memcpy` and
the memory layout `memcpy` will either fail with a
`ACCESS_VIOLATION_READ` as we're only providing <= 2048 bytes
with the server response and will most certainly hit a non-accessible
memory region while copying `0x80000000` bytes or the application
crashes because of a heap corruption.
Here's an example of `miniupnpc` corrupting the heap when compiled
for 32 bit platforms.
AC/Ao 0x80504de <getHTTPResponse+1912> call memcpy@plt <0x8048a20>
dest: 0x805981f AC/AC/ 0x0 //#! <--- size 1 - attacker controlled content_buf
src: 0xffffb77e AC/AC/ 0x41414141 ('AAAA') //#! <--- attacker controlled http response
n: 0x80000000 //#! <--- attacker controlled (must be negative) bytestocopy
pwndbg> i lo
i = 30
buf = "f <xml>BOOM</x"...
n = <optimized out>
endofheaders = 91
chunked = 1
content_length = 1
chunksize = 2147483648
bytestocopy = 2147483648 //#! <--- nr of bytes to copy from buf
header_buf = 0x8059008 "HTTP/1.1 200 OK"...
header_buf_len = 2048
header_buf_used = <optimized out>
content_buf = 0x8059810 "<xml>BOOM</x351a 02"
content_buf_len = 1 //#! <--- destination, realloc'd to 1
content_buf_used = 15
chunksize_buf = "