Listo!!

, luego de un par de horas..
"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:
/* 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.
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):
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...
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?
$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,
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:
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 !!:
#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:
./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:
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
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

.
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.eshSaludos !!