
AI로 생성한 주제 설명용 이미지입니다.
- 원문: Google Project Zero
- 저자: James Forshaw
- 공개일: 2026-09-21
- Tags: Windows, Privilege Escalation, COM, Custom Marshaling
본 글은 원 논문의 주요 기술적 내용을 이해하기 쉽게 요약·정리한 글입니다. 자세한 내용은 상단의 원문 링크를 참고하세요.
한눈에 보기
Google Project Zero는 Microsoft가 수정한 CVE-2026-66804의 권한 상승 원리를 설명한다. Windows에는 CrossDevice COM class가 system-wide로 등록돼 있었지만 in-process server DLL인 %PROGRAMDATA%\CrossDevice\CrossDevice.Streaming.Source.dll이 존재하지 않았다. 일반 사용자가 C:\ProgramData 아래 필요한 directory와 DLL을 만들 수 있어 dangling COM registration이 DLL planting primitive가 됐다.
직접 class를 만드는 것만으로는 SYSTEM process에 DLL이 로드되지 않는다. 연구자는 normal user가 실행할 수 있는 \Microsoft\Windows\Shell\CreateObjectTask로 SYSTEM Shell Create Object Handler COM server를 시작하고, custom marshaling이 허용된 이 server에 악성 OBJREF를 전달했다. Unmarshal 시 dangling CLSID가 조회되면서 planted DLL이 privileged process에 로드된다.
연구 배경
CVE-2026-66804는 “Dark Elevator”로 불린 CVE-2026-50343의 incomplete fix다. 이전 취약점은 weak registry permission으로 installer plugin class를 추가하고 InstallService가 이를 로드하게 했다. Microsoft가 InstallService 경로를 고쳤지만, 원래 CrossDevice dangling registration과 user-writable DLL path는 남아 다른 privileged COM activation path로 재사용됐다.
COM은 CLSID registry entry로 in-process server DLL을 찾는다. Server가 없는데 registration만 남고 path를 저권한 사용자가 만들 수 있으면 privileged client가 그 CLSID를 resolve하는 순간 공격자 DLL이 load될 수 있다.
공격 모델 / 전제 조건
공격자는 Windows의 normal local user이며 kernel exploit이나 administrator credential이 없다. Local filesystem에서 C:\ProgramData 아래 CrossDevice 경로와 DLL을 생성할 수 있고, normal user에게 execute 권한이 있는 scheduled task를 시작할 수 있다고 가정한다.
공격 대상은 SYSTEM으로 실행되는 COM server다. 이 server가 custom COM marshaling을 차단하는 EOAC_NO_CUSTOM_MARSHAL 또는 COMGLB_UNMARSHALING_POLICY_STRONG을 활성화하지 않아야 한다. 최종 목적은 SYSTEM process가 공격자 선택 DLL을 in-process server로 load하게 하는 것이다.
핵심 Root Cause
시스템 전역 COM registration이 존재하지 않는 DLL을 모든 사용자가 directory를 생성할 수 있는 ProgramData 하위에서 찾도록 남아 있었던 것이 1차 원인이다. 기존 fix는 이 registration을 제거하거나 path ownership을 바로잡지 않고 한 activation carrier인 InstallService만 막았다.
2차 원인은 privileged Shell Create Object Handler가 custom marshaling을 허용한다는 것이다. COM runtime은 custom OBJREF의 CLSID를 신뢰해 unmarshal object의 in-process server를 lookup·load한다. Writable path의 dangling registration과 privileged unmarshaling endpoint가 결합돼 권한 경계가 무너진다.
핵심 공격 원리
COM object가 IMarshal을 구현하면 GetUnmarshalClass에서 실제 전달 object와 다른 CLSID를 지정할 수 있다. 공격자는 CrossDevice dangling CLSID {E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}를 반환하는 fake marshaler를 만든다.
이 object를 privileged out-of-process COM server의 interface parameter로 전달하면 RPC 호출 전 COM runtime이 object를 marshal하고 server에서 자동 unmarshal한다. Server가 CLSID를 조회하면 system-wide registration이 가리키는 planted DLL을 해당 SYSTEM process에 load한다.
공격 흐름
- 공격자는 C:\ProgramData\CrossDevice\CrossDevice.Streaming.Source.dll 위치에 DLL을 준비한다.
- Global named event ShellCreateObjectTaskReadyEvent를 생성한다.
- normal user가 실행 가능한 Shell\CreateObjectTask scheduled task를 시작한다.
- Task가 SYSTEM Shell Create Object Handler COM server를 export한다.
- 공격자는 fake IMarshal object가 dangling CrossDevice CLSID를 반환하도록 한다.
- ICreateObject::Proc3의 IUnknown* parameter로 fake object를 전달한다.
- SYSTEM server가 custom OBJREF를 unmarshal하며 CLSID의 in-process DLL을 load한다.
- 공격자 DLL의 code가 privileged process 안에서 실행된다.
성공 조건 / 실패 조건
성공에는 dangling system-wide CLSID, user-creatable directory·DLL path, normal user가 시작할 수 있는 privileged COM server, custom marshaling 허용, attacker-controlled COM object parameter가 모두 필요하다.
Registration을 제거하거나 DLL path를 privileged owner만 쓸 수 있게 하면 실패한다. Privileged COM server가 EOAC_NO_CUSTOM_MARSHAL 또는 strong unmarshaling policy를 켜고 class를 trusted marshaler로 opt-in하지 않으면 custom CLSID load도 차단된다. Scheduled task 실행이나 global event 생성 권한을 줄이는 것도 특정 carrier를 막지만 dangling registration 자체를 해결하지는 않는다.
연구진의 실험 환경
이 자료는 학술 논문의 대규모 실험이 아니라 Project Zero의 exploitation technique 설명이다. 저자는 OleViewDotNet·NtObjectManager command를 사용해 COM class, scheduled task access, hosting process와 CustomMarshalAllowed 상태를 확인하고 working exploit을 원래 issue에 첨부했다고 밝힌다.
구체적인 Windows build별 반복 횟수·성공률·patch matrix는 게시글에 제시되지 않는다. Get-ComProcess command는 Windows 11 25H2의 구조 변경 때문에 현재 깨져 있고 이전 버전에서 동작한다고 명시돼 있다. 이는 진단 도구 제한이지 취약점의 affected range를 뜻하지 않는다.
주요 실험 결과
CrossDevice CLSID는 %PROGRAMDATA%\CrossDevice\CrossDevice.Streaming.Source.dll을 가리키지만 DLL이 없었고, normal user가 parent structure를 만들 수 있었다. Shell Create Object Handler는 일반적인 class activation으로는 “Class not registered”가 나지만, normal user가 시작 가능한 scheduled task와 ready event를 사용하면 SYSTEM server로 활성화됐다.
해당 dllhost는 custom marshaling을 허용했고, ICreateObject interface의 두 번째 parameter가 IUnknown*이어서 별도의 복잡한 IStorage fake 없이 custom-marshaled object를 전달할 수 있었다. 이를 통해 planted DLL이 privileged process에 load되는 완전한 CVE-2026-66804 exploit path를 구성했다.
실제 발견된 취약점 / 사례
CVE-2026-66804는 dangling CrossDevice COM registration과 custom marshaling을 결합한 local privilege escalation이다. 게시글은 15명의 reporter가 같은 문제를 신고했다고 밝히며, CVE-2026-50343 fix 이후에도 근본 registration이 남아 있었음을 강조한다.
동일 기법은 dangling registration뿐 아니라 buggy custom unmarshaler에도 적용될 수 있다. DLL load만으로도 process crash나 code execution이 발생할 수 있어 privileged COM server의 custom marshaling policy가 중요한 attack surface다.
저자 주장 vs 실제 증명 범위
게시글은 하나의 working exploit chain과 dangling COM server를 찾는 audit 방법을 구체적으로 설명한다. COM unmarshal semantics, task activation, privileged host와 writable path의 연결은 실제 tool output과 code skeleton으로 뒷받침된다.
그러나 전체 Windows version affected range, patch build, exploit reliability, EDR 우회, remote attack은 증명하지 않는다. Local normal-user foothold가 선행하며, 모든 privileged COM server가 custom marshaling을 허용하는 것도 아니다. “다른 server도 있을 것”이라는 언급은 추정이지 조사 결과 목록이 아니다.
기존 공격 / 기존 점검 방식과의 차이
이전 Dark Elevator는 weak registry permission으로 InstallService plugin registration을 추가했다. 새 경로는 남아 있던 dangling registration을 그대로 사용하고, 별도의 SYSTEM COM server에 custom OBJREF를 보내 DLL load를 유도한다.
기존 점검이 service executable 존재 여부나 writable registry만 보면 놓친다. Machine-wide InProcServer32 전체에서 target DLL이 실제 load 가능한지, path component를 low-privilege user가 만들거나 바꿀 수 있는지, privileged COM process의 unmarshaling policy가 무엇인지 결합해서 봐야 한다.
연구의 한계와 주의해서 볼 부분
게시글은 짧은 기술 분석으로 재현 환경·patch chronology·affected build를 완전하게 문서화하지 않는다. CVE가 최근 수정됐다고만 설명하므로 운영 환경에서는 Microsoft advisory와 실제 installed build를 별도로 확인해야 한다.
제시된 scanning script는 missing DLL을 찾을 뿐 exploitability를 자동 판정하지 않는다. 환경 변수 확장, search path resolution, directory ACL, file ACL, WOW64 view, COM trust policy, activation 가능한 privileged client를 수동 검증해야 한다.
공개 PoC / Exploit / Tool / Artifact 분석
공식 Project Zero 글은 original issue의 attachment에 updated working exploit이 있다고 명시한다. 2026-09-22 확인 시 issue URL은 응답하지만 web view가 Google sign-in 화면을 제공해 첨부파일 내용과 download 가능 여부를 독립적으로 검증하지 못했다. 따라서 공개 글에서 PoC 존재는 확인되지만, 현재 비인증 사용자가 바로 내려받아 실행 가능한 artifact라고 단정하지 않는다.
게시글의 discovery script는 James Forshaw의 OleViewDotNet·NtObjectManager PowerShell command를 전제로 한다. 글에는 이번 CVE 전용 공식 GitHub 저장소가 제시되지 않았다.
레드팀 / 모의해킹에서 어떻게 활용할까
승인된 Windows lab에서 임의 DLL을 실행하기 전에 machine-wide COM registration과 filesystem ACL을 read-only로 교차 분석한다. Missing InProcServer32 target을 찾고 각 path component에 standard user가 create·modify할 수 있는지 확인한다.
그 다음 privileged COM process의 custom marshal policy, activation principal, scheduled task ACL, parameter interface를 확인한다. 실제 load 검증이 필요하면 harmless DLL이 marker만 남기도록 하고 production system이 아닌 snapshot 가능한 VM에서 수행한다.
실제 점검 시 추가할 체크리스트
- Machine-wide InProcServer32 target이 실제 존재하고 load 가능한가
- 환경변수 확장 뒤 모든 parent directory ACL이 안전한가
- standard user가 missing path component 또는 DLL을 만들 수 있는가
- 기존 patch가 carrier만 막고 dangling registration을 남기지 않았는가
- privileged COM server가 EOAC_NO_CUSTOM_MARSHAL을 적용하는가
- COMGLB_UNMARSHALING_POLICY_STRONG 상태가 강제되는가
- normal user가 privileged server의 scheduled task를 시작할 수 있는가
- global named object가 service lifetime이나 activation을 제어하는가
- interface parameter가 attacker-controlled COM object를 받는가
- patch 뒤 registration 제거·ACL·unmarshal policy를 각각 회귀시험했는가
실무 가치 평가
매우 높다. 단일 weak ACL이 아니라 registry dangling reference, filesystem namespace, task ACL, COM marshaling policy가 합성돼 SYSTEM code execution으로 이어지는 Windows exploitation 사고방식을 잘 보여준다. 특히 incomplete fix 검토와 variant analysis에 직접 활용할 수 있다.
결론
Dangling COM registration은 단순 orphan entry가 아니다. Target path를 저권한 사용자가 만들 수 있고 privileged process가 custom CLSID를 unmarshal하면 DLL planting이 권한 상승으로 바뀐다. Fix는 한 activation route만 막는 것이 아니라 registration·path ownership·privileged unmarshaling 정책을 함께 닫아야 한다.
보다 보니.. 이해가 안되는 부분이 있어서 추가로 공부한 부분
Windows Dangling COM 이해를 위한 핵심 용어 정리
참고 글: Google Project Zero
Windows Dangling COM Objects and Local Privilege Escalation
https://projectzero.google/2026/09/windows-dangling-com.html
Google Project Zero의 Windows Dangling COM Objects and Local Privilege Escalation 글은 Windows COM 구조를 어느 정도 알고 있다는 전제에서 작성되어 있어 처음 읽으면 Custom Marshaling, OBJREF, IMarshal, Shell Create Object Handler 같은 용어에서 막히기 쉽습니다.
이 글에서는 원문의 공격 흐름을 이해하는 데 필요한 용어를 중심으로 정리합니다.
1. 전체 공격 흐름부터 보기
먼저 세부 용어보다 전체 구조를 보면 이해가 쉽습니다.
[일반 사용자]
│
│ 공격자 DLL 배치
▼
[Dangling COM Registration]
CLSID → 존재하지 않던 DLL 경로
│
│ Custom Marshaling
▼
[SYSTEM 권한 COM Server]
Shell Create Object Handler
│
│ Unmarshal 수행
▼
[Windows COM Runtime]
│
│ CLSID Registry 조회
▼
[공격자 DLL 로드]
│
▼
SYSTEM 권한 코드 실행
핵심은 다음과 같습니다.
공격자가 선택한 COM CLSID를 SYSTEM 권한 프로세스가 Unmarshal하도록 유도하고,
해당 CLSID가 가리키는 DLL을 SYSTEM 프로세스가 로드하게 만드는 방식입니다.
2. COM
COM(Component Object Model) 은 Windows에서 프로그램과 프로그램, 또는 컴포넌트끼리 기능을 호출할 수 있도록 만든 구조입니다.
예를 들어 프로그램이 특정 기능을 사용하고 싶을 때 DLL 경로를 직접 알 필요 없이 COM 객체를 요청할 수 있습니다.
Program A
│
│ "이 COM 객체를 생성해 줘"
▼
Windows COM Runtime
│
▼
COM Object
COM에서는 각 객체를 식별하기 위해 CLSID를 사용합니다.
3. CLSID
CLSID(Class Identifier) 는 COM 클래스를 구분하기 위한 GUID입니다.
예를 들어 다음과 같은 값입니다.
{E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}
Windows Registry에는 CLSID와 실제 COM Server가 연결되어 있습니다.
CLSID
{E9F83CF2-...}
│
▼
InProcServer32
│
▼
C:\ProgramData\CrossDevice\
CrossDevice.Streaming.Source.dll
즉 프로그램이 CLSID를 이용해 COM 객체 생성을 요청하면 Windows가 Registry를 확인해 어떤 DLL 또는 EXE를 사용해야 하는지 찾습니다.
4. COM Server
여기서 Server는 웹 서버나 네트워크 서버를 의미하지 않습니다.
COM 객체를 실제로 구현하거나 호스팅하는 프로그램을 의미합니다.
COM Server는 크게 두 종류가 있습니다.
| 종류 | 형태 | 특징 |
|---|---|---|
| In-process Server | DLL | 호출한 프로세스 내부에 DLL이 로드됨 |
| Out-of-process Server | EXE | 별도의 프로세스에서 실행됨 |
In-process COM Server
Application.exe
│
└── LoadLibrary()
│
▼
example.dll
Out-of-process COM Server
Client.exe
│
│ RPC
▼
COMServer.exe
이번 Project Zero 글에서는 SYSTEM 권한의 COM Server가 공격자의 DLL을 로드하게 만드는 것이 핵심입니다.
5. COM Registration
COM 객체가 어떤 DLL 또는 EXE와 연결되는지 Windows Registry에 등록하는 과정입니다.
쉽게 말하면 COM용 전화번호부입니다.
CLSID
│
▼
Registry
│
▼
DLL / EXE
예를 들면 다음과 같습니다.
CLSID
{AAAA-BBBB-CCCC}
InProcServer32
C:\Windows\System32\example.dll
6. Dangling COM Registration
이번 취약점에서 가장 중요한 개념입니다.
Dangling은 등록 정보는 남아 있지만 실제 대상 파일은 존재하지 않는 상태를 의미합니다.
예를 들어 Registry에는 다음 정보가 존재합니다.
CLSID
│
▼
C:\ProgramData\CrossDevice\
CrossDevice.Streaming.Source.dll
하지만 실제 파일은 존재하지 않습니다.
C:\ProgramData\CrossDevice\
CrossDevice.Streaming.Source.dll
→ 파일 없음
이를 Dangling COM Registration이라고 합니다.
문제는 해당 경로에 일반 사용자가 파일을 생성할 수 있다면 발생합니다.
기존 상태
CLSID
│
▼
[존재하지 않는 DLL]
공격자가 DLL을 생성한 후:
CLSID
│
▼
[공격자가 생성한 DLL]
Registry를 수정하지 않았는데도 기존 COM 등록이 공격자의 DLL을 가리키게 됩니다.
7. Marshaling
COM에서는 객체가 다른 프로세스로 전달되는 경우가 많습니다.
하지만 다음과 같이 한 프로세스의 메모리 주소를 다른 프로세스에 그대로 전달할 수는 없습니다.
Process A
Object Address
0x12345678
Process B에서 0x12345678은 전혀 다른 메모리를 의미할 수 있습니다.
따라서 COM은 객체를 다른 프로세스에서 사용할 수 있는 형태로 변환합니다.
이 과정을 Marshaling이라고 합니다.
COM Object
│
│ Marshaling
▼
Serialized Object Information
│
│ RPC
▼
Other Process
쉽게 표현하면 다음과 같습니다.
COM 객체를 다른 프로세스에 전달하기 위해 필요한 정보로 포장하는 과정입니다.
8. Unmarshaling
Unmarshaling은 Marshaling의 반대 과정입니다.
Marshaling
COM Object
↓
전달 가능한 데이터
Unmarshaling
전달된 데이터
↓
COM Object
받는 프로세스가 전달받은 정보를 이용해 COM 객체를 다시 사용할 수 있도록 만드는 과정입니다.
이번 취약점에서는 바로 이 Unmarshaling 과정이 공격에 이용됩니다.
9. OBJREF
OBJREF(Object Reference) 는 Marshaling된 COM 객체를 표현하는 데이터 구조입니다.
COM Object
│
│ Marshal
▼
OBJREF
OBJREF에는 상대 프로세스가 COM 객체를 다시 사용할 수 있도록 필요한 정보가 들어 있습니다.
대표적인 형태는 다음과 같습니다.
| 종류 | 의미 |
|---|---|
| OBJREF_STANDARD | 일반적인 COM 객체 참조 |
| OBJREF_HANDLER | Handler 기반 객체 참조 |
| OBJREF_CUSTOM | Custom Marshaling 객체 |
| OBJREF_EXTENDED | 확장된 객체 참조 |
이번 글에서 가장 중요한 것은 OBJREF_CUSTOM 입니다.
10. Standard Marshaling
일반적인 COM Marshaling은 원본 객체를 다른 프로세스에 복사하는 방식이 아닙니다.
대신 원본 객체를 계속 참조합니다.
Process A
[Original COM Object]
▲
│
│ RPC
│
Process B
│
[Proxy]
Process B에서 객체의 메서드를 호출하면 실제 요청은 RPC를 통해 Process A의 원본 객체로 전달됩니다.
이를 흔히 Marshal by Reference 방식으로 이해할 수 있습니다.
11. Custom Marshaling
일반적으로 COM Runtime이 객체의 Marshaling 방식을 처리합니다.
하지만 COM 객체가 IMarshal 인터페이스를 직접 구현하면 자신의 Marshaling 방식을 직접 정의할 수 있습니다.
일반 Marshaling
COM Runtime
│
└── Marshaling 방법 결정
Custom Marshaling
COM Object
│
└── IMarshal 구현
│
└── 직접 Marshaling 방법 결정
즉 Custom Marshaling은 다음과 같이 이해하면 됩니다.
COM 객체가 기본 Marshaling 방식 대신 자신만의 객체 전달 및 복원 방법을 정의하는 기능입니다.
12. IMarshal
IMarshal은 COM 객체가 Custom Marshaling을 구현할 때 사용하는 인터페이스입니다.
대표적인 메서드는 다음과 같습니다.
| 메서드 | 역할 |
|---|---|
| GetUnmarshalClass | Unmarshal에 사용할 COM Class 지정 |
| MarshalInterface | 객체를 Marshaling |
| UnmarshalInterface | 객체를 Unmarshaling |
| ReleaseMarshalData | Marshaling 데이터 해제 |
이번 취약점에서 특히 중요한 것은 GetUnmarshalClass() 입니다.
13. GetUnmarshalClass
GetUnmarshalClass()는 다음 질문에 답하는 함수입니다.
이 객체를 Unmarshal할 때 어떤 COM Class를 사용해야 하는가?
반환값은 CLSID입니다.
예를 들어 공격자가 다음 CLSID를 반환하도록 만들 수 있습니다.
GetUnmarshalClass(...)
{
return CLSIDFromString(
L"{E9F83CF2-...}",
pCid
);
}
그러면 상대 프로세스의 COM Runtime은 해당 CLSID를 Registry에서 조회합니다.
GetUnmarshalClass()
│
▼
CLSID
│
▼
Registry Lookup
│
▼
InProcServer32
│
▼
DLL Load
여기서 CLSID가 Dangling COM Registration을 가리키고 있고 공격자가 해당 DLL 경로를 채워놓았다면 다음 상황이 만들어집니다.
SYSTEM Process
│
│ Unmarshal
▼
공격자가 지정한 CLSID
│
▼
Registry
│
▼
공격자가 생성한 DLL
│
▼
SYSTEM 권한으로 DLL Load
이 부분이 이번 공격의 핵심 연결 고리입니다.
14. RPC
RPC(Remote Procedure Call) 는 다른 프로세스 또는 다른 컴퓨터에 있는 함수를 호출하는 기술입니다.
COM에서도 프로세스 간 통신에 RPC가 사용됩니다.
Client
│
Proxy
│
│ RPC
▼
Stub
│
Server
Client 입장에서는 일반 함수 호출처럼 보이지만 실제 요청은 다른 프로세스의 COM Server에서 실행될 수 있습니다.
15. Proxy / Stub
RPC 또는 COM에서 자주 등장하는 용어입니다.
Proxy
클라이언트 쪽에서 실제 객체처럼 행동하는 대리 객체입니다.
Client
│
▼
Proxy
│
│ RPC
▼
Server
Stub
서버 쪽에서 RPC 요청을 받아 실제 COM 객체에 전달하는 역할을 합니다.
RPC Data
│
▼
Stub
│
▼
COM Object
간단히 정리하면 다음과 같습니다.
Client
│
Proxy
│
==== RPC ====
│
Stub
│
Server Object
16. SYSTEM Shell Create Object Handler COM Server
이 표현은 하나의 기술 용어가 아니라 세 부분으로 나누면 쉽게 이해할 수 있습니다.
SYSTEM
+
Shell Create Object Handler
+
COM Server
SYSTEM
Windows의 높은 권한을 가진 시스템 계정입니다.
NT AUTHORITY\SYSTEM
Windows 서비스나 운영체제 내부 작업에서 자주 사용됩니다.
Shell Create Object Handler
이번 글에서 공격에 이용된 COM 구성요소의 이름입니다.
관련 CLSID는 다음과 같습니다.
{135FD325-45B7-4C30-89F8-4386961669F0}
COM Server
해당 COM 객체를 실제로 실행하는 프로세스입니다.
Project Zero 글에서 확인된 상태는 다음과 같습니다.
Process : dllhost.exe
User : NT AUTHORITY\SYSTEM
CustomMarshalAllowed : True
즉 다음과 같이 이해할 수 있습니다.
SYSTEM 권한의
dllhost.exe에서 실행되는Shell Create Object Handler라는 COM 객체입니다.
17. Shell Create Object Handler와 Dangling COM은 서로 다른 객체입니다
이번 글에서 가장 헷갈리기 쉬운 부분입니다.
Shell Create Object Handler 자체가 Dangling COM Registration을 가진 것은 아닙니다.
공격에는 서로 다른 두 개의 COM CLSID가 등장하며, 역할도 다릅니다.
| 역할 | COM | CLSID | 상태 |
|---|---|---|---|
| 고권한 실행 지점 | Shell Create Object Handler | {135FD325-45B7-4C30-89F8-4386961669F0} |
정상 존재, SYSTEM 권한, Custom Marshaling 허용 |
| 실제 DLL 로드 대상 | CrossDevice COM | {E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496} |
Dangling Registration, DLL 없음 |
즉 두 COM 객체의 관계는 원래부터 연결되어 있던 것이 아닙니다.
공격자가 Custom Marshaling을 이용해 두 객체를 연결한 것입니다.
Shell Create Object Handler는 무엇인가
Shell Create Object Handler는 Windows에 원래 존재하는 COM 구성요소입니다.
이 객체는 SYSTEM 권한의 dllhost.exe에서 실행되며, COM 객체 생성을 중계하는 역할을 합니다.
개념적으로는 다음과 같습니다.
Shell Create Object Handler
"이 CLSID의 COM 객체를 만들어 줘"
│
▼
ICreateObject
│
▼
COM Object 생성
Project Zero가 이 객체를 공격에 이용한 이유는 다음 세 가지입니다.
1. SYSTEM 권한으로 실행됩니다
Process : dllhost.exe
User : NT AUTHORITY\SYSTEM
따라서 이 프로세스 안에서 공격자의 DLL이 로드되면 해당 DLL 역시 SYSTEM 권한으로 실행됩니다.
2. Custom Marshaling이 허용되어 있습니다
해당 COM Server에서는 다음과 같은 상태가 확인되었습니다.
CustomMarshalAllowed = True
즉 공격자가 만든 IMarshal 기반 COM 객체를 이 SYSTEM 프로세스로 전달할 수 있습니다.
3. COM 객체를 인자로 받을 수 있습니다
Shell Create Object Handler가 사용하는 ICreateObject 인터페이스는 IUnknown* 타입의 인자를 받을 수 있습니다.
즉 단순 데이터가 아니라 COM 객체를 전달할 수 있습니다.
공격자는 이 자리에 일반 COM 객체 대신 Custom Marshaling을 구현한 객체를 전달합니다.
Attacker
│
▼
Fake IMarshal Object
│
│ IUnknown* Parameter
▼
SYSTEM
Shell Create Object Handler
18. Shell Create Object Handler가 Dangling COM을 직접 호출하는 것은 아닙니다
처음 보면 다음과 같이 생각하기 쉽습니다.
Shell Create Object Handler
│
│ 원래 기능
▼
Dangling CrossDevice COM
│
▼
missing.dll
하지만 실제 구조는 다릅니다.
Shell Create Object Handler와 CrossDevice COM은 원래 별개의 COM 객체입니다.
공격자는 Custom Marshaling의 GetUnmarshalClass() 를 이용해 두 객체를 연결합니다.
공격자
│
│ Fake IMarshal 객체 전달
▼
Shell Create Object Handler
(SYSTEM)
│
│ COM Runtime이 전달받은 객체를
│ Unmarshal
▼
GetUnmarshalClass()
│
│ 공격자가 CLSID 지정
▼
CrossDevice CLSID
{E9F83CF2-...}
│
▼
Registry Lookup
│
▼
CrossDevice.Streaming.Source.dll
즉 핵심은 다음과 같습니다.
Shell Create Object Handler가 원래부터 CrossDevice COM을 호출하는 것이 아니라, 공격자가GetUnmarshalClass()에서 CrossDevice CLSID를 반환하도록 만들어 SYSTEM COM Runtime이 해당 CLSID를 사용하게 합니다.
19. GetUnmarshalClass가 두 COM 객체를 연결하는 지점
공격자가 만든 Custom Marshaling 객체는 개념적으로 다음과 같은 동작을 합니다.
GetUnmarshalClass(...)
{
return CLSIDFromString(
L"{E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}",
pCid
);
}
이 함수는 SYSTEM 프로세스의 COM Runtime에게 다음과 같이 알려주는 것과 같습니다.
"이 객체를 Unmarshal하려면
이 CLSID를 사용하세요."
{E9F83CF2-E0C0-4CA7-AF01-E90C70BEF496}
그러면 SYSTEM 권한의 COM Runtime은 Registry에서 해당 CLSID를 조회합니다.
GetUnmarshalClass()
│
▼
CrossDevice CLSID
│
▼
HKCR\CLSID\{E9F83CF2-...}
│
▼
InProcServer32
│
▼
%PROGRAMDATA%\CrossDevice\
CrossDevice.Streaming.Source.dll
그런데 이 DLL은 원래 존재하지 않는 Dangling 상태입니다.
공격자가 해당 위치에 DLL을 생성해 두면 다음과 같이 연결됩니다.
SYSTEM dllhost.exe
│
│ Unmarshal
▼
GetUnmarshalClass()
│
▼
CrossDevice CLSID
│
▼
Registry
│
▼
공격자가 생성한 DLL
│
▼
SYSTEM 권한으로 DLL Load
20. 두 COM 객체의 역할을 비유하면
역할을 구분해서 보면 이해하기 쉽습니다.
CrossDevice Dangling COM
등록 정보는 존재하지만 실제 DLL이 없는 상태입니다.
CLSID 존재
│
▼
DLL 경로 존재
│
▼
실제 DLL 없음
공격자가 해당 경로에 자신이 만든 DLL을 배치할 수 있다면 Dangling Registration을 탈취할 수 있습니다.
Shell Create Object Handler
SYSTEM 권한으로 동작하면서 Custom Marshaling을 허용하는 COM Server입니다.
공격자는 이 객체를 통해 SYSTEM 프로세스가 자신이 지정한 CLSID를 Unmarshal하도록 유도합니다.
즉 역할을 요약하면 다음과 같습니다.
Shell Create Object Handler
= SYSTEM 권한의 Unmarshal 실행 지점
CrossDevice COM
= 공격자의 DLL로 연결되는 Dangling CLSID
21. 실제 공격 관계
전체 관계를 연결하면 다음과 같습니다.
[공격자]
│
│ Fake IMarshal
▼
┌─────────────────────────────┐
│ Shell Create Object Handler │
│ │
│ CLSID: {135FD325-...} │
│ SYSTEM dllhost.exe │
│ Custom Marshaling: Allowed │
└──────────────┬──────────────┘
│
│ Unmarshal
▼
GetUnmarshalClass()
│
│ CrossDevice CLSID 반환
▼
┌─────────────────────────────┐
│ CrossDevice COM │
│ │
│ CLSID: {E9F83CF2-...} │
│ Dangling Registration │
└──────────────┬──────────────┘
│
▼
%PROGRAMDATA%\CrossDevice\
CrossDevice.Streaming.Source.dll
│
│ 공격자가 배치
▼
Attacker DLL
│
▼
SYSTEM 코드 실행
핵심적으로 두 객체의 역할은 다음과 같이 나뉩니다.
Shell Create Object Handler
│
└── SYSTEM 권한 + Custom Marshaling 허용
│
│ GetUnmarshalClass()
▼
CrossDevice COM
│
└── Dangling Registration
│
▼
Attacker DLL
22. dllhost.exe
dllhost.exe는 Windows의 COM Surrogate 프로세스입니다.
DLL 기반 COM 객체를 별도의 프로세스에서 실행해야 할 때 사용됩니다.
dllhost.exe
┌────────────────────┐
│ COM Object A │
│ COM Object B │
└────────────────────┘
이번 공격에서는 dllhost.exe가 SYSTEM 권한으로 실행되고 있다는 점이 중요합니다.
공격 목표는 결국 다음과 같습니다.
공격자 DLL
│
▼
SYSTEM dllhost.exe
│
▼
DLL Load
23. CoCreateInstance
CoCreateInstance()는 COM 객체를 생성할 때 사용하는 대표적인 Windows API입니다.
개념적인 흐름은 다음과 같습니다.
CoCreateInstance()
│
▼
CLSID 확인
│
▼
Registry 조회
│
▼
COM Server 탐색
│
▼
COM Object 생성
프로그램은 DLL 경로를 직접 알 필요 없이 CLSID만 이용해 객체를 생성할 수 있습니다.
24. RPCSS
RPCSS(Remote Procedure Call Service) 는 Windows의 RPC와 COM 인프라에서 중요한 시스템 서비스입니다.
일반적인 Out-of-process COM 객체 생성은 다음과 같은 흐름으로 이루어질 수 있습니다.
Application
│
CoCreateInstance
│
▼
RPCSS
│
▼
COM Server 실행
│
▼
COM Object 반환
Project Zero 글에서는 Shell Create Object Handler가 일반적인 COM Server처럼 자동 실행되지 않는다는 점도 공격 과정에서 중요한 요소로 설명됩니다.
25. CreateObjectTask
글에서는 다음 Scheduled Task가 등장합니다.
\Microsoft\Windows\Shell\CreateObjectTask
이 작업을 통해 SYSTEM 권한의 COM Server를 실행합니다.
일반 사용자
│
│ Scheduled Task 실행
▼
CreateObjectTask
│
▼
SYSTEM dllhost.exe
│
▼
Shell Create Object Handler
즉 공격에 필요한 SYSTEM COM Server를 활성화하는 역할을 합니다.
26. Global Named Event
글에는 다음 Windows Event 객체도 등장합니다.
Global\ShellCreateObjectTaskReadyEvent
Windows Event는 프로세스 간 동기화에 사용되는 객체입니다.
예를 들어 한 프로세스가 다음과 같이 신호를 보낼 수 있습니다.
Process A
SetEvent()
│
▼
"준비 완료"
다른 프로세스는 해당 Event가 발생할 때까지 기다립니다.
Process B
WaitForSingleObject()
│
▼
작업 계속
이번 공격에서는 Scheduled Task와 COM Server 실행 상태를 유지하는 과정에서 사용됩니다.
27. IUnknown
IUnknown은 COM의 가장 기본적인 인터페이스입니다.
대부분의 COM 인터페이스는 IUnknown을 기반으로 만들어집니다.
대표 메서드는 다음 세 가지입니다.
| 메서드 | 역할 |
|---|---|
| QueryInterface | 다른 인터페이스 지원 여부 확인 |
| AddRef | 객체 참조 수 증가 |
| Release | 객체 참조 수 감소 |
구조를 단순화하면 다음과 같습니다.
IUnknown
│
├── IMarshal
│
├── ICreateObject
│
└── 기타 COM Interface
28. IID
IID(Interface Identifier) 는 COM 인터페이스를 식별하는 GUID입니다.
CLSID와 비교하면 다음과 같습니다.
| 구분 | 의미 |
|---|---|
| CLSID | COM Class를 식별 |
| IID | COM Interface를 식별 |
예를 들어 하나의 COM 객체가 여러 Interface를 제공할 수 있습니다.
Calculator COM Object
│
├── ICalculator
│ └── IID A
│
└── IAdvancedCalculator
└── IID B
29. IDL
IDL(Interface Definition Language) 은 RPC 또는 COM Interface의 구조를 정의하는 언어입니다.
예를 들어 다음과 같은 형태입니다.
interface ICreateObject : IUnknown
{
HRESULT Proc3(
GUID* p0,
IUnknown* p1,
GUID* p2,
IUnknown** p3
);
}
IDL에는 다음 정보가 정의됩니다.
- 어떤 함수가 존재하는지
- 어떤 파라미터를 전달하는지
- 어떤 데이터 타입을 사용하는지
- 반환값은 무엇인지
즉 RPC Interface의 설계도라고 볼 수 있습니다.
30. ICreateObject
Project Zero 글에서는 Shell Create Object Handler가 제공하는 ICreateObject Interface가 중요하게 등장합니다.
특히 다음과 같은 형태의 함수가 존재합니다.
ICreateObject::Proc3(
GUID*,
IUnknown*,
GUID*,
IUnknown**
)
여기서 두 번째 인자는 다음과 같습니다.
IUnknown*
즉 COM 객체를 인자로 전달할 수 있습니다.
이 구조 때문에 공격자는 Custom Marshaling 객체를 SYSTEM COM Server로 전달할 수 있습니다.
Attacker
│
▼
Fake IMarshal Object
│
│ IUnknown* Parameter
▼
SYSTEM
Shell Create Object Handler
│
▼
Unmarshal
│
▼
OBJREF_CUSTOM
│
▼
공격자가 선택한 CLSID
31. EOAC_NO_CUSTOM_MARSHAL
Windows에는 Custom Marshaling을 제한하기 위한 보안 옵션이 존재합니다.
EOAC_NO_CUSTOM_MARSHAL
이 옵션은 COM Process에서 Custom Marshaling을 제한하는 데 사용됩니다.
즉 보안 관점에서는 다음과 같은 의미입니다.
Custom Marshaling
│
X
EOAC_NO_CUSTOM_MARSHAL
하지만 Project Zero가 확인한 Shell Create Object Handler는 다음 상태였습니다.
CustomMarshalAllowed = True
따라서 공격자가 만든 Custom Marshaling 객체를 처리할 수 있었습니다.
32. 이번 글에서 기억해야 할 핵심 용어
| 용어 | 의미 |
|---|---|
| COM | Windows 컴포넌트 호출 시스템 |
| CLSID | COM Class 식별자 |
| IID | COM Interface 식별자 |
| COM Registration | CLSID와 DLL/EXE 연결 정보 |
| Dangling COM Registration | 등록 정보는 존재하지만 실제 DLL은 없는 상태 |
| COM Server | COM 객체를 구현 또는 실행하는 프로그램 |
| Marshaling | COM 객체를 다른 프로세스로 전달 가능한 형태로 변환 |
| Unmarshaling | 전달된 정보를 다시 COM 객체로 복원 |
| OBJREF | Marshaling된 COM 객체 참조 데이터 |
| IMarshal | Custom Marshaling을 구현하는 COM Interface |
| GetUnmarshalClass | Unmarshal에 사용할 COM Class의 CLSID 반환 |
| dllhost.exe | COM Surrogate Process |
| Shell Create Object Handler | 이번 공격에서 이용된 SYSTEM COM Object |
| RPC | 프로세스 또는 시스템 간 함수 호출 기술 |
| IUnknown | 모든 COM Interface의 기본 Interface |
33. 공격 흐름 다시 정리
지금까지의 용어를 연결하면 공격 과정은 다음과 같습니다.
1. Dangling COM Registration 발견
CLSID
│
▼
C:\ProgramData\...\missing.dll
│
└── 파일 없음
2. 공격자가 동일한 위치에 DLL 생성
CLSID
│
▼
C:\ProgramData\...\missing.dll
│
└── 공격자 DLL
3. SYSTEM Shell Create Object Handler 실행
CreateObjectTask
│
▼
SYSTEM dllhost.exe
│
▼
Shell Create Object Handler
4. 공격자가 Custom Marshaling 객체 전달
Fake COM Object
│
│ IMarshal
▼
OBJREF_CUSTOM
5. SYSTEM Process에서 Unmarshal
OBJREF_CUSTOM
│
▼
GetUnmarshalClass()
│
▼
공격자가 선택한 CLSID
6. COM Runtime이 CLSID 조회
CLSID
│
▼
Registry
│
▼
InProcServer32
7. 공격자의 DLL Load
SYSTEM dllhost.exe
│
▼
Attacker DLL
│
▼
SYSTEM 권한 코드 실행
34. 한 문장으로 정리
이번 공격은 다음과 같이 요약할 수 있습니다.
일반 사용자가 채워 넣을 수 있는 경로를 가리키는 Dangling COM Registration을 발견한 뒤, Custom COM Marshaling을 이용해 SYSTEM 권한의
Shell Create Object Handler가 해당 CLSID를 Unmarshal하도록 유도하고, 결과적으로 공격자의 DLL을 SYSTEM 프로세스에 로드시키는 권한 상승 기법입니다.
참고 자료
Google Project Zero
https://projectzero.google/2026/09/windows-dangling-com.htmlMicrosoft IMarshal
https://learn.microsoft.com/windows/win32/api/objidl/nn-objidl-imarshalMicrosoft DCOM OBJREF
https://learn.microsoft.com/openspecs/windows_protocols/ms-dcom/fe6c5e46-adf8-4e34-a8de-3f756c875f31
댓글