xorl %eax, %eax

Archive for the ‘vulnerabilities’ Category

CVE-2017-17046: Xen ARM Information Leak

leave a comment »

This vulnerability was released with XSA-245 security advisory by Julien Grall of ARM. The issue is simply the lack of cleaning up memory which means that one domain might end up having access to uncleaned memory of another domain after reboots (soft or hard). This vulnerability affects only ARM because they do not support NUMA and, as J. Grall explained, specific regions (like the Dom0 kernel) are marked as reserved until the hardware domain is built and they are subsequently copied to its memory. To fix this, xen/common/page_alloc.c was modified as you see below.

diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c
index 0b9f6cc6df..fbe5a8af39 100644
--- a/xen/common/page_alloc.c
+++ b/xen/common/page_alloc.c
@@ -1700,6 +1700,16 @@ static void init_heap_pages(
 {
     unsigned long i;
 
+    /*
+     * Some pages may not go through the boot allocator (e.g reserved
+     * memory at boot but released just after --- kernel, initramfs,
+     * etc.).
+     * Update first_valid_mfn to ensure those regions are covered.
+     */
+    spin_lock(&heap_lock);
+    first_valid_mfn = min_t(unsigned long, page_to_mfn(pg), first_valid_mfn);
+    spin_unlock(&heap_lock);
+
     for ( i = 0; i < nr_pages; i++ )
     {
         unsigned int nid = phys_to_nid(page_to_maddr(pg+i));

And in the same source code file, the equivalent helpers were added to make the above change feasible.

diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c
index fbe5a8af39..472c6fe329 100644
--- a/xen/common/page_alloc.c
+++ b/xen/common/page_alloc.c
@@ -192,7 +192,11 @@ PAGE_LIST_HEAD(page_broken_list);
  * BOOT-TIME ALLOCATOR
  */
 
-static unsigned long __initdata first_valid_mfn = ~0UL;
+/*
+ * first_valid_mfn is exported because it is use in ARM specific NUMA
+ * helpers. See comment in asm-arm/numa.h.
+ */
+unsigned long first_valid_mfn = ~0UL;
 
 static struct bootmem_region {
     unsigned long s, e; /* MFNs @s through @e-1 inclusive are free */
diff --git a/xen/include/asm-arm/numa.h b/xen/include/asm-arm/numa.h
index a2c1a3476d..3e7384da9e 100644
--- a/xen/include/asm-arm/numa.h
+++ b/xen/include/asm-arm/numa.h
@@ -12,9 +12,15 @@ static inline __attribute__((pure)) nodeid_t phys_to_nid(paddr_t addr)
     return 0;
 }
 
+/*
+ * TODO: make first_valid_mfn static when NUMA is supported on Arm, this
+ * is required because the dummy helpers is using it.
+ */
+extern unsigned long first_valid_mfn;
+
 /* XXX: implement NUMA support */
-#define node_spanned_pages(nid) (total_pages)
-#define node_start_pfn(nid) (pdx_to_pfn(frametable_base_pdx))
+#define node_spanned_pages(nid) (max_page - first_valid_mfn)
+#define node_start_pfn(nid) (first_valid_mfn)
 #define __node_distance(a, b) (20)

Written by xorl

January 22, 2018 at 10:15

Posted in vulnerabilities

Linux kernel RDMA NULL pointer dereference

leave a comment »

While reading through Linux kernel’s ChangeLog I came across this one. I was unable to find any CVE ID for this vulnerability reported by Håkon Bugge on 6 December 2017. The vulnerability was discovered by Google’s syzkaller kernel fuzzer and reported in syzkaller719569. The vulnerable code starts in the setsockopt() system call implementation for RDS (Reliable Datagram Sockets). This code is located at net/rds/af_rds.c and here is the code path we are interested in.

static int rds_setsockopt(struct socket *sock, int level, int optname,
			  char __user *optval, unsigned int optlen)
{
	struct rds_sock *rs = rds_sk_to_rs(sock->sk);
	int ret;
   ...
	case RDS_GET_MR:
		ret = rds_get_mr(rs, optval, optlen);
		break;
	case RDS_GET_MR_FOR_DEST:
		ret = rds_get_mr_for_dest(rs, optval, optlen);
		break;
   ...
}

Before we check those two options, it is important to see some specific value in “rds_sock” structure. This structure is defined in net/rds/rds.h header file. The key here is that “rs_transport” pointer in “rds_sock” structure can be NULL.

/**
 * struct rds_transport -  transport specific behavioural hooks
   ...
 */
   ...
struct rds_transport {
   ...
};

struct rds_sock {
   ...
	/*
	 * bound_addr used for both incoming and outgoing, no INADDR_ANY
	 * support.
	 */
   ...
	struct rds_transport    *rs_transport;
   ...
};

Back in rds_setsockopt(), there are two options with very similar code. The RDS_GET_MR and RDS_GET_MR_FOR_DES which will both result in a code that uses copy_from_user() to copy the “rds_get_mr_args” or “rds_get_mr_for_dest_args” structure to the kernel space, and then invoke __rds_rdma_map() routine to do the mapping.

int rds_get_mr(struct rds_sock *rs, char __user *optval, int optlen)
{
	struct rds_get_mr_args args;
   ...
	if (copy_from_user(&args, (struct rds_get_mr_args __user *)optval,
			   sizeof(struct rds_get_mr_args)))
		return -EFAULT;

	return __rds_rdma_map(rs, &args, NULL, NULL);
}

int rds_get_mr_for_dest(struct rds_sock *rs, char __user *optval, int optlen)
{
   ...
	if (copy_from_user(&args, (struct rds_get_mr_for_dest_args __user *)optval,
			   sizeof(struct rds_get_mr_for_dest_args)))
		return -EFAULT;
   ...
	return __rds_rdma_map(rs, &new_args, NULL, NULL);
}

If we now move to __rds_rdma_map() in net/rds/rdma.c we will quickly notice what’s the issue that triggers the vulnerability. The code will try to access the get_mr() callback function from the “rs_transport” pointer which as mentioned earlier can be unset (NULL), resulting in a NULL pointer dereference. This code path can be reached from the previously described setsockopt() system call options.

static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
				u64 *cookie_ret, struct rds_mr **mr_ret)
{
   ...
	if (!rs->rs_transport->get_mr) {
		ret = -EOPNOTSUPP;
		goto out;
	}
   ...
	return ret;
}

The patch was to add an additional check that ensures that “rs_transport” is not NULL. If it’s NULL the __rds_rdma_map() will exit with “ENOTCONN” (Transport endpoint is not connected) error code.

diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 8886f15abe90..bc2f1e0977d6 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -183,7 +183,7 @@  static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args,
 	long i;
 	int ret;
 
-	if (rs->rs_bound_addr == 0) {
+	if (rs->rs_bound_addr == 0 || !rs->rs_transport) {
 		ret = -ENOTCONN; /* XXX not a great errno */
 		goto out;
 	}

Written by xorl

December 18, 2017 at 23:19

Posted in vulnerabilities

CVE-2017-17450: Linux kernel nfnl_osf security bypass

leave a comment »

This vulnerability is identical to CVE-2017-17448. It was reported by Kevin Cernekee and it affects the OSF nsfnetlink helper in the same way as CVE-2017-17448 affected the connection tracking helper of NetFilter. Basically, the lack of “CAP_NET_ADMIN” capability checks means that any unprivileged user can execute commands like the following to create or delete namespaces and also obtaining “CAP_NET_ADMIN” capability in the created ones.

    vpnns -- nfnl_osf -f /tmp/pf.os
    vpnns -- nfnl_osf -f /tmp/pf.os -d

Similarly to CVE-2017-17448, the fix was to add the missing checks in the add and remove functions as you can see below.

diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 36e14b1..a34f314 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -19,6 +19,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 
+#include <linux/capability.h>
 #include <linux/if.h>
 #include <linux/inetdevice.h>
 #include <linux/ip.h>
@@ -70,6 +71,9 @@ static int xt_osf_add_callback(struct net *net, struct sock *ctnl,
 	struct xt_osf_finger *kf = NULL, *sf;
 	int err = 0;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (!osf_attrs[OSF_ATTR_FINGER])
 		return -EINVAL;
 
@@ -115,6 +119,9 @@ static int xt_osf_remove_callback(struct net *net, struct sock *ctnl,
 	struct xt_osf_finger *sf;
 	int err = -ENOENT;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (!osf_attrs[OSF_ATTR_FINGER])
 		return -EINVAL;

Written by xorl

December 10, 2017 at 13:53

Posted in vulnerabilities

CVE-2017-17448: Linux kernel cthelper security bypass

leave a comment »

This is a vulnerability reported by Kevin Cernekee on 3 December 2017 and it affects nfnl_cthelper of NeFilter. The nfnl_cthelper is a connection tracking helper for the user-space and its code resides in net/netfilter/nfnetlink_cthelper.c. What Kevin Cernekee noticed is that nfnetlink_rcv() (located at net/netfilter/nfnetlink.c) checks if the caller has the “CAP_NET_ADMIN” permission capability but there is a design flaw.

static void nfnetlink_rcv(struct sk_buff *skb)
{
   ...
	if (!netlink_net_capable(skb, CAP_NET_ADMIN)) {
		netlink_ack(skb, nlh, -EPERM, NULL);
		return;
	}
   ...
}

This code checks for the “CAP_NET_ADMIN” capability only in the namespace that owns the “skb” socket. On the other hand, the nfnl_cthelper is a user-space helper that is shared by all network namespaces. Since there are no “CAP_NET_ADMIN” capability checks in nfnl_cthelper it means that an unprivileged user can create namespaces, bypassing the “CAP_NET_ADMIN” capability check. Kevin Cernekee’s patch for this vulnerability was to add the missing capability checks on the “new”, “get”, and “del” functions of this helper. You can see the patch here.

diff --git a/net/netfilter/nfnetlink_cthelper.c b/net/netfilter/nfnetlink_cthelper.c
index 41628b393673..d33ce6d5ebce 100644
--- a/net/netfilter/nfnetlink_cthelper.c
+++ b/net/netfilter/nfnetlink_cthelper.c
@@ -17,6 +17,7 @@ 
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/errno.h>
+#include <linux/capability.h>
 #include <net/netlink.h>
 #include <net/sock.h>
 
@@ -407,6 +408,9 @@  static int nfnl_cthelper_new(struct net *net, struct sock *nfnl,
 	struct nfnl_cthelper *nlcth;
 	int ret = 0;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (!tb[NFCTH_NAME] || !tb[NFCTH_TUPLE])
 		return -EINVAL;
 
@@ -611,6 +615,9 @@  static int nfnl_cthelper_get(struct net *net, struct sock *nfnl,
 	struct nfnl_cthelper *nlcth;
 	bool tuple_set = false;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (nlh->nlmsg_flags & NLM_F_DUMP) {
 		struct netlink_dump_control c = {
 			.dump = nfnl_cthelper_dump_table,
@@ -678,6 +685,9 @@  static int nfnl_cthelper_del(struct net *net, struct sock *nfnl,
 	struct nfnl_cthelper *nlcth, *n;
 	int j = 0, ret;
 
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
 	if (tb[NFCTH_NAME])
 		helper_name = nla_data(tb[NFCTH_NAME]);

Written by xorl

December 10, 2017 at 13:46

Posted in vulnerabilities

CVE-2017-17085: Wireshark CIP Safety dissector integer overflow

leave a comment »

On 30 November 2017 Wireshark released their WNPA-SEC-2017-49 security advisory. The security advisory describes a vulnerability in the CIP Safety protocol dissector which is identified as DoS. The vulnerability was discovered by “Buildbot Builder” via fuzzing and the trigger PoC PCAP is available online (fuzz-2017-11-28-28119.pcap). The vulnerable code is in epan/dissectors/packet-cipsafety.c and you can see the exact code path below.

/* packet-cipsafety.c
 * Routines for CIP (Common Industrial Protocol) Safety dissection
 * CIP Safety Home: www.odva.org
   ...
 */
   ...
static void
dissect_cip_safety_data( proto_tree *tree, proto_item *item, tvbuff_t *tvb, int item_length, packet_info *pinfo)
{
   int base_length, io_data_size;
   gboolean multicast = (((pntoh32(pinfo->dst.data)) & 0xf0000000) == 0xe0000000);
   ...
   /* compute the base packet length to determine what is actual I/O data */
   base_length = multicast ? 12 : 6;
   ...
      case CIP_SAFETY_EXTENDED_FORMAT:
         if (item_length-base_length <= 2)
         {
   ...
            proto_tree_add_item(tree, hf_cipsafety_crc_s5_0, tvb, item_length-base_length+1, 1, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(tree, hf_cipsafety_crc_s5_1, tvb, item_length-base_length+2, 1, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(tree, hf_cipsafety_timestamp, tvb, item_length-base_length+3, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(tree, hf_cipsafety_crc_s5_2, tvb, item_length-base_length+5, 1, ENC_LITTLE_ENDIAN);
   ...
}

As you can see from the above code snippet, dissect_cip_safety_data() retrieves a signed integer (item_length) as an argument. Later on, it calculates “base_length”. Then, if the packet is CIP Safety in extended format it will do the comparison that you see above. If “item_length” results in a negative value it will enter the ‘if’ clause and try to invoke proto_tree_add_item() with a negative length variable. The latter will try to copy a large amounts (specifically equal to “item_length”) of data leading to a segmentation fault due to memory corruption. Below you can see the patch that Pascal Quantin developed to fix this vulnerability.

     /* compute the base packet length to determine what is actual I/O data */
     base_length = multicast ? 12 : 6;
	 
+    if (item_length <= base_length) {
+       expert_add_info(pinfo, item, &ei_mal_io);
+       return;
+    }
+

The above ensures that “item_length” will never be smaller than “base_length” to enter the dissection code. This should catch cases of “item_length” being negative.

Written by xorl

December 10, 2017 at 13:28

Posted in vulnerabilities

CVE-2017-15868: Linux kernel l2cap BNEP privilege escalation

leave a comment »

The Linux kernel project patched a vulnerability discovered by Al Viro in the BNEP (Bluetooth Network Encapsulation Protocol) implementation of the Linux Bluetooth stack (BlueZ). The code for this is located at net/bluetooth/bnep/core.c and specifically in bnep_add_connection() that you see below.

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];
	int err;

	BT_DBG("");

	baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
	baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
   ...
}

The above is not immediately noticeable but “sock” is user derived and it is never verified until it’s used by baswap() to set the source and destination channels. You can see how l2cap_pi() macro is defined in include/net/bluetooth/l2cap.h header file here.

/* ----- L2CAP socket info ----- */
#define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)

And below is baswap() function which is available in net/bluetooth/lib.c source code file.

void baswap(bdaddr_t *dst, bdaddr_t *src)
{
	unsigned char *d = (unsigned char *) dst;
	unsigned char *s = (unsigned char *) src;
	unsigned int i;

	for (i = 0; i < 6; i++)
		d[i] = s[5 - i];
}

The above means that a malicious user can set an invalid “socK” pointer which can point to malicious callback functions that can be exploited to result in privilege escalation. The patch was to ensure that before “sock” is used it is checked by l2cap_is_socket() function as you can see in the following patch. If the check fails it will return “EBADFD” (File descriptor in bad state).

diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 85bcc21..ce82722d 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -533,6 +533,9 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 
 	BT_DBG("");
 
+	if (!l2cap_is_socket(sock))
+		return -EBADFD;
+
 	baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
 	baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);

To better understand this check you can see l2cap_is_socket() routine as defined in net/bluetooth/l2cap_sock.c below. What it does is ensuring that “sock” is not NULL and that “sock->ops” callback functions point to the “l2cap_sock_ops” structure.

bool l2cap_is_socket(struct socket *sock)
{
	return sock && sock->ops == &l2cap_sock_ops;
}
EXPORT_SYMBOL(l2cap_is_socket);

The vulnerable code is reachable via BNEPCONNADD IOCTL command. You can see the exact code path that leads to bnep_add_connection() below. As you can see there is no check on the socket that is passed from the user-space to the kernel via copy_from_user() function.

static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
   ...
	switch (cmd) {
	case BNEPCONNADD:
   ...
		if (copy_from_user(&ca, argp, sizeof(ca)))
			return -EFAULT;

		nsock = sockfd_lookup(ca.sock, &err);
   ...
		ca.device[sizeof(ca.device)-1] = 0;

		err = bnep_add_connection(&ca, nsock);
   ...
}

Written by xorl

December 7, 2017 at 22:40

Posted in vulnerabilities

CVE-2017-3735: OpenSSL X.509 IPAddressFamily out-of-bounds read

leave a comment »

On 28 August 2017 the OpenSSL project released a security advisory security that affected all versions since 2006. The vulnerability was discovered by OSS-Fuzz fuzzer and it was fixed by Rich Salz of the OpenSSL development team. The vulnerable code was in crypto/x509v3/v3_addr.c in the function you see below.

/*
 * Extract the AFI from an IPAddressFamily.
 */
unsigned int X509v3_addr_get_afi(const IPAddressFamily *f)
{
    return ((f != NULL &&
             f->addressFamily != NULL && f->addressFamily->data != NULL)
            ? ((f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]))
            : 0);
}

As its name suggests, this routine is designed to extract the AFI (Address Family Identifier) from the IP “addressFamily” element from a X.509 certificate. You can see how this structure is defined in include/openssl/x509v3.h header file below.

typedef struct IPAddressFamily_st {
    ASN1_OCTET_STRING *addressFamily;
    IPAddressChoice *ipAddressChoice;
} IPAddressFamily;

If we check back the code from X509v3_addr_get_afi() we will see that when it’s not returning zero it does the following. Check if “f” is not NULL, check if “f->addressFamily” is not NULL, check if “f->addressFamily->data” is not NULL and then return the AFI based on the data in “f->addressFamily->data[0]” and “f->addressFamily->data[1]”. This seems to have been catching all of the cases but it actually misses one. What if the length of the ASN.1 octet is only one byte? In this case the access to “f->addressFamily->data[1]” will result in an out-of-bounds read by one byte. Below you can see the patch that adds another check which ensures that the “f->addressFamily->length” is not less than two.

@@ -84,10 +84,12 @@ static int length_from_afi(const unsigned afi)
  */
 unsigned int X509v3_addr_get_afi(const IPAddressFamily *f)
 {
-    return ((f != NULL &&
-             f->addressFamily != NULL && f->addressFamily->data != NULL)
-            ? ((f->addressFamily->data[0] << 8) | (f->addressFamily->data[1]))
-            : 0);
+    if (f == NULL
+            || f->addressFamily == NULL
+            || f->addressFamily->data == NULL
+            || f->addressFamily->length < 2)
+        return 0;
+    return (f->addressFamily->data[0] << 8) | f->addressFamily->data[1];
 }

This can be triggered via a X.509 certificate with IPAddressFamily extension having a malformed AFI (Address Family Identifier). The result of is most likely an erroneous certificate.

Written by xorl

December 4, 2017 at 22:50

Posted in vulnerabilities