~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~* Product: Linux Kernel Versions: <= 2.4.25 Bug: Integer overflow Impact: Attackers may be able to execute arbitrary code with kernel-level privileges. Risk: High Date: May 11, 2004 Author: Shaun Colley Email: shaunige yahoo co uk WWW: http://www.nettwerked.co.uk ~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~* Introduction ############# The Linux Kernel is the core of the Linux Operating System, written entirely from scratch with assistance from a group of loosely-knit hackers across the Net. The Linux Kernel project aims for POSIX compatibility, and implements everything one would expect from a modern, rapidly-developed kernel; networking, multimedia support, peripheral support, etc. Within the vast support for networking, there lies a bug, in kernel versions 2.4.25 and below. The bug is an integer overflow, which could result in too little memory being allocated, resulting in overwriting of kernel memory. Versions 2.4.26 and above, and 2.6.X are not vulnerable to the issue, as they removed the vulnerable socket option, as it was considered less than mandatory. Details ######## The bug exists in the SCTP implementation, which resides in 'net/sctp' in the Linux Kernel source tree. Due to insufficient sanitizing of function arguments, the sctp_setsockopt() is vulnerable to an integer overflow when parsing and dealing with the SCTP 'SCTP_SOCKOPT_DEBUG_NAME' socket option, leading up to the allocation of memory. Below is the vulnerable code: --- net/sctp/socket.c snippet --- switch (optname) { case SCTP_SOCKOPT_DEBUG_NAME: /* BUG! we don't ever seem to free this memory. --jgrimm */ if (NULL == (tmp = kmalloc(optlen + 1, GFP_KERNEL))) { retval = -ENOMEM; goto out_unlock; } if (copy_from_user(tmp, optval, optlen)) { retval = -EFAULT; goto out_unlock; } tmp[optlen] = '\000'; sctp_sk(sk)->ep->debug_name = tmp; break; --- When the kmalloc() call is invoked to allocate 'optlen' amount of memory, 1 is incremented to 'optlen' to ensure enough memory is allocated for the option value (optval). However, since sanitization of function arguments are failed to be performed, 'optlen' could be the maximum value that an unsigned integer can hold correctly, thus causing the value to wrap around when the calculation 'optlen + 1' is performed. Below is the vulnerable call: --- if (NULL == (tmp = kmalloc(optlen + 1, GFP_KERNEL))) { retval = -ENOMEM; goto out_unlock; } --- Because kmalloc() takes the 'count' variable as an unsigned number, negative numbers are interpreted as large unsigned numbers. However, if -1 is passed as 'optlen' (represented as 0xffffffff (hex) in unsigned variables, which is the largest value an unsigned integer can hold correctly), an integer overflow will occur in optlen (as a result of the kmalloc call incrementing optlen by 1), causing the value to wrap around to 0. This is illustrated below: --- User passes: optlen = -1 (signed) (-1 = 0xffffffff unsigned) kmalloc interprets the optlen variable as unsigned: -- if (NULL == (tmp = kmalloc(0xffffffff + 1, GFP_KERNEL))) { retval = -ENOMEM; goto out_unlock; } 0xffffffff + 1 = 0x0 --- And thus, due to the integer overflow, 0 is passed to kmalloc(), causing too little memory to be allocated to hold 'optval'. Following the memory allocation, a copy_from_user() call is implemented to copy the contents of the user-supplied 'optval' into the new memory freshly allocated: --- if (copy_from_user(tmp, optval, optlen)) { retval = -EFAULT; goto out_unlock; } --- Assuming the user passed -1 as optlen, the above copy_from_user call can be represented as below: --- optlen = -1 (0xffffffff unsigned (-1)) copy_from_user() call: -- if (copy_from_user(tmp, optval, 0xffffffff)) { retval = -EFAULT; goto out_unlock; } --- Because of the integer overflow in the kmalloc() call, too little memory was allocated for optval, followed by a copy_from_user() call which copies a large amount of data from user-space (optval) into the allocated memory. Since too little memory may have been allocated, this could result in overwriting of kernel memory, and ultimately privilege elevation to kernel-level privileges, if exploited properly. Please note that this vulnerability only exists in the SCTP_SOCKOPT_DEBUG_NAME SCTP socket option. Exploitation ############# To exploit the flaw, a sctp_setsockopt() call with the following values would need to be implemented: --- level = SOL_SCTP optname = SCTP_SOCKOPT_DEBUG_NAME optval = expl_payload optlen = -1 [ ... ] sctp_setsockopt(mysock, SOL_SCTP, SCTP_SOCKOPT_DEBUG_NAME, expl_payload, -1); [ ... ] --- Note that the level SOL_SCTP must be supplied, rather than IPPROTO_SCTP level, because IPPROTO_SCTP causes the causes the function to set options via another function. Solution ######### I reported this bug to the linux-net list, but since the 'SCTP_SOCKOPT_DEBUG_NAME' SCTP socket option was removed in kernels 2.4.26 (and above) and 2.6, this was considered to be an easily resolvable issue: Upgrade to Linux Kernel 2.4.6 or 2.6. This removes all possibility of the vulnerability, since the offending socket option has essentially been removed. Another benefit of upgrading to the latest kernel version if the vast improvements in the SCTP implementation - the implementation has been improved and expanded some. Please note that the flaw reported in this advisory was dismissed as a "non-issue" on the linux-net mailing-list, so I saw it appropriate to post this here. A quick fix for the issue is to apply the following patch: --- sctp_vuln.patch --- --- socket.orig.c 2004-05-11 18:31:45.000000000 +0100 +++ socket.c 2004-05-11 18:32:40.000000000 +0100 @@ -1516,18 +1516,7 @@ switch (optname) { case SCTP_SOCKOPT_DEBUG_NAME: - /* BUG! we don't ever seem to free this memory. --jgrimm */ - if (NULL == (tmp = kmalloc(optlen + 1, GFP_KERNEL))) { - retval = -ENOMEM; - goto out_unlock; - } - - if (copy_from_user(tmp, optval, optlen)) { - retval = -EFAULT; - goto out_unlock; - } - tmp[optlen] = '\000'; - sctp_sk(sk)->ep->debug_name = tmp; + /* do nothing */ break; case SCTP_SOCKOPT_BINDX_ADD: --- EOF --- (patch also available here: ) Apply the patch and recompile the kernel: --- root# cd /usr/src/linux/net/sctp root# patch < sctp_vuln.patch patching file socket.c root# cd /usr/src/linux root# make oldconfig && make dep && make bzImage && make modules && make modules_install --- The above patch was created to apply cleanly to the 2.4.25 Linux Kernel version. The above patch removes the code which handles the vulnerable socket option. Credit ####### This vulnerability was discovered by Shaun Colley / shaun2k2 - . Thanks to people on the linux-net list for acknowledging the issue (or at least in part). Disclaimer ########### The information contained within this document was believed to be accurate at the time of it's publishing. However, it may be inaccurate at times, so don't consider any information to be 'set in stone', and I do not guarantee the accuracy of information contained within this 'advisory'. Please feel free to email me with mistakes or errors I have made, as long as they are nicely phrased. Flames should be directed to /dev/null - I am not interested in any policies I may or may not have followed. Thank you for your time. Shaun.