The usage of the SCTP implementation in all versions prior to 2.4.26 of the Linux kernel are susceptible to an integer overflow.
f066b2cbb6af3fe4fe3ad12ba310fa17c52d57e4b55c41f6d53c4a9f4d6b0539
~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*~*
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:
<hXXp://www.nettwerked.co.uk/code/sctp_vuln.patch>)
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 - <shaunige yahoo co uk>.
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.