In this excerpt of a Trend Micro Vulnerability Research Service vulnerability report, Quintin Crist and Dusan Stevanovic of the Trend Micro Research Team detail a recently patched remote code execution vulnerability in the Microsoft Windows operating system, originally discovered and reported by the researcher known as Arimura. The bug is the result of a dynamically allocated buffer created by an NFS function and is present only on Windows Server 2022. An unauthenticated attacker could exploit this bug to execute arbitrary code in the context of SYSTEM. This is the third such NFS vulnerability in as many months. The following is a portion of their write-up covering CVE-2022-34715, with a few minimal modifications.
A remote code execution vulnerability exists in Windows Network File System. The vulnerability is due to incorrect validation of fields within an NFS request. A remote attacker can exploit this vulnerability by sending malicious RPC calls to a target server. Successful exploitation results in arbitrary code execution in the context of SYSTEM. Unsuccessful exploitation may result in a crash of the target system.
The Vulnerability
Microsoft Windows ships with several network features designed to communicate and interact with non-Windows file shares. One of these modules is called Network File System (NFS).
NFS is a protocol originally developed by Sun Microsystems in 1984. Version 2 is documented in RFC 1094. Version 3 is documented in RFC 1813. Version 4 was developed by the IETF and is documented in RFC 3010 (released December 2000) and revised in RFC 3530 (released April 2003) and RFC 7530 (released March 2015). NFS allows users to access remote file shares in the same way that the local file system is accessed. Different access levels and permissions can be set on shares, such as read-write and read-only. Additionally, IP/UID/GID/Kerberos security can be used. NFS uses Open Network Computing (ONC) Remote Procedure Call (RPC) to exchange control messages. ONC RPC was originally developed by Sun Microsystems and can also be referred to as Sun RPC.
When ONC RPC messages are transferred over TCP, they are prepended with a Fragment header structure (as illustrated in the following table) that specifies the length of the message. This allows the receiver to distinguish multiple messages sent over a single TCP session. Other protocols such as UDP do not use this field. Note that all multi-byte values are encoded in big-endian byte order.
Offset Size Description | |
——- —– ———————————- | |
0x0000 4 Fragment header, the highest bit is the last fragment flag, | |
lower bits represent the fragment size = N | |
0x0004 N RPC Message |
The structure of ONC RPC request messages, in general, is as follows:
Offset Size Description | |
——— —– ———————————- | |
0x0000 4 XID | |
0x0004 4 Message Type | |
0x0008 4 RPC Version | |
0x000c 4 Program | |
0x0010 4 Program Version | |
0x0014 4 Procedure | |
0x0018 C Credentials | |
0x0018+C V Verifier | |
0x0018+C+V N Program-specific data |
The Credentials structure in a Sun-RPC message has the following structure:
Offset Size Description | |
——– —– ———————————- | |
0x0000 4 Flavor | |
0x0004 4 Length, n | |
0x0008 n Contents | |
0x0008+n p fill bytes(padding to 4-byte alignment) |
The Flavor field in the above structure serves as a type identifier of the Contents data. Security flavors have been called authentication flavors for historical reasons. There are multiple security flavors defined in the RPC specification, such as AUTH_NONE(0), AUTH_SYS(1), AUTH_SHORT(2), AUTH_DH(3), and RPCSEC_GSS(6).
The Contents field for the flavor RPCSEC_GSS has the following structure:
Offset Size Description | |
——– —– ———————————- | |
0x0000 4 GSS Version, 1 for this version | |
0x0004 4 GSS Procedure | |
0x0008 4 GSS Sequence Number | |
0x000c 4 GSS Service | |
0x0010 x GSS Context | |
0x0010+x p fill bytes(padding to 4-byte alignment) |
There are four types defined for the GSS Procedure field: RPCSEC_GSS_DATA(0), RPCSEC_GSS_INIT(1), RPCSEC_GSS_CONTINUE_INIT(2), and RPCSEC_GSS_DESTROY(3). Also, for the GSS Service field, there are three types: rpc_gss_svc_none(1), rpc_gss_svc_integrity(2), and rpc_gss_svc_privacy(3). When using RPCSEC_GSS to authenticate RPC clients, a security context must be created by using RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT RPC messages. First, the RPC client sends an RPCSEC_GSS_INIT message to start the creation of the context. Then, the RPC server decides whether it needs another token for the creation. If so, the server replies with a GSS_S_CONTINUE_NEEDED message, and the client needs to send an RPCSEC_GSS_CONTINUE_INIT message to continue.
If the GSS Service field is set to 2 (rpc_gss_svc_integrity), the Program-specific data field is prefixed with the following structure:
Offset Size Description | |
——— —– ———————————- | |
0x0000 4 Length | |
0x0004 4 GSS Seq Number |
If the GSS Service field is set to 3 (rpc_gss_svc_privacy), the Program-specific data field is encrypted.
When the Program field is set to 100003 (NFS) and the Procedure field is set to 1 (Compound), the Program-specific data field has the following structure:
Offset Size Description | |
——— —– ———————————- | |
0x0000 4 Tag Length = T | |
0x0004 T Tag Data | |
0x0004+T 4 NFS Minor Version | |
0x0008+T 4 OP Count = O | |
0x000C+T … Request Data |
In the request data, for each operation in the message:
Offset Size Description | |
——— —– ———————————- | |
0x0000 4 Operation OpCode | |
0x0004 … Operation Data |
Operation data for opcode OP_CREATE(6):
typedef opaque utf8string<>; | |
typedef utf8string linktext4; | |
typedef utf8string component4; | |
enum nfs_ftype4 { | |
NF4REG = 1, /* Regular File */ | |
NF4DIR = 2, /* Directory */ | |
NF4BLK = 3, /* Special File – block device */ | |
NF4CHR = 4, /* Special File – character device */ | |
NF4LNK = 5, /* Symbolic Link */ | |
NF4SOCK = 6, /* Special File – socket */ | |
NF4FIFO = 7, /* Special File – fifo */ | |
NF4ATTRDIR = 8, /* Attribute Directory */ | |
NF4NAMEDATTR = 9 /* Named Attribute */ | |
}; | |
struct specdata4 { | |
uint32_t specdata1; | |
uint32_t specdata2; | |
}; | |
union createtype4 switch (nfs_ftype4 type) { | |
case NF4LNK: | |
linktext4 linkdata; | |
case NF4BLK: | |
case NF4CHR: | |
specdata4 devdata; | |
case NF4SOCK: | |
case NF4FIFO: | |
case NF4DIR: | |
void; | |
default: | |
void; /* server should return NFS4ERR_BADTYPE */ | |
}; | |
struct CREATE4args { //OPERATION DATA | |
/* CURRENT_FH: directory for creation */ | |
createtype4 objtype; | |
component4 objname; | |
fattr4 createattrs; | |
}; |
Operation data for opcode OP_OPEN(18):
typedef opaque verifier4[8]; | |
union open_claim4 switch (open_claim_type4 claim) { | |
/* | |
* No special rights to file. Ordinary OPEN of the specified file. | |
*/ | |
case CLAIM_NULL: | |
/* CURRENT_FH: directory */ | |
pathname4 file; | |
/* | |
* Right to the file established by an open previous to server | |
* reboot. File identified by filehandle obtained at that time | |
* rather than by name. | |
*/ | |
case CLAIM_PREVIOUS: | |
/* CURRENT_FH: file being reclaimed */ | |
uint32_t delegate_type; | |
/* | |
* Right to file based on a delegation granted by the server. | |
* File is specified by name. | |
*/ | |
case CLAIM_DELEGATE_CUR: | |
/* CURRENT_FH: directory */ | |
open_claim_delegate_cur4 delegate_cur_info; | |
/* Right to file based on a delegation granted to a previous boot | |
* instance of the client. File is specified by name. | |
*/ | |
case CLAIM_DELEGATE_PREV: | |
/* CURRENT_FH: directory */ | |
pathname4 file_delegate_prev; | |
}; | |
union openflag4 switch (opentype4 opentype) { | |
case OPEN4_CREATE: | |
createhow4 how; | |
default: | |
void; | |
}; | |
union createhow4 switch (createmode4 mode) { | |
case UNCHECKED4: | |
case GUARDED4: | |
fattr4 c reateattrs; | |
case EXCLUSIVE4: | |
verifier4 createverf; | |
}; | |
enum opentype4 { | |
OPEN4_NOCREATE = 0, | |
OPEN4_CREATE = 1 | |
}; | |
typedef uint64_t clientid4; | |
struct nfs_lockowner4 { | |
clientid4 clientid; | |
opaque owner<>; | |
}; | |
typedef uint32_t seqid4; | |
struct OPEN4args { //OPERATION DATA | |
open_claim4 claim; | |
openflag4 openhow; | |
nfs_lockowner4 owner; | |
seqid4 seqid; | |
uint32_t share_access; | |
uint32_t share_deny; | |
}; |
Operation data for opcode OP_SETATTR(34):
Offset Size Description | |
——— —– ———————————- | |
0x0000 16 StateID | |
0x0010 A Attributes Data (fattr4) | |
0x0010+A 4 Delegation Type |
Attributes Data (fattr4) has the following format:
Offset Size Description | |
——— —– ———————————- | |
0x0000 4 Bitmap Length = BL | |
0x0004 BL Bitmap Data | |
0x0004+BL 4 Attribute Data Length = AL | |
… … Data for first bit set in bitmap | |
… … Data for second bit set in bitmap | |
… | |
… … Data for last bit set in bitmap |
Attributes Data for ACL ( Bit12, 0x1000 ):
Offset Size Description | |
——— —– ———————————- | |
0x0000 4 ACE_Count = ACE | |
… |
A buffer overflow vulnerability exists in the Windows implementation of NFS. The vulnerability is due to incorrect validation of the of the ACE_Count
field when processing ACL attribute data in Nfs4SrvAclBuildWindowsAclsFromNfsAcl
.
This function is only vulnerable when setting ACL attribute data using opcodes 6, 18, 34.
The server allocates a response buffer of size (ACE_Count << 5)
. This size is stored as a uint64_t
in Nfs4SrvAclBuildWindowsAclsFromNfsAcl
. A buffer of this size is created using NfsMemMgrBufferAllocate
. However, NfsMemMgrBufferAllocate
only takes a uint32_t
for the buffer size, so the upper 32 bits of the requested size is ignored.
This allows an attacker to specify an ACE_Count
such that (ACE_Count << 5) & 0xFFFFFFFF < ACE_Count
. This will result in a buffer overflow later in the function when the buffer is used.
In particular, ACE_Count
values above 0x8000000 will trigger this vulnerability.
Note that ACE_Count = 0x8000000
itself is not vulnerable since NfsMemMgrBufferAllocate
will error when the requested length is zero.
An attacker can use this vulnerability to overflow a heap buffer. Successful exploitation may result in arbitrary code execution in the context of SYSTEM. Unsuccessful exploitation will result in a crash of the target system.
Source Code Walkthrough
The following code snippet was taken from nfssvr.sys
version 10.0.20348.825. Comments have been added by Trend Micro researchers.
Nfs4SrvAclBuildWindowsAclsFromNfsAcl | |
… | |
1c0067c1e MOV R8D,R12D //R12D is ACE_Count | |
1c0067c21 LEA R9=>local_50,[RBP + -0x1] | |
1c0067c25 SHL R8D,0x5 //ACE_Count, shifted left, stored 32bit register | |
1c0067c29 MOV EDX,0x504d4554 | |
1c0067c2e MOV ECX,R13D | |
1c0067c31 CALL NfsMemMgrBufferAllocate | |
… | |
//Later after successful buffer allocation | |
1c0067da8 XOR EDX,EDX | |
1c0067daa MOV EAX,R12D //R12D is ACE_Count | |
1c0067dad MOV RCX,R14 | |
1c0067db0 MOV R8D,R12D | |
1c0067db3 SHL R8,0x5 //ACE_Count, shifted left, stored 64bit register | |
1c0067db7 MOV qword ptr [RBP + local_88],RAX | |
1c0067dbb CALL memset // Crashes in the memset here due to huge count | |
… |
Detecting Attacks
The detection device needs to check if the Program field in an RPC request message has the value 100003 (NFS), Procedure field has the value 1 (COMPOUND), and Program Version field has the value 4 (NFS4). If found, the device must inspect the Program-specific data in the ONC RPC messages.
The detection device should check for the vulnerable opcodes (6, 18, 34) in each operation. If present the device should check the operation for ACL attribute data. If ACL attribute data is present, the device should check for an ACE_Count field above 0x8000000.
There is no fixed offset into the message that can be used to ignore non-vulnerable opcodes since NFS operations do not consistently contain a length of the operation data. The full message must be processed to determine if there is any ACL attribute data.
If the ACE_Count field is greater than 0x8000000, the traffic should be considered suspicious; an attack exploiting this vulnerability is likely underway.
Conclusion
This bug was patched by Microsoft in August 2022 and assigned CVE-2022-34715. Their advisory lists the vulnerability as not requiring authentication. However, all known exploitation paths require file creation or modification privileges. In their write-up, they also list disabling NFSv4.1 as a method to mitigate attacks. However, this could lead to a loss of functionality. Applying the NFS-related updates is the best method to fully address the multiple NFS bugs patched in recent months.
Special thanks to Quintin Crist and Dusan Stevanovic of the Trend Micro Research Team for providing such a thorough analysis of this vulnerability. For an overview of Trend Micro Research services please visit http://go.trendmicro.com/tis/.
The threat research team will be back with other great vulnerability analysis reports in the future. Until then, follow the team on Twitter or Instagram for the latest in exploit techniques and security patches.
原文始发于Trend Micro Research Team:趋势科技对 Windows NFS V4 远程代码执行漏洞(CVE-2022-34715)的分析