X.org Privilege Escalation / Use-After-Free / Weak Entropy
Posted on 02 March 2017
X41 D-Sec GmbH Security Advisory: X41-2017-001 Multiple Vulnerabilities in X.org ================================= Overview -------- Vendor: X.org/Freedesktop.org Vendor URL: https://www.x.org/wiki/ Credit: X41 D-Sec GmbH, Eric Sesterhenn Advisory-URL: https://www.x41-dsec.de/lab/advisories/x41-2017-001-xorg/ Status: Public Timing attack against MIT Cookie ================================ Vulnerability Type: Other Affected Products: Xorg Server Attack Type: Local Impact: Escalation of Privileges Severity Rating: low Confirmed Affected Version: 1.19.0 and lower Confirmed Patched Version: - Vector: local CVE: CVE-2017-2624 CVSS Score: 5.9 CVSS Vector: CVSS:3.0/AV:L/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N Summary and Impact ------------------ The xorg-server uses memcmp() to check the received MIT cookie against a series of valid cookies. If the cookie is correct, it is allowed to attach to the Xorg session: XID MitCheckCookie(unsigned short data_length, const char *data, ClientPtr client, const char **reason) { struct auth *auth; for (auth = mit_auth; auth; auth = auth->next) { if (data_length == auth->len && memcmp(data, auth->data, (int) data_length) == 0) return auth->id; } *reason = "Invalid MIT-MAGIC-COOKIE-1 key"; return (XID) -1; } Since most memcmp() implementations return after an invalid byte is seen, this causes a time difference between a valid and invalid byte, which in theory could allow an efficient brute force attack[1]. Analysis -------- X41 was not able to measure a significant difference using the optimised memcmp() version of a standard Linux system, but for a naive implementation consisting of a loop comparing the bytes. Since timing attacks against memcmp() have been successful in the past [2] and fixed elsewhere [3][4] X41 would consider this an issue. If this would be exploited, it would allow a local attacker to run code in the Xorg session of another user. In order to prevent this, MIT-COOKIES should be removed or a memcmp() similar to timingsafe_memcmp()[5] used. Other projects (e.g. openssl) use timing safe memcmp() implementations to compare cookies retrieved via the network[6]. Workaround ---------- None References ---------- [1] https://cryptocoding.net/index.php/Coding_rules#Compare_secret_strings_in_constant_time [2] http://de.slideshare.net/cisoplatform7/defcon-22paulmcmillanattackingtheiotusingtimingattac [3] http://seb.dbzteam.org/crypto/python-oauth-timing-hmac.pdf [4] https://bugs.ruby-lang.org/issues/10098 [5] http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libc/string/timingsafe_memcmp.c [6] https://github.com/openssl/openssl/blob/master/ssl/t1_lib.c#L1249 Potential Use after Free in Xorg Server ======================================= Vulnerability Type: Other Affected Products: Xorg Server Attack Type: Local Impact: - Severity Rating: none Confirmed Affected Version: 1.19.0 and lower Confirmed Patched Version: Vector: local CVE: - CVSS Score: - CVSS Vector: - Summary and Impact ------------------ In XDM is a (currently non security) issue, regarding a potential use after free. The ToID() function in os/auth.c is not used anywhere, just defined in the struct and filled by the protocols, but there are no users. AuthToIDFunc ToID; /* convert cookie to ID */ X41 noticed that, XdmToID() frees the cookie argument in case it can resolve the ID or on failure, but not if it can't allocate memory for plain: XdmToID(unsigned short cookie_length, char *cookie) { XdmAuthorizationPtr auth; XdmClientAuthPtr client; unsigned char *plain; plain = malloc(cookie_length); if (!plain) return (XID) -1; for (auth = xdmAuth; auth; auth = auth->next) { XdmcpUnwrap((unsigned char *) cookie, (unsigned char *) &auth->key, plain, cookie_length); if ((client = XdmAuthorizationValidate(plain, cookie_length, &auth->rho, NULL, NULL)) != NULL) { free(client); free(cookie); free(plain); return auth->id; } } free(cookie); free(plain); return (XID) -1; } The same return value is given, whether no memory could be allocated or it just failed to lookup the ID, so the caller cannot distinguish whether this memory is freed or not, which might lead to double-free or memory leaks. The other ToID functions do not free this parameter. Weak entropy usage for session keys in libxdm ============================================= Vulnerability Type: Other Affected Products: libXdmcp Attack Type: Local Impact: Escalation of Privileges Severity Rating: medium Confirmed Affected Version: 1.1.2 and lower Confirmed Patched Version: Vector: local CVE: CVE-2017-2625 CVSS Score: 7.1 CVSS Vector: CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N Summary and Impact ------------------ To further explore the auth mechanism libXdmcp-1.1.2 was checked as well. XDM uses weak entropy to generate the session keys on non BSD systems: void XdmcpGenerateKey (XdmAuthKeyPtr key) { #ifndef HAVE_ARC4RANDOM_BUF long lowbits, highbits; srandom ((int)getpid() ^ time((Time_t *)0)); lowbits = random (); highbits = random (); getbits (lowbits, key->data); getbits (highbits, key->data + 4); #else arc4random_buf(key->data, 8); #endif } On multi user systems it might possible to check the PID of the process and how long it is running to get an estimate of these values, which could allow an attacker to attach to the session of a different user. Several checked Linux distributions (debian testing, archlinux and Ubuntu) did not link against libbsd at the time this was found. Workaround ---------- Compile against libbsd Weak Entropy Usage in Session Keys in libICE ============================================ Vulnerability Type: Other Affected Products: libICE Attack Type: Local Impact: Escalation of Privileges Severity Rating: medium Confirmed Affected Version: 1.0.9 and lower Confirmed Patched Version: Vector: local CVE: CVE-2017-2626 CVSS Score: 7.1 CVSS Vector: CVSS:3.0/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N Summary and Impact ------------------ libICE depends on arc4random() as well to generate the session cookies, thereby falling back to the same weak mechanism as libXdmcp: IceGenerateMagicCookie ( int len ) { char *auth; #ifndef HAVE_ARC4RANDOM_BUF long ldata[2]; int seed; int value; int i; #endif if ((auth = malloc (len + 1)) == NULL) return (NULL); #ifdef HAVE_ARC4RANDOM_BUF arc4random_buf(auth, len); #else #ifdef ITIMER_REAL { struct timeval now; X_GETTIMEOFDAY (&now); ldata[0] = now.tv_sec; ldata[1] = now.tv_usec; } #else { long time (); ldata[0] = time ((long *) 0); ldata[1] = getpid (); } #endif seed = (ldata[0]) + (ldata[1] << 16); srand (seed); for (i = 0; i < len; i++) { value = rand (); auth[i] = value & 0xff; } #endif auth[len] = '