martes, 1 de marzo de 2011

Analyzing the Linux Kernel CAP_SYS_ADMIN Phonet Root exploit

A vulnerability in the phonet driver was pointed out by Dan Rosenberg leading to root access if it is well exploited. The concerned source code can be found at net/phonet/af_phonet.c. When we call to socket function and we pass a negative value as second argument, an array index overflow is triggered.
Taking a look at af_phonet.c, i found the piece of vulnerable code. When a call to socket is made, pn_socket_create() is called afterwards.

af_phonet.c:63
static int pn_socket_create(struct net *net, struct socket *sock, int protocol,
int kern)

As you can see, the third argument's type is a signed integer. Later in this function we have

af_phonet.c:88
pnp = phonet_proto_get(protocol);


And looking for phonet_proto_get definition

static struct phonet_protocol *phonet_proto_get(int protocol)
{
struct phonet_protocol *pp;

if (protocol >= PHONET_NPROTO)
return NULL;
[...]

pp = rcu_dereference(proto_tab[protocol]);
[...]

return pp;
}

Finally, we have an array access using protocol as its index. The if verification can be bypassed by writing in protocol a negative value (PHONET_NPROTO=3) and then proto_tab is indexed with this value triggering the vulnerability.

How to exploit it?
Exploit-DB: Linux Kernel CAP_SYS_ADMIN to Root Exploit 2 (32 and 64-bit)

It is cleaner than Rosenberg's exploit. The key idea is to pass a negative value to socket() to fake driver's structures when proto_tab[protocol] is accessed.

I will tell you a secret and do not say it to anyone else...
SYM_ADDRESS 0x4e4f4850 is an ascii coded address, so it is translated as "NOHP" which is "PHON" in little endian.

Joe Sylve, in his exploit, try to find this value somewhere in the pn_proto structure. In fact it is the driver's name and usually it is hardcoded inside pn_proto.

(net/phonet/datagram.c:185)
static struct proto pn_proto = {

.close = pn_sock_close,
[...]
.name = "PHONET", // Our favourite field!
};

But how to reach that value?

pn_proto + SYM_OFFSET

Because we can control proto_tab[] accesses by passing a negative index so we are able to reference lower addresses than proto_tab's one and fortunately pn_proto is located before proto_tab in the address space. By calculating

offset = proto_tab - (pn_proto + SYM_OFFSET)

and making index equals to -offset, we force the driver to look for phonet_protocol structure at SYM_ADDRESS. He builds two structures to fool the driver, pn_dgram_protocol (net/phonet/datagram.c:200) and phonet_dgram_ops (net/phonet/socket.c:461). phonet_dgram_ops provides an ioctl reference for ioctl() operations but because the exploit has taken control of this structure, ioctl operations are now redirected to get_root() :-)

const struct proto_ops_skel fake_proto_ops2 = {
.family = AF_PHONET,
.ioctl = &getroot,
};

Have a nice root-id!