Bug 1595595 (CVE-2018-1000517)

Summary: CVE-2018-1000517 busybox: wget: Heap-based buffer overflow in the retrieve_file_data() function
Product: [Other] Security Response Reporter: Andrej Nemec <anemec>
Component: vulnerabilityAssignee: Red Hat Product Security <security-response-team>
Status: CLOSED NOTABUG QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: admiller, anemec, athmanem, dvlasenk, sgayou
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2018-06-29 18:44:25 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On: 1595679    
Bug Blocks: 1595598    

Description Andrej Nemec 2018-06-27 08:11:29 UTC
A heap buffer overflow vulnerability was found in wget.

Upstream patch:

https://git.busybox.net/busybox/commit/?id=8e2174e9bd836e53c8b9c6e00d1bc6e2a718686e

Comment 1 Andrej Nemec 2018-06-27 08:11:57 UTC
Created wget tracking bugs for this issue:

Affects: fedora-all [bug 1595596]

Comment 3 Tomáš Hozza 2018-06-27 10:50:40 UTC
(In reply to Andrej Nemec from comment #0)
> A heap buffer overflow vulnerability was found in wget.
> 
> Upstream patch:
> 
> https://git.busybox.net/busybox/commit/
> ?id=8e2174e9bd836e53c8b9c6e00d1bc6e2a718686e

That is definitely not an upstream git and that file does not exist in wget. It is probably some "custom" busybox implementation of "wget" command, but it does not seem to have much in common with GNU wget we ship in RHEL and Fedora.

Comment 4 Andrej Nemec 2018-06-27 10:55:02 UTC
Yep, this is not upstream wget but a busybox wget. I'm changing the whiteboard to reflect this. Thanks!

Comment 5 Andrej Nemec 2018-06-27 10:55:20 UTC
Created busybox tracking bugs for this issue:

Affects: fedora-all [bug 1595679]

Comment 9 Scott Gayou 2018-07-02 17:06:41 UTC
The flaw appears to be that when HTTP chunks are sent, the chunk size is implicitly trusted and cast to off_t, which is signed. A specific range of values can bypass multiple checks and get a large, attacker-controlled value to a fread which can result in a heap buffer overflow.

The reason for the newer build crash is as follows:

if (G.got_clen) {
        if (G.content_len < (off_t)sizeof(G.wget_buf)) {
                if ((int)G.content_len <= 0)
                        break;
                rdsz = (unsigned)G.content_len;
        }
}

G.content_len is attacker-controlled. got_clen is set after we control content_len. content_len is a off_t, so it can be negative. If an attacker makes it negative, we bypass the first check (<= sizeof(G.wget_buf)). The second check casts an off_t to an int, which is a truncation.

You'd see this warning without the explicit cast:

implicit conversion loses integer precision: 'off_t' (aka 'long') to 'int'
      [-Wshorten-64-to-32]

Thus, if it truncates to become a positive value, we bypass the next check (<= 0) and now control rdsz. This then buffer overflows char wget_buf[CONFIG_FEATURE_COPYBUF_KB*1024]; CONFIG_FEATURE_COPYBUF_KB seems to usually between 4 and 8 based on build options.

So if we pass in 0xffffffff0001f400, it truncates to 0x0001f400, which is roughly 128KB. Heap overflow.

In RHEL6 we don't do the truncation:

unsigned rdsz = sizeof(buf);
	
if (content_len < sizeof(buf) && (G.chunked || G.got_clen))
    rdsz = (unsigned)content_len;
n = safe_fread(buf, rdsz, dfp);

content_len (signed long) will be converted to an unsigned long. If content_len was negative, this would result in a large positive value, and the < sizeof(buf) check will capture this edge case and prevent attacker control of rdsz.

Furthermore, several lines up, there is the following line:

while (content_len > 0 || !G.got_clen) {

This will prevent content_len from being negative unless !G.got_clen is true. 

The vulnerable code can only be hit during chunked transmission. However, G.got_clen is set when chunked is enabled.

As such, this does not appear to affect RHEL6. The RHEL5 anaylsis follows from the above.