domingo, 16 de agosto de 2009

Cómo funciona el exploit sock_sendpage()

 
Tavis Ormandy y Julien Tinnes, del equipo de seguridad de Google, han descubierto un bug en el kernel Linux que nadie ha sido capaz de ver en los últimos ocho años. Este bug, que afecta a todas las versiones del kernel desde la 2.4.4 a la 2.4.37.4 y desde la 2.6.0 a la 2.6.30.4 permite escalar privilegios en el sistema. En este artículo vamos a analizar su funcionamiento.


Primer error:
Todo comienza en la función sock_sendpage() situada en net/socket.c. El problema reside en la llamada a sendpage() pues no se ha verificado que el puntero esté inicializado.


static ssize_t sock_sendpage(struct file *file, struct page *page,
              int offset, size_t size, loff_t *ppos, int more)
{
   struct socket *sock;
   int flags;

   sock = file->private_data;

   flags = !(file->f_flags & O_NONBLOCK) ? 0 : MSG_DONTWAIT;
   if (more)
      flags |= MSG_MORE;

   return sock->ops->sendpage(sock, page, offset, size, flags);
}


A continuación, dejo una copia de la función sock_splice_read() que no tiene nada que ver ni con el bug ni con el exploit, pero nos sirve para ver como se podría haber evitado la vulnerabilidad.  En esta función se verifica el puntero a la función mediante unlikely() antes su llamada.


static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
                 struct pipe_inode_info *pipe, size_t len,
            unsigned int flags)
{
   struct socket *sock = file->private_data;

   if (unlikely(!sock->ops->splice_read))
      return -EINVAL;

   return sock->ops->splice_read(sock, ppos, pipe, len, flags);
}


Segundo error:
Hemos visto que no se verifica si el puntero a sendpage() esta correctamente inicializado. Sera necesario ahora, encontrar situaciones en las que el puntero a la función sendpage() no se haya inicialiado.

Tavis Ormandy y Julien Tinnes en su investigación localizaron deficiencias en la incialización del puntero para la macro SOCKOPS_WRAP, definida en include/linux/net.h, que incluye PF_APPLETALK, PF_IPX, PF_IRDA, PF_X25 y PF_AX25. Tambien encontraron deficiencias en otros protocolos  como  PF_BLUETOOTH, PF_IUCV, PF_INET6 (con IPPROTO_SCTP), PF_PPPOX y PF_ISDN.


Veamos por ejemplo el caso del protocolo bluetooth. En net/bluetooth/bnep/sock.c nos encontramos con la estructura bnet_sock_ops en la que vemos que no se inicializa sendpage con sock_no_sendpage(), por lo que quedará apuntando a NULL.

static const struct proto_ops bnep_sock_ops = { 
        .family         = PF_BLUETOOTH,
        .owner          = THIS_MODULE,
        .release        = bnep_sock_release,
        .ioctl          = bnep_sock_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl   = bnep_sock_compat_ioctl,
#endif
        .bind           = sock_no_bind,
        .getname        = sock_no_getname,
        .sendmsg        = sock_no_sendmsg,
        .recvmsg        = sock_no_recvmsg,
        .poll           = sock_no_poll,
        .listen         = sock_no_listen,
        .shutdown       = sock_no_shutdown,
        .setsockopt     = sock_no_setsockopt,
        .getsockopt     = sock_no_getsockopt,
        .connect        = sock_no_connect,
        .socketpair     = sock_no_socketpair,
        .accept         = sock_no_accept,
        .mmap           = sock_no_mmap
};



El caso de SOCKOPS_WRAP es mas sutil, pues en net/ipx/af_ipx.c todo parece correcto. Podemos ver la inicialización en la última linea.


static const struct proto_ops SOCKOPS_WRAPPED(ipx_dgram_ops) = {
        .family         = PF_IPX,
        .owner          = THIS_MODULE,
        .release        = ipx_release,
        .bind           = ipx_bind,
        .connect        = ipx_connect,
        .socketpair     = sock_no_socketpair,
        .accept         = sock_no_accept,
        .getname        = ipx_getname,
        .poll           = datagram_poll,
        .ioctl          = ipx_ioctl,
#ifdef CONFIG_COMPAT
        .compat_ioctl   = ipx_compat_ioctl,
#endif
        .listen         = sock_no_listen,
        .shutdown       = sock_no_shutdown, /* FIXME: support shutdown */
        .setsockopt     = ipx_setsockopt,
        .getsockopt     = ipx_getsockopt,
        .sendmsg        = ipx_sendmsg,
        .recvmsg        = ipx_recvmsg,
        .mmap           = sock_no_mmap,
        .sendpage       = sock_no_sendpage,
};


Sin embargo, si estudiamos con detenimiento la macro SOCKOPS_WRAP en include/linux/net.h vemos que mietras otras llamadas se inicializan correctamente, no se realiza la inicialización de sendpage.
No pego el código por que es demasiado largo, consultar en include/linux/net.h y ver que la última inicialización es la de mmap(), olvidando sendpage().



Cómo explotarlo?
Lo primero que necesitaremos para explotar esta vulnerabilidad es una función que podamos ejecutar en espacio de usuario que use sock_sendpage(). Hay muchas funciones que lo hacen, una de ellas es sendfile().



Tavis Ormandy y Julien Tinnes y proponen como secuencia válida de llamadas para forzar el uso de sendpage(), la siguiente:

/* ... */
int fdin = mkstemp(template);
int fdout = socket(PF_PPPOX, SOCK_DGRAM, 0);

unlink(template);

ftruncate(fdin, PAGE_SIZE);

sendfile(fdout, fdin, NULL, PAGE_SIZE);
/* ... */



Cuando se ejecute la función sendpage(), si esta no está inicializada y apunta a NULL, el kernel intentará ejecutar las instrucciones que hay en la dirección 0. Podemos aprovechar esto mapeando la dirección 0 y almacenando allí las instrucciones que queremos que ejecute el kernel. Así, cuando se intente ejecutar sendpage() realmente se ejecutará nuestro código en espacio de kernel. Podremos escalar privilegios haciendo que el código almecando en la dirección 0 ponga nuestros uid a cero (root).

Este tipo de bugs son muy comunes en el kernel Linux y se conocen como bugs por 'NULL pointer dereference'. Ya vimos aquí uno de estos con el exploit vmsplice.


En las referencias dejo un ejemplo de exploit.




referencias:
- http://www.securityfocus.com/archive/1/505751
- http://blog.cr0.org/2009/08/linux-null-pointer-dereference-due-to.html
- exploit: http://www.frasunek.com/proto_ops.tgz

 

miércoles, 5 de agosto de 2009

Criptograma Principiantes 05/08/09


RJ BZR KWPSJHLMHZR CJMJLRLG LAJVJMJZR AL BK NJRNK VZWNK LG PSL BZR UWZOWKNKAZWLR LRMWJTLG UWZOWKNKR, LB UWJNLW UKQKWZ MKWUJGHLWZ PSL UKRKRL UZW KPSJ ALRHWSJWJK BK MJXJBJFKMJZG.

OLWKBA ILJNTLWO