xorl %eax, %eax

CVE-2011-1079: Linux kernel Bluetooth Missing NULL Termination

leave a comment »

This was also reported by Vasiliy Kulikov and it is located in net/bluetooth/bnep/sock.c file.

static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
        struct bnep_connlist_req cl;
        struct bnep_connadd_req  ca;
  ...
        switch (cmd) {
        case BNEPCONNADD:
                if (!capable(CAP_NET_ADMIN))
                        return -EACCES;

                if (copy_from_user(&ca, argp, sizeof(ca)))
                        return -EFAULT;

                nsock = sockfd_lookup(ca.sock, &err);
                if (!nsock)
                        return err;

                if (nsock->sk->sk_state != BT_CONNECTED) {
                        sockfd_put(nsock);
                        return -EBADFD;
                }

                err = bnep_add_connection(&ca, nsock);
                if (!err) {
                        if (copy_to_user(argp, &ca, sizeof(ca)))
                                err = -EFAULT;
                } else
                        sockfd_put(nsock);

                return err;
  ...
        return 0;
}

As you can see this one requires ‘CAP_NET_ADMIN’ capability in order to run but if this is the case, you can provide a ‘bnep_connadd_req’ structure with non-NULL terminated device name. For completeness, here is the equivalent structure definition as seen in net/bluetooth/bnep/bnep.h header file.

struct bnep_connadd_req {
        int   sock;       // Connected socket
        __u32 flags;
        __u16 role;
        char  device[16]; // Name of the Ethernet device
};

Now looking back into bnep_sock_ioctl() we see that it will call bnep_add_connection() passing the user derived structure. The latter routine is part of net/bluetooth/bnep/core.c that will call the following routine.

int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
{
        struct net_device *dev;
        struct bnep_session *s, *ss;
        u8 dst[ETH_ALEN], src[ETH_ALEN];
  ...
        /* session struct allocated as private part of net_device */
        dev = alloc_netdev(sizeof(struct bnep_session),
                           (*req->device) ? req->device : "bnep%d",
                           bnep_net_setup);
  ...
        return err;
}

This one transfers the execution to a C macro as this is defined in include/linux/netdevice.h header file.

/* Support for loadable net-drivers */
extern struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
                                       void (*setup)(struct net_device *),
                                       unsigned int txqs, unsigned int rxqs);
#define alloc_netdev(sizeof_priv, name, setup) \
        alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1)

Which in fact makes a call to alloc_netdev_mqs() as this is defined in net/core/dev.c

struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name,
                void (*setup)(struct net_device *),
                unsigned int txqs, unsigned int rxqs)
{
        struct net_device *dev;
        size_t alloc_size;
        struct net_device *p;

        BUG_ON(strlen(name) >= sizeof(dev->name));
  ...
free_p:
        kfree(p);
        return NULL;
}

So, almost certainly this check will trigger the BUG_ON(). However, users will sufficient capabilities can still provide smaller device names in order to leak kernel memory data when the structure is copied back to them.

The fix was obviously to NULL terminate the device name.

 		}
+		ca.device[sizeof(ca.device)-1] = 0;
 
 		err = bnep_add_connection(&ca, nsock);

Written by xorl

May 3, 2011 at 19:56

Posted in bugs, linux

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s