xorl %eax, %eax

FreeBSD-SA-08:13: NetGraph/Bluetooth Privilege Escalation

with one comment

This is an old vulnerability but since no significant kernel level vulnerabilities have been disclosed lately (that’s great btw) I’m going to talk about this one. It was found by Christer Oberg and announced on 23 December 2008. It affects FreeBSD 6.3 up to 6.4 and 7.0 up to 7.1-PRERELEASE. Here I’ll use FreeBSD 7.0 to describe the bug. A quick look in kern/uipc_domain.c reveals this:

78 /*
79  * Dummy protocol specific user requests function pointer array.
80  * All functions return EOPNOTSUPP.
81  */
82 struct pr_usrreqs nousrreqs = {
83         .pru_accept =           pru_accept_notsupp,
84         .pru_attach =           pru_attach_notsupp,
85         .pru_bind =             pru_bind_notsupp,
86         .pru_connect =          pru_connect_notsupp,
87         .pru_connect2 =         pru_connect2_notsupp,
88         .pru_control =          pru_control_notsupp,
89         .pru_disconnect =       pru_disconnect_notsupp,
90         .pru_listen =           pru_listen_notsupp,
91         .pru_peeraddr =         pru_peeraddr_notsupp,
92         .pru_rcvd =             pru_rcvd_notsupp,
93         .pru_rcvoob =           pru_rcvoob_notsupp,
94         .pru_send =             pru_send_notsupp,
95         .pru_sense =            pru_sense_null,
96         .pru_shutdown =         pru_shutdown_notsupp,
97         .pru_sockaddr =         pru_sockaddr_notsupp,
98         .pru_sosend =           pru_sosend_notsupp,
99         .pru_soreceive =        pru_soreceive_notsupp,
100         .pru_sopoll =          pru_sopoll_notsupp,
101 }; 


This is a pretty self explanatory array of pointers for different routines. Next, there is an initialization function which using a simple macro initializes some of the above function pointers if they’re set to NULL. This is done like this:

103 static void
104 protosw_init(struct protosw *pr)
105 {
106         struct pr_usrreqs *pu;
107 
108         pu = pr->pr_usrreqs;
109         KASSERT(pu != NULL, ("protosw_init: %ssw[%d] has no usrreqs!",
110             pr->pr_domain->dom_name,
111             (int)(pr - pr->pr_domain->dom_protosw)));
112 
113 #define DEFAULT(foo, bar)       if ((foo) == NULL)  (foo) = (bar)
114         DEFAULT(pu->pru_accept, pru_accept_notsupp);
115         DEFAULT(pu->pru_connect, pru_connect_notsupp);
116         DEFAULT(pu->pru_connect2, pru_connect2_notsupp);
117         DEFAULT(pu->pru_control, pru_control_notsupp);
118         DEFAULT(pu->pru_listen, pru_listen_notsupp);
119         DEFAULT(pu->pru_rcvd, pru_rcvd_notsupp);
120         DEFAULT(pu->pru_rcvoob, pru_rcvoob_notsupp);
121         DEFAULT(pu->pru_sense, pru_sense_null);
122         DEFAULT(pu->pru_sosend, sosend_generic);
123         DEFAULT(pu->pru_soreceive, soreceive_generic);
124         DEFAULT(pu->pru_sopoll, sopoll_generic);
125 #undef DEFAULT
126         if (pr->pr_init)
127                 (*pr->pr_init)();
128 }


Ok, some of you who are familiar with abstract networking shit of the FreeBSD kernel would notice that there are a few function pointers that do not get initialized here. This is the vulnerability. protosw_init() is called on two situations.. First, when we want to add a new protocol domain to the list of the already supported ones like this:

135 static void
136 net_init_domain(struct domain *dp)
137 {
138         struct protosw *pr;
      ...
142         for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
143                 protosw_init(pr);
      ...
151 }

The second second case where the vulnerable function is being called is when a protocol gets initialized to work on an existing connection. Here is the code:

279 /*
280  * The caller must make sure that the new protocol is fully set up and ready to
281  * accept requests before it is registered.
282  */
283 int
284 pf_proto_register(int family, struct protosw *npr)
285 {
286         struct domain *dp;
287         struct protosw *pr, *fpr;
      ...
299         /* Try to find the specified domain based on the family. */
300         for (dp = domains; dp; dp = dp->dom_next)
301                 if (dp->dom_family == family)
302                         goto found;
      ...
305 found:
      ...
334         /* Copy the new struct protosw over the spacer. */
335         bcopy(npr, fpr, sizeof(*fpr));
      ...
340         /* Initialize and activate the protocol. */
341         protosw_init(fpr);
342 
343         return (0);
344 }

Using this function indirectly, a malicious user can setup a socket that its function pointers are not being correctly initialized and thus have invalid pointer access on some specific functions. The patch reveals which were the function pointers that remain uninitialized on bluetooth and netgraph sockets:

 #define DEFAULT(foo, bar)    if ((foo) == NULL)  (foo) = (bar)
     DEFAULT(pu->pru_accept, pru_accept_notsupp);
+    DEFAULT(pu->pru_bind, pru_bind_notsupp);
     DEFAULT(pu->pru_connect, pru_connect_notsupp);
     DEFAULT(pu->pru_connect2, pru_connect2_notsupp);
     DEFAULT(pu->pru_control, pru_control_notsupp);
+    DEFAULT(pu->pru_disconnect, pru_disconnect_notsupp);
     DEFAULT(pu->pru_listen, pru_listen_notsupp);
+    DEFAULT(pu->pru_peeraddr, pru_peeraddr_notsupp);
     DEFAULT(pu->pru_rcvd, pru_rcvd_notsupp);
     DEFAULT(pu->pru_rcvoob, pru_rcvoob_notsupp);
     DEFAULT(pu->pru_sense, pru_sense_null);
+    DEFAULT(pu->pru_shutdown, pru_shutdown_notsupp);
+    DEFAULT(pu->pru_sockaddr, pru_sockaddr_notsupp);
     DEFAULT(pu->pru_sosend, sosend_generic);
     DEFAULT(pu->pru_soreceive, soreceive_generic);
     DEFAULT(pu->pru_sopoll, sopoll_generic);


I haven’t exploited this but according to the patch and the above code, functions including bind(), disconnect(), peeraddr(), shutdown() and sockaddr() may not be initialized on bluetooth and netgraph sockets and thus allow you to perform various attacks. Don “north” Bailey published an exploit for this issue. Basically, he just maps the malicious data and then creates a netgraph socket (PF_NETGRAPH) and finally using the uninitialized shutdown() function triggers the bug to overwrite the credential structure with his data. It requires the address of allproc in order to perform the overwrite in the kernel.

Written by xorl

March 16, 2009 at 09:54

Posted in bugs, freebsd

One Response

Subscribe to comments with RSS.

  1. CVE-2008-5736

    Jericho

    April 3, 2009 at 09:24


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