Windows RPC에 내장된 방어 기능
목차
이 블로그의 내용은 BlackHat USA 2023에서 처음 발표되었습니다.
MS-RPC: 공격 탐지를 위한 프로토콜 분석
이번 블로그 게시물에서는 계속해서 MS-RPC에 대해자세히 알아보겠습니다. 하지만 이번에는 공격적인 측면에 초점을 맞추고 취약점을 찾는 대신, Windows에 내장된 몇 가지 방어 기능에 대해 설명하겠습니다. 이를 활용해 RPC 내부에서 어떤 일이 벌어지고 있는지 파악하고, 그 과정에서 악성 활동을 탐지하는 방법을 살펴보겠습니다.
MS-RPC는 Windows 운영 체제에 필수적이기 때문에 결과적으로 많은 측면 이동 경로가 그 위를 지나게 됩니다. PSExec, 원격 예약 작업, DCSync, PetitPotam 같은 공격은 모두 MS-RPC 프로토콜을 통해 실행되며, 기존의 네트워크 메타데이터(소스 및 대상 포트와 IP 주소, 때로는 프로세스 정보)만으로는 정상 네트워크 트래픽과 악성 네트워크 트래픽을 구분하기 어려울 수 있습니다.
따라서 Windows 운영 체제에 내장된 추적 및 모니터링 메커니즘인 ETW(Event Tracing for Windows)를 통해 가시성을 강화하겠습니다. ETW는 특히 기존 네트워크 메타데이터와 비교할 때 RPC를 위한 정보의 보고를 제공합니다.
ETW 공급자를 사용해 RPC 이벤트를 추적하는 개념은 새로운 것이 아니며, 이를 위한 유용한 리소스와 툴이 이미 많이 있습니다( 참조 섹션에 몇 가지 링크가 있습니다. 추가로 알고 계신 것이 있다면 알려주세요). 기존 리서치는 대부분 공격자가 프로그램을 조작하거나 수정해 로깅을 완전히 우회할 수 있는 클라이언트 측 이벤트에 초점을 맞추고 있습니다. 여기서는 대신 공격자의 손이 닿지 않는 서버 측 이벤트를 사용해 공격을 탐지하는 데 초점을 맞추고, 이를 분석할 때 고려해야 할 점들을 살펴보겠습니다.
이 게시물에서는 RPC ETW 공급자의 이벤트를 소비하고 구문 분석하는 방법과 이벤트에서 잠재적인 악성 활동을 스캔하는 방법을 살펴보겠습니다. ETW 공급자를 사용하면 요청되는 작업을 정확히 확인할 수 있으므로 공격을 훨씬 더 세밀하게 탐지할 수 있습니다. 이 기능이 어떻게 작동하는지 조금 더 살펴보겠습니다.
소개: RPC란 무엇일까요??
RPC는 Remote Procedure Call의 약자로, IPC(Interprocess Communication)의 한 형태입니다. 이 프로토콜은 특히 서로 다른 프로세스 간에 원격 함수 호출을 허용하도록 설계되었습니다. 여기서는 Microsoft가 구축한 MS-RPC에 초점을 맞추겠습니다.
MS-RPC는 클라이언트-서버 모델로 설계되었습니다. 서버는 IDL(Interface Definition Language)을 사용해 노출할 인터페이스를 정의합니다. IDL 내부에는 인터페이스에 대한 UUID(Universally Unique Identifier)와 인터페이스가 노출하는 함수에 대한 함수 정의가 있습니다(그림 1).
[
uuid(12345678-4000-2006-0000-20000000001a)
]
interface Test
{
void Foo([in] int number, [in] char *message);
void Bar([out] int * result);
}
그림 1: IDL 인터페이스 정의의 예
통신은 특정 전송 프로토콜을 통해 전달되며 각 전송에는 고유한 종류의 엔드포인트가 있습니다(그림 2). 이를 설명하기 위해 다음과 같은 예를 사용하겠습니다. RPC 통신은 TCP를 통해 전달될 수 있으며, 이 경우 전송은 TCP이고 엔드포인트는 포트 번호로 식별되는 TCP 소켓입니다.
전송 |
엔드포인트 |
---|---|
TCP 명명된 파이프 UDP ALPC HTTP Hyper-V 소켓 |
<port number> <pipe name> <port number> <ALPC port> <hostname> <UUID> |
그림 2: 공통 전송 프로토콜 및 해당 엔드포인트의 종류
함수는 IDL 파일에서 읽을 수 있는 이름을 가지고 있지만 네트워크에서는 다르게 식별된다는 점에 유의해야 합니다. 문자열 이름 대신 opnum(operation number의 약어)이라는 정수로 식별됩니다. opnum은 일반적으로 IDL 인터페이스 정의에서 함수가 나타나는 순서에 따라 할당됩니다(따라서 그림 1의 예제에서 Foo는 opnum 0을 사용해 식별되고, Bar는 opnum 1이 됩니다). 이는 나중에 보게 될 데이터에서 관련 함수를 식별하기 위해 해당 함수의 opnum을 알아야 할 때 중요합니다.
MS-RPC에 대한 더 자세하고 심층적인 개요를 보려면 이전 게시물을 참조하거나 해당 주제에 대한 HexaCon 또는 DEF CON 의 컨퍼런스 프레젠테이션 중 하나를 참조하세요.
#ETW 정의
Microsoft-Windows-RPC
RPC ETW 공급자 는 RPC 런타임 rpcrt4.dll 내에 구축됩니다. 총 13개의 이벤트가 있지만, 우리가 가장 관심을 가지고 있는 것은 클라이언트 호출 시작 및 중지에 대한 이벤트 5와 7, 서버 호출 시작 및 중지에 대한 이벤트 6과 8 등 4개의 이벤트입니다. 가장 많은 정보를 제공하는 호출 시작 이벤트(그림 4)에 초점을 맞추겠습니다. (호출 중지 이벤트는 RPC 반환 상태를 알려줄 뿐입니다.) 클라이언트와 서버 이벤트 모두 동일한 형식을 공유합니다.
<template tid="RpcServerCallStartArgs_V1">
<data name="InterfaceUuid" inType="win:GUID"/>
<data name="ProcNum" inType="win:UInt32"/>
<data name="Protocol" inType="win:UInt32"/>
<data name="NetworkAddress" inType="win:UnicodeString"/>
<data name="Endpoint" inType="win:UnicodeString"/>
<data name="Options" inType="win:UnicodeString"/>
<data name="AuthenticationLevel" inType="win:UInt32"/>
<data name="AuthenticationService" inType="win:UInt32"/>
<data name="ImpersonationLevel" inType="win:UInt32"/>
</template>
그림 4: RPC ETW 호출 시작 이벤트 스키마
이론적으로 이벤트의 데이터는 RPC 트래픽에 대한 인사이트를 얻는 데 필요한 모든 메타데이터를 제공해야 합니다. 이제 인터페이스 UUID와 opnum에서 무엇이 요청되었는지 알 수 있으며, NetworkAddress 필드를 통해 소스나 대상 주소(클라이언트 또는 서버 이벤트를 보고 있는지 여부에 따라 다름)도 알 수 있습니다. 너무 간단한 것 아니냐고요?
그렇지 않습니다. RPC 런타임은 서버 호출 이벤트를 처리할 때 NetworkAddress 필드를 채우지 않기 때문에, 연결 메타데이터를 얻으려면 해당 데이터를 찾을 수 있는 다른 방법을 찾아야 합니다. 클라이언트 이벤트는 이 필드가 채워져 있습니다.
이제 다음과 같은 의문이 제기됩니다. 서버 이벤트를 무시하고 클라이언트 측에만 의존할 수 있을까요? 그 대답은 아니요입니다. 공격자가 제어하는 머신에서 발생하는 악성 동작을 찾으려는 것이기 때문에 공격자가 클라이언트 측 ETW 공급자를 변조(끄거나 이벤트를 차단)하지 않을 것이라고 확신할 수 없으며, RPC 런타임을 통과할 것이라고 확신할 수도 없습니다.
PoC(Proof of Concept)와 툴에 자주 사용되는 인기 있는 Python 라이브러리 Impacket( PSExec같은 네트워크 공격 구축이 포함되어 있음)은 내부에 RPC 트래픽을 구축하므로, 이를 사용하는 공격자는 RPC 런타임을 우회하고 ETW 공급자에 등록되지 않을 것입니다. 서버 이벤트는 공격자가 통제할 수 없어 서버 이벤트에 의존하는 것이 더 신중한 방법이므로,우리는 다른 곳에서 네트워크 데이터를 가져와 RPC 이벤트와 매칭하는 방법을 중점적으로 살펴보겠습니다.
네트워크 플로우에 RPC 이벤트 매칭
RPC ETW 공급자에서 잠시 주제를 옮겨 다른 ETW 공급자, 즉 TCP 와 SMB 공급자를 살펴보겠습니다. 이 두 프로토콜은 RPC 트래픽을 위한 일반적인 전송 프로토콜입니다. RPC 엔드포인트의 종류는 전송 프로토콜에 따라 다르므로, RPC 공급자로부터 받은 엔드포인트(포트 번호, 파이프 이름 등)를 전송 ETW 공급자의 해당 값과 일치시킬 수 있습니다.
TCP의 경우 매우 간단합니다. TCP 3자 핸드셰이크가 완료되면 트리거되는 이벤트 ID 1017, 즉 TcpAcceptListenerComplete를 살펴보겠습니다.
기본적으로 두 개의 필드, LocalAddress 와 RemoteAddress 가 있습니다(하지만 여기서는 서버 이벤트를 살펴보고 있으므로 로컬은 서버를, 원격은 클라이언트를 가리킵니다). 주소 필드의 값은 2진수이며 주소 계열, IP 주소, 포트 번호를 포함합니다(그림 6).
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
---|---|---|---|---|---|---|---|
주소 패밀리 |
포트 번호 |
주소 값(AF_INET인 경우 IP) |
그림 6: 주소 이진 필드 형식
이제 RemoteAddress 필드에서 (클라이언트의) IP 주소를 추출한 다음 LocalAddress 필드에 있는 (대상) 포트로 추적하면 됩니다. 이제 서버에서 동일한 TCP 포트 번호로 RPC 이벤트가 발생하면, TCP 공급자로부터 추출한 포트 대 IP 매칭을 기반으로 이벤트의 출처를 알 수 있습니다(그림 7).
SMB의 경우 ETW 이벤트에 따라 서로 다른 정보가 나타나기 때문에 상황이 좀 더 복잡해집니다. 엔드포인트는 파일과 동일하게 명명된 파이프지만 동일한 SMB 세션에서 여러 파일에 접속할 수 있습니다. 따라서 이 경우 엔드포인트를 네트워크 소스와 매칭하기 위해서는 실제 연결과 파일 요청이라는 두 가지 이벤트를 추적해야 합니다(그림 8).
연결 이벤트의 경우 SMB 연결이 설정될 때 트리거되는 이벤트 ID 500, Smb2ConnectionAcceptStart가 있습니다. 여기에서 소스 IP와 연결 UUID를 가져옵니다. 그런 다음 요청된 파일 이름과 동일한 연결 UUID 필드가 포함된 이벤트 ID 8, Smb2RequestCreate_V2를 찾습니다. 이제 연결 UUID를 통해 두 이벤트를 상호 참조하고 파이프 이름을 요청한 IP와 매칭하기만 하면 됩니다(파이프 이름은 나중에 RPC 호출 엔드포인트와 매칭해야 합니다).
이 모든 매칭을 직접 구축하는 번거로움을 덜기 위해 알고리즘을 구축하고 Github 리포지터리에 RPC 가시성 툴을 추가했습니다. 이 툴은 Python으로 작성되었으며, 기록된 RPC 네트워크 트래픽을 Neo4j 데이터베이스에 저장해 쉽게 시각화할 수 있습니다.
공격 탐지
이제 필요한 모든 정보를 확보했으므로 악성 RPC 플로우를 탐지하는 데 집중할 수 있습니다. 일반적인 프로세스는 간단합니다. RPC를 통해 전달되는 공격을 발견하고 PoC를 실행해 어떤 RPC 인터페이스를 사용하는지 그리고 어떤 작업이 요청되었는지 확인합니다. 그런 다음 해당 작업과 일치하는 서명을 만들 수 있습니다. 이번 릴리스에는 Neo4j 데이터베이스 구축과 함께 작동하는 서명 쿼리가 포함되었지만, 일반적인 로직과 조건은 계속 읽어보시기 바랍니다.
PSExec
PSExec은 공격 기법의 일반적인 이름이자 (이 기법에 이름을 붙인) Sysinternals 툴이기도 합니다. 기본적으로 이 툴은 서비스 바이너리를 원격 대상의 ADMIN$ 공유(Windows의 설치 폴더)에 복사한 다음, RPC 인터페이스(MS-SCMR를 통해 서비스 관리자와 통신하고 바이너리에 대한 서비스를 생성 및 실행합니다(그림 9).
원격 서비스 상태를 쿼리하는 워치독과 같이 SCMR을 통해 원격 머신에 접속해야 하는 정상적인 이유는 아주 많습니다. 그러나 새로운 서비스를 원격으로 생성해야 하는 이유는 매우 적습니다. 따라서 SCMR을 통한 모든 연결에 알림을 트리거하지 않고(들어오는 네트워크 연결을 서비스 관리자 프로세스 services.exe와 매칭하는 간단한 방법으로도 RPC ETW 없이 탐지가 가능하므로) 원격 서비스를 생성하는 연결에 대해서만 알림을 트리거하려 합니다.
따라서 서명은 (RPC 가시성 구축에 국한되지 않는 넓은 의미에서) 다음과 같아야 합니다.
interface_uuid == “367ABB81-9844-35F1-AD32-98F038001003” AND (opnum == 0xC OR opnum == 0x18)
여기서 0xC는 RCreateServiceW , 0x18은 RCreateServiceA의 opnum입니다.
원격 작업 스케줄러
PSExec과 마찬가지로 작업 스케줄러를 사용해 원격 바이너리를 실행하고 이러한 방식으로 측면 이동을 수행할 수 있습니다. 새로운 바이너리를 실행할 필요도 없습니다. cmd 또는 PowerShell 콘솔을 실행하고 온라인에서 호스팅되는 바이너리를 다운로드할 수도 있기 때문입니다.
또한 PSExec과 마찬가지로 작업 스케줄러 서비스에 대한 모든 접속을 탐지하지 않고 SchRpcRegisterTask에 대한 RPC 호출만 살펴보려 합니다.
interface_uuid == “86D35949-83C9-4044-B424-DB363231FD0C” AND opnum == 0x1
DCSync
DCSync는 또 다른 RPC 기반 공격이지만 도메인 컨트롤러를 대상으로 합니다. 이 경우 공격자는 새로운 도메인 컨트롤러인 것처럼 가장해 실제 도메인 컨트롤러에 연결합니다. 그런 다음 도메인 컨트롤러의 인증정보 데이터베이스 복제를 요청해 KRBTGT 비밀번호 해시에 대한 접속 권한을 얻습니다.
복제 요청은 MS-DRSR RPC 인터페이스를 통해 발생하며, 특정 함수인 DRSGetNCChanges (opnum 3)을 사용합니다.
interface_uuid == “e3514235-4b06-11d1-ab04-00c04fc2dcd2” AND opnum == 0x3
PetitPotam
PetitPotam 은 EFS(Encrypted File System) 서비스에 대한 인증 강제 공격입니다. 기본적으로 공격자는 EFS RPC 인터페이스( MS-EFSR)에 연결해 UNC 경로로 지정된 원격 파일을 열도록 지시할 수 있습니다. 그런 다음 공격자가 릴레이할 수 있는 인증을 통해 아웃바운드 SMB 연결을 트리거합니다.
공개된 공격 PoC는 EfsRpcOpenFileRaw (opnum 0)를 사용했으며, 이후 패치가 적용되었습니다. 이 취약점을 발견한 연구원 토포탐(Topotam)은 EfsRpcEncryptFileSrv(opnum 4) 에도 동일한 결함이 있음을 발견했습니다.
interface_uuid == “c681d488-d850-11d0-8c52-00c04fd90f7e” AND (opnum == 0x0 OR opnum == 0x4)
단점
RPC ETW 공급자로부터 수집할 수 있는 정보는 상당히 많지만, 모든 보안 요구사항을 원스톱으로 해결할 수 있는 것은 아닙니다. 각 네트워크 플로우에서 어떤 작업이 요청되었는지에 대한 데이터는 매우 중요하지만, 가장 중요한 정보인 각 요청에서 어떤 데이터가 전달되었는지는 기록되지 않습니다. 즉, ETW 공급자를 통한 측면 이동 탐지는 기존 네트워크 4튜플보다 훨씬 더 많은 컨텍스트를 제공함에도 여전히 휴리스틱 기반 접근 방식에 불과합니다.
또한 순수한 탐지 방법으로서 공격을 차단하거나 대응하는 데는 사용할 수 없습니다. Microsoft는 RPC에 대한 또 다른 기본 제공 방어 메커니즘인 Windows 방화벽의 RPC 필터를 제공합니다. 해당 필터링 메커니즘에 대한 자세한 내용과 사용 방법은 RPC(Remote Procedure Call) 필터에 대한 최종 가이드 게시물에서 확인할 수 있습니다.
요약
RPC ETW 공급자는 Windows에 새로 추가된 기능은 아니지만 네트워크 방어를 고려할 때 대부분 소홀히 다루어졌습니다. 이벤트와 상호 작용하고 이벤트를 소비하는 몇 가지 툴이 있지만, 대부분 보안 연구자를 대상으로 하며 네트워크 측면의 요소에는 비교적 초점을 맞추고 있지 않습니다.
이 게시물에서는 네트워크에서 요청된 RPC 작업에 대한 가시성을 확보하기 위해 TCP 및 SMB 공급자와 함께 RPC ETW 공급자를 사용하는 방법에 대해 설명했습니다. 이는 측면 이동에 사용될 수 있는 악성 요청을 탐지하는 데 사용 가능한 휴리스틱 기반의 접근 방식을 제공합니다.
참조
다른 보안 연구자의 RPC ETW 소비자 툴
RpcMon , CyberArk 제작
RpcInvestigator , Trail of Bits 제작
Utilizing RPC Telemetry , Jonathan Johnson of SpecterOps 제작
모든 RPC에 대한 GitHub 리포지터리 , Akamai 제작
Offensive Windows IPC Internals 2: RPC , Carsten Sandker 작성
- Windows RPC 서버의 보안 유지를 위한 권장 사항 , James Forshaw 작성