xorl %eax, %eax

OpenBSD getsockopt(2) NULL Pointer Dereference

with 8 comments

Recently, a new reliability fix was posted in OpenBSD’s errata page. The bug is located in ip_ctloutput() routine which can be found at sys/netinet/ip_output.c. Here is a snippet of the susceptible code.

/*
 * IP socket option processing.
 */
int
ip_ctloutput(op, so, level, optname, mp)
	int op;
	struct socket *so;
	int level, optname;
	struct mbuf **mp;
{
	struct inpcb *inp = sotoinpcb(so);
	struct mbuf *m = *mp;
	int optval = 0;
     ...
	if (level != IPPROTO_IP) {
     ...
	} else switch (op) {
     ...
		case IP_AUTH_LEVEL:
		case IP_ESP_TRANS_LEVEL:
		case IP_ESP_NETWORK_LEVEL:
		case IP_IPCOMP_LEVEL:
    ...
			optval = *mtod(m, int *);
    ...
	return (error);
}

So, in case of a getsockopt(2) call using a “IPPROTO_IP” protocol and some option like IP_AUTH_LEVEL, IP_ESP_TRANS_LEVEL, IP_ESP_NETWORK_LEVEL or IP_IPCOMP_LEVEL you can reach this code. By doing so, pointer ‘m’ could be NULL and the call to mtod() will attempt to retrieve:

(int *) NULL->m_data

As we can read from sys/mbuf.h:

/*
 * Macros for type conversion
 * mtod(m,t) -  convert mbuf pointer to data pointer of correct type
 */
#define mtod(m,t)       ((t)((m)->m_data))

The subsequent uses of ‘optval’ can lead to exploitable conditions. This was fixed by applying the following patch:

 		case IP_ESP_NETWORK_LEVEL:
 		case IP_IPCOMP_LEVEL:
+			*mp = m = m_get(M_WAIT, MT_SOOPTS);
 #ifndef IPSEC
 			m->m_len = sizeof(int);

If you are not aware of it, m_get() is a C macro which can be found at kern/uipc_mbuf.c and it is being used to return an item from the ‘mbuf’ pool using pool_get(). This way, pointer ‘m’ as well as ‘mp’ are initialized correctly.
This bug was discovered by Clément LECIGNE, who also mentions at his blog that this is not exploitable on releases after 4.3 since the following “protection” against NULL page mappings was added:

 /* user/kernel map constants */
-#define VM_MIN_ADDRESS		((vaddr_t)0)
+#define VM_MIN_ADDRESS		((vaddr_t)PAGE_SIZE)
 #define VM_MAXUSER_ADDRESS	((vaddr_t)((PDSLOT_PTE<<PDSHIFT) - USPACE))

Since 4.4 the minimum virtual memory address allowed to be mapped is that of PAGE_SIZE constant which is 4096 or more. According to the OpenBSD people, this is exploitable only by root user (since it requires a raw socket) and this is why it isn’t considered a security related bug. (Once again, I disagree.)

Written by xorl

October 31, 2009 at 00:44

Posted in bugs, openbsd

8 Responses

Subscribe to comments with RSS.

  1. Hi xorl,

    There is a mistake in your analysis.

    Your first code snippet is kernel code which handles the setsockopt() call whereas the vulnerability is located in the code which handles the getsockopt() call, it is bellow the setsockopt() one. Now I know why people tell me that it requires root.

    This is more interesting because we do not need to be root to call it and we have a :

    *mtod(m, int *) = optval;

    Yes yes, we can write an optval where we want. :)

    On monday, I will try to publish my exploit with an analysis of this vulnerability on my blog.

    BTW, big congrats for your excellent and huge work !

    clem1

    October 31, 2009 at 16:59

  2. Thanks for the correction clem1.
    Also, I believe that that NULL mapping protection is just silly… I mean, it breaks almost every single application that is making use of VM86.
    Anyway, I’m looking forward for your posts :)

    xorl

    November 1, 2009 at 01:38

  3. If it was at least configurable in run-time, not by changing the define, recompiling the kernel and rebooting.

    Well I also call it a serious security vulnerability and that it is not exploitable is pure luck for them, however it doesn’t negate the status of the issue.

    fiction

    November 2, 2009 at 19:46

  4. Dear OpenBSD developers, it seems that your reliability fix leads to instant root access :P

    http://vulndev.blogspot.com/2009/11/openbsd-ownage-party.html

    Credits to Clément LECIGNE for making you look so funny. :P

    xorl

    November 3, 2009 at 22:14

  5. You seem to misunderstand the mitigation that was put in place in 2008. This issue is not exploitable by anyone, not even root, on 4.4+ kernels since mapping NULL is no longer possible. You can no longer elevate privileges, the worst damage you can do when such a bug is discovered is crash the kernel.

    On 4.3 and earlier kernels, you can gain root with this exploit. From 4.4 on, the kernel crashes. Hence, it’s a reliability fix, not a security fix.

    Paul

    November 5, 2009 at 09:05

  6. Dear Paul I understand basic VM management code and I insist. You should probably rephrase to something like: “The VM_MIN_ADDRESS patch, which BTW breaks almost every single VM86 application was merged with the kernel in order to provide security against NULL VM address mappings”. That was its aim, it doesn’t mean that there is no way to overcome this.

    It’s just an extremely basic protection against NULL mappings and I have to admit that (at last) even Linux guys have a better one. Of course, none of them could be compared to grsecutity’s UDEREF or the segmentation model of early Linux kernels that is no longer used.

    To conclude, yea… this is not exploitable on some platforms ;P

    xorl

    November 5, 2009 at 10:45

  7. Well, your statement that you need root to exploit this is not true (according to the guy that found the issue), and also according to him, it’s not exploitable anymore since 4.4. On any platform (running OpenBSD). You can panic the machine, which is annoying in and of itself, but is not a security issue.

    Paul

    November 6, 2009 at 14:01

  8. Paul, this is getting boring my comment about root access was already corrected in the first comment by the author of the vulnerability.
    He says that it’s not exploitable on OpenBSD =>4.4 because he wasn’t able to bypass the VM_MIN_ADDRESS protection, not because it’s not vulnerable.

    xorl

    November 7, 2009 at 01:09


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