elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.
 
Inicio Ayuda Buscar Ingresar Registrarse
25 Mayo 2012, 09:27  


Tema destacado: Entra al canal IRC oficial de #elhacker.net

+  Foro de elhacker.net
|-+  Seguridad Informática
| |-+  Bugs y Exploits (Moderador: berz3k)
| | |-+  CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] 2 Ir Abajo Respuesta Imprimir
Autor Tema: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'  (Leído 3,447 veces)
Ivanchuk


Desconectado Desconectado

Mensajes: 466


LLVM


Ver Perfil WWW
CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« en: 20 Febrero 2010, 02:06 »

Buscando algo para entretenerme encontre un bug de escalada de privilegios en el kernel de linux.

http://www.securityfocus.com/bid/37036/info

Ahi dice que hay un error en el driver hfc_usb.c y que se puede ejecutar codigo por medio de un buffer overflow.
Me baje el codigo del kernel para ver el commit y averigüar mas sobre este error, con la posible meta de hacer algun
exploit.
Bueno, en el log simplemente dicen que
Citar
isdn: hfc_usb: Fix read buffer overflow
 
 Check whether index is within bounds before testing the element.


Esta es la diff del archivo buggy contra el parcheado.

Código:
ivanchuk@laptop:~/kernel/linux-2.6.31.y/drivers/isdn/hisax$ git diff 405f55712dfe464b3240d7816cc4fe4174831be2 hfc_usb.c
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
index 9de5420..a420b64 100644
--- a/drivers/isdn/hisax/hfc_usb.c
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -817,8 +817,8 @@ collect_rx_frame(usb_fifo * fifo, __u8 * data, int len, int finish)
 }
 /* we have a complete hdlc packet */
 if (finish) {
- if ((!fifo->skbuff->data[fifo->skbuff->len - 1])
- && (fifo->skbuff->len > 3)) {
+ if (fifo->skbuff->len > 3 &&
+ !fifo->skbuff->data[fifo->skbuff->len - 1]) {
 
 if (fifon == HFCUSB_D_RX) {
 DBG(HFCUSB_DBG_DCHANNEL,

Dio vuelta las comparaciones en el if nada mas. El problema es que no llego a ver que consecuencias puede llegar a tener eso. Alguien se da una idea?.
« Última modificación: 20 Febrero 2010, 14:22 por Anon » En línea

Sólo quien practica lo absurdo puede lograr lo imposible.

Join us @ http://foro.h-sec.org
sirdarckcat
Troll Buena Onda y
CoAdmin
***
Desconectado Desconectado

Mensajes: 6.947


Lavando Platos


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #1 en: 20 Febrero 2010, 06:03 »

despues de platicarlo con varios amigos llegamos a la conclusion que la cagaron en security focus, y que el del changelog se equivoco al poner overflow.. por lo que solo es un kernel panic, nada mas.


Saludos!!

En línea

AlbertoBSD
Estudiante y
Colaborador
***
Desconectado Desconectado

Mensajes: 1.955


Anonymous & Paranoid


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #2 en: 20 Febrero 2010, 06:36 »

Concuerdo con sdc.

El diff aqui esta mas bonito y con colores http://bit.ly/aiI9uI

Pero para dejar un poco mas claro esto:

Código
!fifo->skbuff->data[fifo->skbuff->len - 1]

Solo se va a cumplir si el elemento marcado vale 0 :P

Código
fifo->skbuff->len > 3

Solo se cumple si el len es mayor que tres.

No se ni que haga la función y ni por que solo mayor que 3 funciona, pero como dice en el comit, al parecer alguien se dio cuenta que es mas optimo primero validar si la longitud del buffer es mayor a 3 y después el contenido del mismo

Puede darse el caso de que la región de memoria que este usando este limpia y en la mayoría de las ocasiones si pase la primera comparación pero no la segunda, eso seria una comprobación innecesaria, por ese lado yo lo veo como optimización de código.
En cualquiera de que las 2 opciones falle de cumple el else que:

Código
 840                 } else {
841                         DBG(HFCUSB_DBG_FIFO_ERR,
842                             "HFC-S USB: ERROR frame len(%d) fifo(%d)",
843                             fifo->skbuff->len, fifon);
844                         DBG_SKB(HFCUSB_DBG_VERBOSE_USB, fifo->skbuff);
845                         skb_trim(fifo->skbuff, 0);
846                 }

Eso es un Kernel Panic ?!?! xD no lo sabia

Saludos



Edit

OMG tiene un CVE por solo el cambio de orden de las comparaciones wow: CVE-2009-4005

LOL




« Última modificación: 20 Febrero 2010, 06:44 por Anon » En línea

Bien Super Divertido
@wifigdlmx
sirdarckcat
Troll Buena Onda y
CoAdmin
***
Desconectado Desconectado

Mensajes: 6.947


Lavando Platos


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #3 en: 20 Febrero 2010, 07:03 »

es kernel panic porque si len vale 0, vas a tratar de leer memoria invalida y como corres como ring0, el codigo se panic'ea.


la vulnerabilidad es un DoS a modo de kernel panic, no es una optimisacion..




Saludos!!

En línea

AlbertoBSD
Estudiante y
Colaborador
***
Desconectado Desconectado

Mensajes: 1.955


Anonymous & Paranoid


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #4 en: 20 Febrero 2010, 07:21 »

A si es cierto xD, no habia visto el caso donde len es 0


Saludos

En línea

Bien Super Divertido
@wifigdlmx
nitr0us

Desconectado Desconectado

Mensajes: 204


#rm -fr /


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #5 en: 20 Febrero 2010, 08:20 »

Listo!!  :D, luego de un par de horas..

Citar
"12:30 - I Restate my assumptions: C is the language of Linux kernel" ... PI: THE CHAOS ORDER
jajaj

Les comento mis "assumptions":

Ok, según mi paranoia y yo, si existe un buffer overflow pero no en el if() sino que antes!!. Veamos:

Código:
/* collect rx data from INT- and ISO-URBs  */
781 static void
782 collect_rx_frame(usb_fifo * fifo, __u8 * data, int len, int finish)
El parámetro len es signed int , eso significa que puede tener valores negativos.

Código:
792         if (!fifo->skbuff) {
793                 fifo->skbuff = dev_alloc_skb(fifo->max_size + 3);
Si no se ha reservado memoria, reserva el máximo permitido (max_size) + 3.

Ok, ahora, analizando la estructura de datos de fifo (usb_fifo):
Código:
typedef struct usb_fifo {
int fifonum; /* fifo index attached to this structure */
int active; /* fifo is currently active */
struct hfcusb_data *hfc; /* pointer to main structure */
int pipe; /* address of endpoint */
__u8 usb_packet_maxlen; /* maximum length for usb transfer */
unsigned int max_size; /* maximum size of receive/send packet */
Es unsigned int, es decir, el último bit de la izquierda NO está reservado para el signo, lo que se traduce a que SOLO acepta números positivos.

Sigamos...
Código:
801         if (len) {
802                 if (fifo->skbuff->len + len < fifo->max_size) {
803                         memcpy(skb_put(fifo->skbuff, len), data, len);
Ahora, según yo, aquí es donde está el Integer overflow para saltar los 2 if()'s y posteriormente llegar al código vulnerable (803). ¿Por que?
Código:
$cat > if_signed.c
#include<stdio.h>

int main()
{
int len = -1;

if(len)
printf("nitr0us\n");
else
printf("=/\n");

return 0;
}
$gcc if_signed.c
$./a.out
nitr0us
Si al if le pasas un int -1, este a unsigned int pasa a ser UINT_MAX, que es el máximo valor POSITIVO de un entero sin signo, logrando entrar así al if(). Entonces, como vimos en la declaración de collect_rx_frame(), len es un int, y si esta variable es controlada por el usuario, que en este caso sería un paquete HDLC malformado con "len" en -1, es posible saltar ese primer if().

Ahora,
Código:
802                 if (fifo->skbuff->len + len < fifo->max_size) {
Vamos a la estructura de datos de fifo->skbuff para ver que tipo de datos es el miembro len. Esto está declarado en skbuff.h, donde vemos:
Código:
struct sk_buff {
         /// These two members must be first.
         struct sk_buff          *next;
         struct sk_buff          *prev;

         struct sock             *sk;
         struct skb_timeval      tstamp;
....
         char                    cb[48];
 
         unsigned int            len,
                                      data_len,
len es unsigned int (solo valores POSITIVOS).

Entonces, vamos a poner números para ejemplificar esto y que mejor que con código. He programado esto para explicar con valores reales, además, puse al inicio un ejemplo de cambio de signo al sobreescribir el bit de signo (el 1er bit a la izquierda en signed int) con el máximo valor que un int puede tener (INT_MAX).. Espero se entienda el por que es posible saltarse los 2 if()'s hasta llegar a memcpy() donde hasta ahora, creo que ocurre el overflow !!:
Código:
#include<stdio.h>
#include<limits.h>

int main(){
int len_param = -1;
unsigned int len_fifo = 1337;
unsigned int max_size = 0xffff;

printf("Valor de INT_MAX: %d (0x%x)\n", INT_MAX, INT_MAX);
printf("Valor de INT_MAX+1: %d (0x%x)\n", INT_MAX + 1, INT_MAX + 1);
printf("Valor de INT_MAX+2: %d (0x%x)\n\n", INT_MAX + 2, INT_MAX + 2);


printf("Esto es lo que el primer if() ve: 0x%x\n", (unsigned int) len_param);
if(len_param)

printf("Esto es lo que el segundo if() ve: ((0x%x + 0x%x) < 0x%x)\n", (unsigned int) len_fifo, (unsigned int) len_param, max_size);
printf("Esto es lo que el segundo if() ve: (0x%x < 0x%x)\n\n", (unsigned int) len_fifo + (unsigned int) len_param, max_size);
if((len_fifo + len_param) < max_size)
printf("memcpy(skb_put()) ... OVERFLOW !\n");
}
Ejecutenlo y obtendrán algo como:
Código:
./a.out
Valor de INT_MAX: 2147483647 (0x7fffffff)
Valor de INT_MAX+1: -2147483648 (0x80000000)
Valor de INT_MAX+2: -2147483647 (0x80000001)

Esto es lo que el primer if() ve: 0xffffffff
Esto es lo que el segundo if() ve: ((0x539 + 0xffffffff) < 0xffff)
Esto es lo que el segundo if() ve: (0x538 < 0xffff)

memcpy(skb_put()) ... OVERFLOW !

Ahora, supongamos que llegamos a memcpy(). Según su definición:
Código:
void * memcpy ( void * destination, const void * source, size_t num );
Este recibe como primer parámetro la dirección de memoria destino, como segundo parámetro la dirección de memoria de los datos a copiar y como 3er parámetro el número de bytes a copiar. Entonces, en el código de kernel tenemos que como dirección de memoria destino es la que devuelve la función skb_put(fifo->skbuff, len).

Bueno ya con eso estuvo ... según yo, el overflow está en la línea 803
Código:
803                         memcpy(skb_put(fifo->skbuff, len), data, len);


La misma persona que publicó la vulnerabilidad, fue la misma que hizo el patch (Roel Kluin).
Ya le envié un correo preguntándole si se hizo wey con el patch para despistar al enemigo ::) o el por qué de dicho cambio en el if() jaja  >:D :xD.

Citar
Eso es un Kernel Panic ?!?! xD no lo sabia

No, solo es información de debug. Cuando el kernel hace panic intencionalmente, llama a una función con el mismo nombre [panic()] si mal no recuerdo, por lo regular, después de un printk() (es como el printf() jeje pero en kernel-side). Por otro lado, cuando el kernel hace panic NO-intencionalmente, es por que intenta acceder a una dirección de memoria no válida y simplemente muere !.
Un ejemplo de esto, pero en user-land es http://www.brainoverflow.org/exploits_pocs/ELFsh_crash.esh

Saludos !!
En línea

Ivanchuk


Desconectado Desconectado

Mensajes: 466


LLVM


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #6 en: 20 Febrero 2010, 12:36 »

Gracias a todos por responder.
@sdc
No tengo mucha idea de cuando hay kernel panic, pero estando en ring0 no hay proteccion de nada no?. En espacio kernel no se deberia llamar a kernel_panic() explicitamente como dice nitrous?.

Muy buen análisis nitrous, lo lei de punta a punta :).
Pero creo que se deberia chequear si es que len puede ser negativo o no.
La funcion collect_rx_frame() se llama mas arriba desde rx_iso_complete(),
Código:
/drivers/isdn/hisax/hfc_usb.c:680

static void
rx_iso_complete(struct urb *urb)

...

len = urb->iso_frame_desc[k].actual_length;

...

if (len > 2)
collect_rx_frame(fifo, buf + 2,
len - 2,
(len < maxlen) ?
eof[fifon] : 0);
} else {
collect_rx_frame(fifo, buf, len,
(len < maxlen) ? eof[fifon] : 0);
}

Ahi tenes el chequeo de len para la primera llamada, o sea que no puede ser negativo. En el otro caso habria que seguirlo un poco mas. En la otra opcion del if se puede llegar a alcanzar el estado que decis, visto que len es un int y actual_length es unsigned.

De todas maneras (no estoy 100% seguro) creo que length es de un byte porque segun el controlador cada fifo puede contener hasta 128 bytes como maximo. O sea que lo unico posible es que length valga 0 como dijo sdc.

Edit:
Se me paso otra funcion que tambien llama a collect_rx_frame()  :¬¬, es rx_int_complete(), a verificarla tambien.
« Última modificación: 20 Febrero 2010, 13:35 por Ivanchuk » En línea

Sólo quien practica lo absurdo puede lograr lo imposible.

Join us @ http://foro.h-sec.org
AlbertoBSD
Estudiante y
Colaborador
***
Desconectado Desconectado

Mensajes: 1.955


Anonymous & Paranoid


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #7 en: 20 Febrero 2010, 14:20 »

Ivanchuk, hay un caso en el que si se manda llamar a la función collect_rx_frame, como dijo nitr0us

Código
 735                         if (fifo->last_urblen != maxlen) { 736                                 /* the threshold mask is in the 2nd status byte */
737                                 hfc->threshold_mask = buf[1];
738                                 /* care for L1 state only for D-Channel
 739                                    to avoid overlapped iso completions */
740                                 if (fifon == HFCUSB_D_RX) {
741                                         /* the S0 state is in the upper half
 742                                            of the 1st status byte */
743                                         s0_state_handler(hfc, buf[0] >> 4);
744                                 }
745                                 eof[fifon] = buf[0] & 1;
746                                 if (len > 2)
747                                         collect_rx_frame(fifo, buf + 2,
748                                                          len - 2,
749                                                          (len < maxlen) ?
750                                                          eof[fifon] : 0);
751                         } else {
752                                 collect_rx_frame(fifo, buf, len, 753                                                  (len <
754                                                   maxlen) ? eof[fifon] :
755                                                  0);
756                         }
 

Si el primer If no se cumple, se llama a collect_rx_frame desde la linea resaltada (752), habría que lograr que fifo->last_urblen sea igual maxlen, el if que tu marcas no tiene else, el else es del primer if resaltado.

Saludos
En línea

Bien Super Divertido
@wifigdlmx
Ivanchuk


Desconectado Desconectado

Mensajes: 466


LLVM


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #8 en: 20 Febrero 2010, 14:38 »

Si tenes razon no pegue todo el codigo, y me quedo cualquiera xD.
A investigar!
En línea

Sólo quien practica lo absurdo puede lograr lo imposible.

Join us @ http://foro.h-sec.org
kamsky
Colaborador
***
Desconectado Desconectado

Mensajes: 2.210


Como no sabían que era imposible, lo hicieron...


Ver Perfil
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #9 en: 20 Febrero 2010, 15:49 »

Buen análisis Nitr0us, un único apunte:

Citar
Es unsigned int, es decir, el último bit de la izquierda NO está reservado para el signo

Esto no es del todo cierto, ya que el orden en el que se almacenan los bits dependerá de la arquitectura del procesador (Big Endian o Little Endian), así que sería más correcto decir que el bit de mayor peso (independientemente de donde se almacene) es el que se reserva para definir el signo en caso de interpretar el entero como SIGNED

salu2
En línea

----NO HAY ARMA MÁS MORTÍFERA QUE UNA PALABRA BROTADA DE UN CORAZÓN NOBLE, Y UN PAR DE HUEVOS QUE LA RESPALDEN---

                       hack 4 free!!
Ivanchuk


Desconectado Desconectado

Mensajes: 466


LLVM


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #10 en: 20 Febrero 2010, 18:27 »

Me estuve informando un poco. Primero tengo que decir que nunca programe un driver usb jeje, asi que todo es not guaranteed xD.

Aparentemente en todos los casos de llamada a la funcion collect_rx_frame() se lo hace con un largo especificado por el URB (USB Request Block), que los crea el driver para transferencias usb. O sea que len no identifica el largo de la trama HDLC sino mas bien el de la transferencia usb.

La finalizacion de la trama HDLC la identifica el chip y la envia como informacion en las transferencias usb.
Se puede dar que len sea cero en el caso que justo el tamaño de la trama HDLC sea multiplo del tamaño maximo del urb lo que da lugar a otra transferencia pero nula.

En fin, todo ese quilombo para decir que len no puede ser negativo en las llamadas a collect_rx_frame() :(.

Referencias:
[1] Programming Guide for Linux USB Device Drivers. http://www.lrr.in.tum.de/Par/arch/usb/download/usbdoc/usbdoc-1.32.pdf
[2] ISDN HDLC FIFO controller with S/T interface and USB interface. http://www.colognechip.com/hfc-s-usb.pdf
[3] Lecion USB. http://sopa.dis.ulpgc.es/ii-dso/leclinux/drivers/usb/LEC_USB.pdf
[4] High-Level Data Link Control. http://es.wikipedia.org/wiki/High-Level_Data_Link_Control
« Última modificación: 20 Febrero 2010, 18:47 por Ivanchuk » En línea

Sólo quien practica lo absurdo puede lograr lo imposible.

Join us @ http://foro.h-sec.org
sirdarckcat
Troll Buena Onda y
CoAdmin
***
Desconectado Desconectado

Mensajes: 6.947


Lavando Platos


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #11 en: 20 Febrero 2010, 18:30 »

@kamsky, que pesado xD


@ivanchuck,
Citar
@sdc
No tengo mucha idea de cuando hay kernel panic, pero estando en ring0 no hay proteccion de nada no?. En espacio kernel no se deberia llamar a kernel_panic() explicitamente como dice nitrous?.
Por otro lado, cuando el kernel hace panic NO-intencionalmente, es por que intenta acceder a una dirección de memoria no válida y simplemente muere !.

Un ejemplo de esto, pero en user-land es http://www.brainoverflow.org/exploits_pocs/ELFsh_crash.esh

Saludos!!



En línea

Ivanchuk


Desconectado Desconectado

Mensajes: 466


LLVM


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #12 en: 20 Febrero 2010, 19:42 »

@kamsky, que pesado xD


@ivanchuck,
Citar
@sdc
No tengo mucha idea de cuando hay kernel panic, pero estando en ring0 no hay proteccion de nada no?. En espacio kernel no se deberia llamar a kernel_panic() explicitamente como dice nitrous?.
Por otro lado, cuando el kernel hace panic NO-intencionalmente, es por que intenta acceder a una dirección de memoria no válida y simplemente muere !.

Un ejemplo de esto, pero en user-land es http://www.brainoverflow.org/exploits_pocs/ELFsh_crash.esh

Saludos!!





mmm lo habia visto pero no parece ser un kernel panic ...

Citar
#(gdb) n 300
#
#Program received signal SIGSEGV, Segmentation fault.
#elfsh_find_previous_rmnbr (file=<value optimized out>, idx=65535) at save.c:57
#57                      if (sect->flags & ELFSH_SECTION_REMOVED)
#(gdb) p sect


Me parece que un kernel panic en user-land no creo que se pueda provocar.
En kernel mode cuando hay error de paridad en memoria, por interrupcion nmi por ej. se genera un kernel panic segun lei por ahi, pero no me doy idea como se puede controlar la ejecucion de un modulo cuando accede a cualquier direccion de memoria si esta en ring 0 :-\.
Tenes alguna referencia para leer sobre eso?
En línea

Sólo quien practica lo absurdo puede lograr lo imposible.

Join us @ http://foro.h-sec.org
sirdarckcat
Troll Buena Onda y
CoAdmin
***
Desconectado Desconectado

Mensajes: 6.947


Lavando Platos


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #13 en: 20 Febrero 2010, 19:57 »

mmm, a que memoria apuntaria fifo->skbuff->data[-1]? seria bueno ver en ASM que se hace ahi, porque si suma 0xFFFFFFFF entonces pues seria leer la memoria que hay antes de tal array..



no seria posible tambien pasar como len cualquier valor arbitrario? y hacer que el sistema trate de leer la memoria en 0x000000 o algo asi?




Saludos!!

En línea

sirdarckcat
Troll Buena Onda y
CoAdmin
***
Desconectado Desconectado

Mensajes: 6.947


Lavando Platos


Ver Perfil WWW
Re: CVE-2009-4005 - Linux Kernel 'hfc_usb.c'
« Respuesta #14 en: 20 Febrero 2010, 19:58 »

Código:
sdc@lindsay-family:~$ cat > test.c
#include <stdio.h>
int main(){
int x[100];
printf("%x",x[-1]);
}

sdc@lindsay-family:~$ gcc -o testarray test.c
sdc@lindsay-family:~$ ./testarray
67726

Código:
sdc@lindsay-family:~$ gcc -o testarray test.c
sdc@lindsay-family:~$ ./testarray
67726f 67726f
sdc@lindsay-family:~$ cat test.c
#include <stdio.h>
int main(){
int x[100];
printf("%x %x\n",*(x-1),x[-1]);
}
« Última modificación: 20 Febrero 2010, 20:02 por sirdarckcat » En línea

Páginas: [1] 2 Ir Arriba Respuesta Imprimir 

Ir a:  
Powered by SMF 1.1.16 | SMF © 2006-2008, Simple Machines