Microsoft Windows WRITE_ANDX SMB Command Handling Kernel DoS
16 Sep. 2008
Summary
Microsoft Windows is prone to a remote Kernel Denial of Service due to the way srv.sys handles malformed WRITE_ANDX SMB packets.
Remote attackers could exploit this issue without having valid credentials on the target machine. In order to achieve a successful exploitation, the attacker needs enough privileges to remotely send WRITE_ANDX packets to an interface that uses a Named Pipe as endpoint. Those interfaces that allow NULL Sessions vary between Windows versions, in Vista the reliability of a preauth attack through the "\LSARPC" has been successfully demonstrated.
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced. This cannot be protected by try-except, it must be protected by a Probe. Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: 92bc0000, memory referenced.
Arg2: 00000000, value 0 = read operation, 1 = write operation.
Arg3: 81c834b3, If non-zero, the instruction address which referenced the bad memory
address.
Arg4: 00000000, (reserved)
Debugging Details:
READ_ADDRESS: 92bc0000 Nonpaged pool
Srv.sys is the driver that will process the received SMB packet, once the packet is parsed it is routed through the proper driver. In this case, npfs.sys (named pipe filesystem driver). Npfs.sys handles named pipe requests. Below we can see how srv.sys parses some important fields of the packet:
M dulo: srv.sys Vista SP1
PAGE:00048583 movzx ecx, word ptr [ebx+17h] ; Packet. DataOffset
PAGE:00048587 mov [ebp+var_50], ecx
PAGE:0004858A mov eax, [esi+78h] ; Packet
PAGE:0004858D add eax, ecx ; Packet.Data[]
PAGE:0004858F mov [ebp+VirtualAddress], eax
PAGE:00048592 mov eax, [esi+6Ch]
PAGE:00048595 mov eax, [eax+10h]
PAGE:00048598 sub eax, ecx ; Real packet len - DataOffset
PAGE:0004859A movzx edi, word ptr [ebx+15h] ; Packet.DataLen
PAGE:0004859E cmp edi, eax
PAGE:000485A0 jb short loc_485A4
PAGE:000485A2 mov edi, eax
In this part of the code, the driver should add a check to avoid to continue if the offsets are not in concordance to the real size of the packet. Later on, srv.sys builds (or reuses) an FILESYSTE_CONTROL IRP (0xD), whose IOCTL is 0x119FF8 ( FSCTL_PIPE_INTERNAL_WRITE, METHOD_BUFFERED), then it sends this IRP to the proper driver by using a call to IofCallDriver. This IRP contains the packet, however it does not mean that the IRP keeps coherence, in terms of memory usage, with regards to the internal fields of the packet . It s worth noting that the memory the IO Manager allocates for a METHOD_BUFFERED buffer is reserved from the NonPaged Pool area (It is a important fact to have in mind for a better understanding of the bug).
M dulo: srv.sys Vista SP1
PAGE:00048C90 push ebx ; int
PAGE:00048C91 push ebx ; int
PAGE:00048C92 push ebx ; int
PAGE:00048C93 push ebx ; int
PAGE:00048C94 push edi ; int
PAGE:00048C95 push [ebp+VirtualAddress] ; int
PAGE:00048C98 push 119FF8h ; int
PAGE:00048C9D push 0Dh ; char
PAGE:00048C9F push esi ; int
PAGE:00048CA0 mov eax, [ebp+FileInformation]
PAGE:00048CA3 push dword ptr [eax+38h] ; FileObject
PAGE:00048CA6 push dword ptr [esi+80h] ; Irp
PAGE:00048CAC call _SrvBuildIoControlRequest@44 ; SrvBuildIoControlRequest(x,x,x,x,x,x,x,x,x,x,x)
PAGE:0001A1F6 mov ecx, [ebx] ; Packet.DataLen
PAGE:0001A1F8 cmp ecx, edi ; Entry.BufferLen ( 0x400 )
PAGE:0001A1FA jnb short loc_1A1FE
PAGE:0001A1FC mov edi, ecx
PAGE:0001A1FE
PAGE:0001A1FE loc_1A1FE: ; CODE XREF: NpWriteDataQueue(x,x,x,x,x,x,x,x,x,x)+A0 j
PAGE:0001A1FE cmp dword ptr [eax+10h], 1
PAGE:0001A202 jz short loc_1A22D
PAGE:0001A204 test edi, edi
PAGE:0001A206 jbe short loc_1A22D
PAGE:0001A208 push 5246704Eh ; Tag
PAGE:0001A20D push edi ; NumberOfBytes
PAGE:0001A20E push 0 ; PoolType
PAGE:0001A210 call ds:__imp__ExAllocatePoolWithTag@12 ; ExAllocatePoolWithTag(x,x,x)
In other words, for any amount of bytes that memcpy may copy, the same amount of bytes will be dynamically allocated. Thus, we never force an overflow condition.
Finally, below is the piece of code where the bug is triggered:
The parameters of memcpy are calculated to read the data field of the SMB packet. The resulting address will be the src parameter for memcpy, as wen can see it could be pointing to undetermined memory. When the flaw occurs, this pointer holds an address beyond the end of the NonPaged pool s buffers reserved by srv.sys. If this undetermined memory is not valid, the system will "BugCheck", thus triggering a kernel level DoS. Other possibilities like arbitrary kernel memory disclosure has not been researched.
The function that handles Non-Paged memory allocations in srv.sys is srv!SrvAllocateNonPagedPool. Every pool associated with srv.sys is tagged by using LSxx tags.
Every Windows version is theoretically affected by the flaw, however due to the nature of the bug, it might be impossible to reproduce it in certain cases. It has been empirically proven that Microsoft Windows Vista SP1 is more prone to this vulnerability than other versions where the work contexts of srv.sys becomes large from the very beginning.
Example:
Vista SP1
kd> !poolused 2
Pool Used:
NonPaged Paged
Tag Allocs Used Allocs Used
[ ]
LSwi 1 16464 0 0 initial work context
LSwn 4 33088 0 0 normal work context
[ ]
1: kd> !pool 92bbfecf-($Packet.DataLength)
Pool page 92bafed0 region is Nonpaged pool
*92baf000 : large page allocation, Tag is LSwn, size is 0x2050 bytes
Pooltag LSwn : normal work context
We demonstrate that the flaw is indeed reproducible.
1: kd> !pte 92bbfecf - ($Packet.DataLength)
VA 92bafed0
PDE at 00000000C06024A8 PTE at 00000000C0495D78
contains 00000000030B8863 contains 0000000009A40963
pfn 30b8 ---DA--KWEV pfn 9a40 -G-DA--KWEV
1: kd> !pte 92bbfecf + ($Packet.DataLength)
VA 92bcfece
PDE at 00000000C06024A8 PTE at 00000000C0495E78
contains 00000000030B8863 contains 0000325E00000000
pfn 30b8 ---DA--KWEV not valid
PageFile: 0
Offset: 325e
Protect: 0
Dumping memory
1: kd> db 92bbfecf - ($Packet.DataLength)
92bafed0 ff 53 4d 42 2f 00 00 00-00 18 07 c8 00 00 cc cc .SMB/...........
92bafee0 cc cc cc cc cc cc 00 00-00 08 dc 24 01 08 37 72 ...........$..7r
Thought the bug was not reproduced in this way (because it is related to how srv.sys handles its IRPs), if you are interested, you could debug some parts of this code: you can locally reproduce the way to reach to npfs!NpInternalWrite and npfs!NpWriteDataQueue by using Kartoffel (http://kartoffel.reversemode.com/):
k=72
j=0xffff
while j>10000
i=0xffff
while i>10000
begin
print_line("datalenlow=#{i} dataoffset=#{j} fillersize=#{k}")
subexploit(i,j,k)
rescue
print_line("rescue")
end
i=i-10000
end
j=j-10000
end