Posts | Archive

Linux Kernel getname() Stack Memory Disclosures

In this post, we'll look at some kernel stack information disclosures in the getname() functions of several socket AFs recently discovered in the Linux kernel.

The Vulnerability

The getname() function of an address family in the kernel is used to retrieve information about a given socket. This information, in the form of a sockaddr struct, is accessed from userspace through the getsockname(2) and getpeername(2) system calls for bound and connected sockets respectively.

The operation of a typical getname() function is as follows: a sockaddr struct on the stack is filled in with addressing information from internal socket structures and then is memcpy()'ed into the destination sockaddr which is later copied back to userspace. For example, the following is the getname() for the irda address family in net/irda/af_irda.c:

static int irda_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { struct sockaddr_irda saddr; struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); if (peer) { if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN; saddr.sir_family = AF_IRDA; saddr.sir_lsap_sel = self->dtsap_sel; saddr.sir_addr = self->daddr; } else { saddr.sir_family = AF_IRDA; saddr.sir_lsap_sel = self->stsap_sel; saddr.sir_addr = self->saddr; } IRDA_DEBUG(1, "%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel); IRDA_DEBUG(1, "%s(), addr = %08x\n", __func__, saddr.sir_addr); /* uaddr_len come to us uninitialised */ *uaddr_len = sizeof (struct sockaddr_irda); memcpy(uaddr, &saddr, *uaddr_len); return 0; }

As we see here, irda_getname() is filling in several of the members of saddr, which is a sockaddr_irda structure. We can take a look at the definition of a sockaddr_irda structure in include/linux/irda.h:

struct sockaddr_irda { sa_family_t sir_family; /* AF_IRDA */ __u8 sir_lsap_sel; /* LSAP selector */ __u32 sir_addr; /* Device address */ char sir_name[25]; /* Usually :IrDA:TinyTP */ };

The total size of this structure is 36 bytes, including a large 25-byte sir_name member. Notice that the sir_name member is not memset()'ed or initialized in the above irda_getname() function. This means that our final memcpy() will be copying uninitialized data from the stack which will then be returned to userspace, leaking potentially sensitive information. In addition to the 25-byte sir_name, there is also one byte of padding inserted by the compiler for alignment purposes between sir_lsap_sel and sir_addr and 3 bytes of padding after sir_name. This results in a total of 29 bytes of uninitialized kernel stack memory being leaked to userspace.

As it turns out, this issue was not limited to only irda, but also the can, appletalk, rose, netrom, econet, and llc address families. These vulnerabilities affect versions of the Linux 2.6 kernel before 2.6.31-rc7. While these socket families aren't the most common, they ship as modules in common distributions that are loaded automatically via request_module("net-pf-X") when a socket is created. Yet another reason why you should trim unused and potentially vulnerable code from your kernel configuration or use something like grsecurity's MODHARDEN to reduce your attack surface.

The Exploit

Exploiting this memory disclosure vulnerability is straightforward and can be performed by an unprivileged user. For irda, we just need to create an AF_IRDA socket and then call getsockname(2) on it. Exploiting some of the other vulnerable address families may require binding or connecting the socket in order to satisfy certain conditions in the getname() function. For irda, the required code is trivial:

struct sockaddr_irda saddr; int sock, len = sizeof(saddr); sock = socket(AF_IRDA, SOCK_DGRAM, 0); getsockname(sock, (struct sockaddr *) &saddr, &len);

The saddr structure will now contain 29-bytes of uninitialized kernel stack memory (byte 4 and bytes 9-36).

A full example exploit for this issue is available here:

The Fix

The first set of fixes rolled into davem's net-2.6 git repo via Eric Dumazet (and somehow slipped under my radar) in early August. The patches covered a number of address families including can, irda, appletalk, rose, netrom, and econet, the worst of the bunch being the irda example we covered here. Another similar patch for the llc address family rolled in on August 24th which is what alerted me to the previous issues.

The fix is obvious: memset() the sockaddr structure at the beginning of the getname() function so that uninitialized memory will be not be leaked to userspace. For completeness, here's the patch for the irda example:

--- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -714,6 +714,7 @@ static int irda_getname(struct socket *sock, struct sockaddr *uaddr, struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk); + memset(&saddr, 0, sizeof(saddr)); if (peer) { if (sk->sk_state != TCP_ESTABLISHED) return -ENOTCONN;


Copyright © 2018 - Jon Oberheide