Explotando sock_sendpage()

Iniciado por hielasangre, Agosto 27, 2011, 08:38:28 PM

Tema anterior - Siguiente tema

0 Miembros y 1 Visitante están viendo este tema.

Este fallo data de 8 años y nadie se había dado cuenta...
La vulnerabilidad aquí citada aprovecha fallos en los kernels que van desde la versión
2.4.4 hasta 2.4.37.4 y desde 2.6.0 a la 2.6.30.4 permitiendo elevar privilegios de usuario
normal a root...
El error que analizaremos en esta ocasión esta en la funcion sock_sendpage() que se encuentra
en net/socket.c y el problema particularmente se encuentra en la llamada a sendpage() que no
verifica si el puntero se ha inicializado.

Código: php

    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); 
   }


Código: php

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);
}




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 (De el equipo de seguridad informatica de Google) 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.
Prestemos atencion al protocolo Bluetooth, ubicado en net/bluetooth/bnep/sock.c donde encontraremos la estructura bnet_sock_ops en la que no se inicializa sendpage con sock_no_sendpage(), en ese caso quedara apuntando a NULL.

Código: php

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
};




Veamos la macro SOCOPS_WRAP, casi no se nota el error pero prestemos atencion a la ultima linea.

Código: php
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,
};


Bueno ahora que conocemos donde esta el error, veamos como aprovecharlo...
Para tal caso debemos contar con una funcion que podamos ejecutar en espacio de usuario
que use la funcion sock_sendpage(), existen muchas...la propuesta en este caso es sendfile().
Forzaremos el uso de sendpage() con la siguente secuencia:

Código: php
/* ... */
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 funcion sendpage(), si no esta inicializada y apuntando a NULL,
el kernel tratara de ejecutar las instrucciones que hay en la direccion 0, mapeando dicha
direccion y almacenando alli lo que queremos que ejecute el kernel podemos sacar provecho
de dicha vulnerabilidad.
De este modo cuando dicho kernel intente ejecutar sendpage() en realidad ejecutara nuestro codigo
en el espacio de kernel, asi podremos escalar privilegios, haciendo que el codigo almacenado en la
direccion 0 ponga nuestro uid a 0 (uid=0 privilegios de root ^^)

Espero que se haya entendido y ahora se comprenda a fondo de como se aprovecha este maravilloso fallo.



resulta que aun sigue algo de este problema en los kernel actuales verdad?? y es facil inyectar un fork con dicha vulneribilidad?? apuntando que es puntero ?? perdon si el tema es de hace años atras