ETW 공급자에 대한 직접 커널 개체 조작(DKOM) 공격.

밤늦게 작업하면서 컴퓨터 화면을 보고 있는 남자의 옆모습 초상화

작성자

Ruben Boonen

CNE Capability Lead, Adversary Services

IBM X-Force

이 게시물에서는 IBM Security X-Force Red 공격형 해커들이 상승된 권한을 보유한 공격자가 액세스 권한을 사용하여 Windows 커널 포스트 익스플로잇(Kernel post-exploitation) 기능을 준비하는 방법을 분석합니다. 지난 몇 년 동안 공개 계정에 따르면 덜 정교한 공격자들이 이 기술을 사용하여 목표를 달성하고 있는 것으로 나타났습니다. 따라서 이 기능을 집중 조명하고 잠재적 영향에 대해 자세히 알아보는 것이 중요합니다. 특히 이 게시물에서는 커널 포스트 익스플로잇을 사용하여 ETW 센서를 차단하는 데 사용될 수 있는지 평가하고, 이를 지난 해 실제 환경에서 식별된 멀웨어와 연관 지어 살펴봅니다.

소개

시간이 지남에 따라 Windows의 보안 완화 및 탐지 원격 측정은 크게 향상되었습니다. 이러한 기능이 잘 구성된 엔드포인트 탐지 및 대응(EDR) 솔루션과 결합하면 포스트 익스플로잇을 막는 중요한 장벽이 될 수 있습니다. 공격자는 탐지 휴리스틱을 피하기 위해 전술, 기술 및 절차(TTP)를 개발하고 반복하는 데 드는 지속적인 비용에 직면합니다. IBM Security X-Force 적대 시뮬레이션 팀에서도 이와 같은 문제에 직면하고 있습니다. 저희 팀은 가장 크고 가장 강화된 환경에서 고급 위협 능력을 시뮬레이션하는 임무를 맡고 있습니다. 복잡하고 미세 조정된 보안 솔루션과 잘 훈련된 보안 운영 센터(SOC) 팀의 조합은 공격 수법(tradecraft)에 큰 부담을 줄 수 있습니다. 경우에 따라 특정 TTP의 사용이 3~4개월 내에 완전히 쓸모없게 되는 경우도 있습니다(일반적으로 특정 기술 스택과 연결됨).

공격자는 Windows 커널에서 코드 실행을 활용하여 이러한 보호 기능 중 일부를 변조하거나 여러 사용자 영역 센서를 완전히 피할 수 있습니다. 이러한 기능에 대한 첫 번째 데모는 1999년에 Phrack Magazine에 게재되었습니다. 그 후 몇 년 동안 위협 행위자(TA)가 포스트 익스플로잇을 위해 커널 루트킷을 사용한 사례가 많이 보고되었습니다. 더 오래된 예로는 Derusbi Family와 Lamberts Toolkit이 있습니다.

전통적으로 이러한 유형의 기능은 대부분 고급 TA로 제한되었습니다. 그러나 최근 몇 년 동안 더 많은 일반 공격자들이 엔드포인트에서 작업을 용이하게 하기 위해 BYOVD(Bring Your Own Vulnerable Driver) 익스플로잇 프리미티브를 사용하는 것을 목격했습니다. 어떤 경우에는 이러한 기법은 매우 원시적이고 간단한 작업에 국한되었지만, 더 뛰어난 성능의 데모도 있었습니다.

2022년 9월 말, ESET 연구진은 데이터 유출을 목적으로 벨기에와 네덜란드 기업을 대상으로 한 여러 공격에서 Lazarus TA가 사용한 이러한 커널 기능에 대한 백서를 발표했습니다. 이 문서에서는 페이로드가 OS/AV/EDR 원격 측정을 무력화하는 데 사용하는 여러 가지 직접 커널 개체 조작(DKOM) 프리미티브를 제시합니다. 이러한 기법에 대한 공개된 연구 자료는 매우 부족합니다. 커널 포스트 익스플로잇 공격 수법에 대한 보다 철저한 이해는 방어를 위해 매우 중요합니다. 상승된 권한을 가진 공격자는 무엇이든 할 수 있다는 고전적이고 순진한 주장이 자주 들리는데, 왜 그 시나리오의 기능을 모델링해야 할까요? 이는 약한 태세입니다. 방어자는 공격자가 권한이 상승했을 때 어떤 능력을 가지고 있는지, 어떤 데이터 소스가 신뢰할 수 있는지, 어떤 데이터 소스가 신뢰할 수 없는지, 어떤 억제 옵션이 있는지, 고급 기술을 어떻게 탐지할 수 있는지(해당 탐지를 수행할 수 있는 능력이 없는 경우에도) 이해해야 합니다. 이 게시물에서는 특히 ETW(Event Tracing for Windows)구조를 패치하여 공급자를 비효율적이거나 작동 불능 상태로 만드는 데 중점을 둘 것입니다. 이 기법에 대한 배경 지식을 제공하고, 공격자가 커널 ETW 구조를 조작하는 방법을 분석하고, 이러한 구조를 찾는 몇 가지 메커니즘을 살펴보겠습니다. 마지막으로, Lazarus가 페이로드에서 이 기술을 어떻게 구현했는지 검토하겠습니다.

전문가의 인사이트를 바탕으로 한 최신 기술 뉴스

Think 뉴스레터를 통해 AI, 자동화, 데이터 등 가장 중요하고 흥미로운 업계 동향에 대한 최신 소식을 받아보세요. IBM 개인정보 보호정책을 참조하세요.

감사합니다! 구독이 완료되었습니다.

구독한 뉴스레터는 영어로 제공됩니다. 모든 뉴스레터에는 구독 취소 링크가 있습니다. 여기에서 구독을 관리하거나 취소할 수 있습니다. 자세한 정보는 IBM 개인정보 보호정책을 참조하세요.

ETW DKOM

ETW는 Windows 운영 체제에 내장된 고속 추적 기능입니다. 애플리케이션, 드라이버 및 운영 체제별 이벤트 및 시스템 활동을 로깅하여 디버깅, 성능 분석 및 보안 진단을 위한 시스템 동작에 대한 자세한 가시성을 제공합니다.

이 섹션에서는 커널 ETW와 관련 공격 표면에 대한 높은 수준의 개요를 제공합니다. 이는 ETW 공급자를 조작하는 것과 관련된 메커니즘과 이러한 조작과 관련된 영향을 더 잘 이해하는 데 도움이 됩니다.

커널 ETW 공격 표면

Binarly의 연구원들은 BHEU 2021에서 Windows에서 ETW의 일반적인 공격 표면에 대해 논의했습니다. 위협 모델에 대한 개요는 아래 그림과 같습니다.

위협 모델링 ETW를 보여주는 순서도
그림 1 - Veni, No Vidi, No Vici: ETW Blind EDR 센서에 대한 공격(바이너리)

이 게시물에서는 커널 공간 공격 표면에 중점을 둡니다.

커널 모드 ETW 공급자에 대한 공격을 보여주고 설명하는 차트
그림 2 - Veni, No Vidi, No Vici: ETW Blind EDR 센서에 대한 공격(바이너리)

이 게시물에서는 "그림 2"에 표시된 첫 번째 공격 카테고리에 속하는 공격만 고려하며, 이 공격에서는 추적이 비활성화되거나 어떤 방식으로든 변경됩니다.

주의할 점은 Windows에서 불투명한 구조를 고려할 때 이러한 구조는 변경될 수 있으며 실제로 Windows 버전에서 자주 변경된다는 점을 기억하는 것이 중요합니다. 실수로 인해 블루 스크린 오브 데스(BSoD)가 발생할 가능성이 높기 때문에 커널 데이터를 클로버할 때 특히 중요합니다.

초기화

커널 공급자는 ntoskrnl에서 내보내는 함수인 nt!EtwRegister를 사용하여 등록됩니다. 이 함수의 디컴파일된 버전은 아래에서 확인할 수 있습니다.

nt!EtwRegister 디컴파일의 코드 스크린샷
그림 3 – nt!EtwRegister 디컴파일

전체 초기화는 내부 EtwpRegisterKMProvider 함수 내에서 이루어지지만, 여기에는 두 가지 주요 사항이 있습니다.

  • ProviderId는 16바이트 GUID에 대한 포인터입니다. 이 GUID는 운영 체제 전반에서 정적이므로 초기화되는 공급자를 식별하는 데 사용할 수 있습니다.
  • RegHandle은 호출 성공 시 _ETW_REG_ENTRY 구조체에 대한 포인터를 수신하는 메모리 주소입니다. Binarly의 연구에 따르면, 이러한 데이터 구조와 그 안에 포함된 일부 속성들은 ETW 제공업체를 조작할 수 있는 방법을 제공합니다.

Binarly가 그림 2의 슬라이드에서 강조한 구조를 간략하게 나열해 보겠습니다.

ETW_REG_ENTRY

_ETW_REG_ENTRY 구조의 전체 64비트 목록은 아래에 나와 있습니다. 추가된 세부 정보는 Geoff Chappell의 블로그에서 확인할 수 있습니다. 이 구조는 Vergilius 프로젝트에서도 더 깊이 탐구할 수 있습니다.

// 0x70 bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _ETW_REG_ENTRY
{
    struct _LIST_ENTRY RegList;                           //0x0
    struct _LIST_ENTRY GroupRegList;                      //0x10
    struct _ETW_GUID_ENTRY* GuidEntry;                    //0x20
    struct _ETW_GUID_ENTRY* GroupEntry;                   //0x28
    union
    {
        struct _ETW_REPLY_QUEUE* ReplyQueue;              //0x30
        struct _ETW_QUEUE_ENTRY* ReplySlot[4];            //0x30
        struct
        {
            VOID* Caller;                                 //0x30
            ULONG SessionId;                              //0x38
        };
    };
    union
    {
        struct _EPROCESS* Process;                        //0x50
        VOID* CallbackContext;                            //0x50
    };
    VOID* Callback;                                       //0x58
    USHORT Index;                                         //0x60
    union
    {
        USHORT Flags;                                     //0x62
        struct
        {
            USHORT DbgKernelRegistration:1;               //0x62
            USHORT DbgUserRegistration:1;                 //0x62
            USHORT DbgReplyRegistration:1;                //0x62
            USHORT DbgClassicRegistration:1;              //0x62
            USHORT DbgSessionSpaceRegistration:1;         //0x62
            USHORT DbgModernRegistration:1;               //0x62
            USHORT DbgClosed:1;                           //0x62
            USHORT DbgInserted:1;                         //0x62
            USHORT DbgWow64:1;                            //0x62
            USHORT DbgUseDescriptorType:1;                //0x62
            USHORT DbgDropProviderTraits:1;               //0x62
        };
    };
    UCHAR EnableMask;                                     //0x64
    UCHAR GroupEnableMask;                                //0x65
    UCHAR HostEnableMask;                                 //0x66
    UCHAR HostGroupEnableMask;                            //0x67
    struct _ETW_PROVIDER_TRAITS* Traits;                  //0x68
};

ETW_GUID_ENTRY

_ETW_REG_ENTRY 내의 중첩된 항목 중 하나는 _ETW_GUID_ENTRY 구조인 GuidEntry입니다. 이 문서화되지 않은 구조에 대한 자세한 정보는 Geoff Chappell의 블로그와 Vergilius Project에서 확인할 수 있습니다.

// 0x1a8 bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _ETW_GUID_ENTRY
{
    struct _LIST_ENTRY GuidList;                          //0x0
    struct _LIST_ENTRY SiloGuidList;                      //0x10
    volatile LONGLONG RefCount;                           //0x20
    struct _GUID Guid;                                    //0x28
    struct _LIST_ENTRY RegListHead;                       //0x38
    VOID* SecurityDescriptor;                             //0x48
    union
    {
        struct _ETW_LAST_ENABLE_INFO LastEnable;          //0x50
        ULONGLONG MatchId;                                //0x50
    };
    struct _TRACE_ENABLE_INFO ProviderEnableInfo;         //0x60
    struct _TRACE_ENABLE_INFO EnableInfo[8];              //0x80
    struct _ETW_FILTER_HEADER* FilterData;                //0x180
    struct _ETW_SILODRIVERSTATE* SiloState;               //0x188
    struct _ETW_GUID_ENTRY* HostEntry;                    //0x190
    struct _EX_PUSH_LOCK Lock;                            //0x198
    struct _ETHREAD* LockOwner;                           //0x1a0
};

TRACE_ENABLE_INFO

마지막으로 _ETW_GUID_ENTRY 내에 중첩된 항목 중 하나는 _TRACE_ENABLE_INFO 구조인 ProviderEnableInfo입니다. 이 데이터 구조의 요소에 대한 자세한 내용은 마이크로소프트 공식 문서Vergilius 프로젝트를 참고할 수 있습니다. 이 구조의 설정은 공급자의 작동 및 능력에 직접적인 영향을 미칩니다.

// 0x20 bytes (sizeof)
// Win11 22H2 10.0.22621.382
struct _TRACE_ENABLE_INFO
{
    ULONG IsEnabled;                                       //0x0
    UCHAR Level;                                           //0x4
    UCHAR Reserved1;                                       //0x5
    USHORT LoggerId;                                       //0x6
    ULONG EnableProperty;                                  //0x8
    ULONG Reserved2;                                       //0xc
    ULONGLONG MatchAnyKeyword;                             //0x10
    ULONGLONG MatchAllKeyword;                             //0x18
};

등록 핸들 사용법 이해

어느 정도의 이론적 배경도 좋지만, 주제를 더 깊이 이해하려면 구체적인 사용 사례를 살펴보는 것이 항상 가장 좋습니다. 예를 간략하게 살펴보겠습니다. 대부분의 중요한 커널 ETW 공급자는 내보내지지 않는 nt!EtwpInitialize 내에서 초기화됩니다. 이 함수 내부를 살펴보면 약 15개의 공급업체가 표시됩니다.

nt!EtwpInitialize 부분 디컴파일의 코드 스크린샷
그림 4 – nt!EtwpInitialize 부분 디컴파일

Microsoft-Windows-Threat-Intelligence(EtwTi) 항목을 예로 들면, 전역 ThreatIntProviderGuid 매개변수를 확인하여 이 공급자의 GUID를 복구할 수 있습니다.

EtwTi 공급자 GUID의 코드 스크린샷
그림 5 - EtwTi 공급자 GUID

이 GUID를 온라인으로 검색하여 올바른 값(f4e1897c-bb5d-5668-f1d8-040f4d8dd344)을 복구할 수 있었습니다.

등록 핸들 매개변수인 EtwThreatIntProvRegHandle이 사용되는 경우를 살펴보고 어떻게 사용되는지 분석해 보겠습니다. 핸들이 참조되는 한 곳은 nt!EtwTiLogDriverObjectUnLoad입니다. 이 함수의 이름에서 알 수 있듯이, 커널에 의해 드라이버 오브젝트가 언로드될 때 이벤트를 생성하기 위한 것임을 알 수 있습니다.

nt!EtwTiLogDriverUnload 디컴파일의 코드 스크린샷
그림 6 – nt!EtwTiLogDriverUnload 디컴파일

여기에서는 nt!EtwEventEnabled 및 nt!EtwProviderEnabled 함수가 모두 호출되어 등록 핸들을 인수 중 하나로 전달합니다. 이러한 하위 함수 중 하나를 살펴보고 무슨 일이 일어나는지 자세히 살펴보겠습니다.

nt!EtwProviderEnable 디컴파일의 코드 스크린샷
그림 7 – nt!EtwProviderEnable 디컴파일

이번 예시는 따라하기가 조금 어렵습니다. 그러나 포인터 산술은 특별히 중요하지 않습니다. 대신, 이 함수가 등록 핸들을 처리하는 방식을 집중으로 보겠습니다. 이 함수는 _ETW_REG_ENTRY 구조와 GuidEntry 속성과 같은 하위 구조의 여러 속성의 검증하는 것으로 보입니다.

struct _ETW_REG_ENTRY
{
    …
    struct _ETW_GUID_ENTRY* GuidEntry;                    //0x20
    …
}

그리고 GuidEntry->ProviderEnableInfo 속성이 있습니다.

struct _ETW_GUID_ENTRY
{
    …
    struct _TRACE_ENABLE_INFO ProviderEnableInfo;         //0x60
    …
}

그런 다음, 이 함수는 유사한 레벨 기반 검사로 이동합니다. 마지막으로, 이 함수는 true 또는 false를 반환하여 공급자가 지정된 수준 및 키워드에서 이벤트 로깅을 사용할 수 있는지 여부를 나타냅니다. 자세한 내용은 Microsoft의 공식 문서에서 확인할 수 있습니다.

공급자에 등록 핸들을 통해 접근할 때 해당 구조의 무결성이 공급자의 운영에 매우 중요해진다는 것을 알 수 있습니다. 반대로 공격자가 이러한 구조를 조작할 수 있는 경우 호출자의 제어 흐름에 영향을 주어 이벤트가 기록되는 것을 삭제하거나 제거할 수 있습니다.

등록 핸들 공격

Binarly의 공격 표면을 되돌아보고 가벼운 분석을 바탕으로 이벤트 수집을 방해하는 몇 가지 전략을 세울 수 있습니다.

  • 공격자는 _ETW_REG_ENTRY 포인터를 NULL로 설정할 수 있습니다. 그러면 등록 핸들을 참조하는 모든 함수는 공급자가 초기화되지 않았다고 가정합니다.
  • 공격자는 _ETW_REG_ENTRY ->GuidEntry->ProviderEnableInfo 포인터를NULL로 설정할 수 있습니다. ProviderEnableInfo는 공급자의 작동 방식을 설명하는 _TRACE_ENABLE_INFO 구조에 대한 포인터이므로 공급자의 수집 기능을 효과적으로 비활성화해야 합니다.
  • 공격자는 _ETW_REG_ENTRY->GuidEntry->ProviderEnableInfo 데이터 구조 의 속성을 덮어써 공급자의 구성을 조작할 수 있습니다.
    • IsEnabled: 공급자로부터 이벤트 수신을 활성화하거나 공급자로부터 이벤트를 수신할 때 사용되는 설정을 조정하려면 1로 설정합니다. 공급자로부터 이벤트 수신을 비활성화하려면 0으로 설정합니다.
    • Level: 공급자가 기록할 이벤트의 최대 수준을 나타내는 값입니다. 공급자는 일반적으로 이벤트의 수준이 이 값보다 작거나 같으면 MatchAnyKeyword 및 MatchAllKeyword 기준을 충족할 뿐만 아니라 이벤트를 작성합니다.
    • MatchAnyKeyword: 공급자가 작성하려는 이벤트의 범주를 결정하는 키워드의 64비트 비트 마스크입니다. 공급자는 일반적으로 이벤트의 키워드 비트가 이 값에 설정된 아무 비트와 일치하거나 이벤트에 키워드 비트가 설정되지 않고 Level 및 MatchAllKeyword 기준을 충족하는 경우 이벤트를 작성합니다.
    • MatchAllKeyword: 공급자가 작성하도록 하려는 이벤트를 제한하는 키워드의 64비트 비트 마스크입니다. 공급자는 일반적으로 이벤트의 키워드 비트가 이 값에 설정된 모든 비트와 일치하거나 이벤트에 키워드 비트가 설정되지 않고 Level 및 MatchAnyKeyword 기준을 충족하는 경우 이벤트를 작성합니다.

커널 검색 공격 수법

이제 ETW에 대한 DKOM 공격이 어떤 모습인지 잘 알 수 있습니다. Lazarus 멀웨어가 취약한 드라이버를 로드하여 커널 읽기/쓰기 프리미티브를 부여하는 취약점이 공격자에게 있다고 가정해 보겠습니다. 문제는 이러한 등록 핸들을 찾는 방법이 없다는 것입니다.

이러한 핸들을 찾는 두 가지 주요 기법을 간략하게 설명하고 Lazarus가 커널 페이로드에서 사용하는 변형을 보여 드리겠습니다.

중간 무결성 수준(MedIL) KASLR 바이패스

첫째, 커널 ASLR이 있긴 하지만 로컬 공격자가 MedIL 이상의 코드를 실행할 수 있다면 이는 보안 경계가 아니라는 점을 설명하는 것이 현명할 수 있습니다. 샌드박스 또는 LowIL 시나리오에서만 제한되는 커널 포인터를 유출하는 방법은 여러 가지가 있습니다. 배경 설명을 위해 Alex Ionescu의 I Got 99 Problems But a Kernel Pointer Ain’t One을 참고할 수 있습니다. 이 기법들 중 많은 부분이 오늘날에도 여전히 적용 가능합니다.

여기서 선택한 도구는 SystemModuleInformation 클래스를 사용하는 ntll!NtQuerySystemInformation입니다.

internal static UInt32 SystemModuleInformation = 0xB;

[DllImport(“ntdll.dll”)]
internal static extern UInt32 NtQuerySystemInformation(
    UInt32 SystemInformationClass,
    IntPtr SystemInformation,
    UInt32 SystemInformationLength,
    ref UInt32 ReturnLength);

이 함수는 커널 공간에 로드된 모든 모듈의 라이브 베이스 주소를 반환합니다. 이 시점에서 디스크에서 해당 모듈을 구문 분석하고 원시 파일 오프셋을 상대 가상 주소로 또는 그 반대로 변환할 수 있습니다.

public static UInt64 RvaToFileOffset(UInt64 rva, List<SearchTypeData.IMAGE_SECTION_HEADER> sections)
{
    foreach (SearchTypeData.IMAGE_SECTION_HEADER section in sections)
    {
        if (rva >= section.VirtualAddress && rva < section.VirtualAddress + section.VirtualSize)
        {
            return (rva – section.VirtualAddress + section.PtrToRawData);
        }
    }
    return 0;
}

public static UInt64 FileOffsetToRVA(UInt64 fileOffset, List<SearchTypeData.IMAGE_SECTION_HEADER> sections)
{
    foreach (SearchTypeData.IMAGE_SECTION_HEADER section in sections)
    {
        if (fileOffset >= section.PtrToRawData && fileOffset < (section.PtrToRawData + section.SizeOfRawData))
        {
            return (fileOffset – section.PtrToRawData) + section.VirtualAddress;
        }
    }
    return 0;
}

공격자는 표준 로드 라이브러리 API 호출(예: ntll!LdrLoadDll)을 사용하여 이러한 모듈을 사용자 영역 프로세스에 로드할 수도 있습니다. 이렇게 하면 파일 오프셋을 RVA로 변환하고 그 반대로 변환하는 복잡한 작업을 피할 수 있습니다. 그러나 운영 보안(OpSec) 관점에서 이는 더 많은 탐지 원격 측정을 생성할 수 있으므로 이상적이지 않습니다.

방법 1: 가젯 체인

가능한 경우 저는 이 기법을 선호합니다. 패치 변경의 영향을 덜 받기 때문에 모듈 버전 간에 누수를 더 쉽게 이식할 수 있기 때문입니다. 단점은 누출시키려는 대상에 대한 가젯 체인(gadget chain)이 이미 존재해야 한다는 점입니다.

ETW 등록 핸들을 고려하여 Microsoft-Windows-Threat-Intelligence를 예로 들어보겠습니다. 아래에서 nt!EtwRegister에 대한 전체 호출을 확인할 수 있습니다.

nt!EtwRegister 전체 CALL 디스어셈블리의 코드 스크린샷
그림 8 – nt!EtwRegister 전체 CALL 디스어셈블리

여기에서는 등록 핸들인EtwThreatIntProvRegHandle에 대한 포인터를 유출하려고 합니다. 그림 8의 첫 번째 줄에 있는 param_4에 로드된 것과 같습니다. 이 포인터는 커널 모듈의 .data 섹션 내의 전역 변수를 가리킵니다. 이 호출은 내보내지 않은 함수에서 발생하므로 주소를 직접 유출할 수 없습니다. 대신 이 전역이 참조되는 위치를 살펴보고 주소가 유출될 수 있는 함수에서 사용되는지 확인해야 합니다.

nt!EtwThreatIntProvRegHandle 참조의 코드 스크린샷
그림 9 - nt!EtwThreatIntProvRegHandle 참조

이러한 항목 중 일부를 탐색하면 nt!KeInsertQueueAPC의 후보자를 빠르게 알 수 있습니다.

nt!KeInsertQueueApc partial 부분 디컴파일의 코드 스크린샷
그림 10 - nt!KeInsertQueueAPC 부분 디컴파일

이는 몇 가지 이유로 훌륭한 후보입니다.

  • nt!KeInsertQueueAPC는 내보낸 함수입니다. 즉, KASLR 바이패스를 사용하여 라이브 주소를 유출할 수 있습니다. 그러면 우리는 커널 취약점을 이용하여 해당 주소의 데이터를 읽을 수 있습니다.
  • global은 함수를 시작할 때 사용됩니다. 이는 이를 찾기 위해 복잡한 명령어 구문 분석 로직을 구성할 필요가 없다는 것을 의미하므로 매우 유용합니다.

어셈블리를 보면 다음과 같은 레이아웃이 보입니다.

nt!KeInsertQueueApc 부분 디스어셈블리의 코드 스크린샷
그림 11 - nt!KeInsertQueueAPC 부분 디스어셈블리

그러면 해당 등록 핸들을 유출하는 것은 간단해집니다. 취약점을 사용하여 바이트 배열을 읽고 첫 번째 mov R10 명령어를 검색하여 전역 변수의 상대적 가상 오프셋을 계산합니다. 계산은 대략 다음과 같습니다.

Int32 pOffset = Marshal.ReadInt32((IntPtr)(pBuff.ToInt64() + i + 3));
hEtwTi = (IntPtr)(pOffset + i + 7 + oKeInsertQueueApc.pAddress.ToInt64());

등록 핸들을 사용하면 _ETW_REG_ENTRY 데이터 구조에 액세스할 수 있습니다.

일반적으로 이러한 가젯 체인은 다양한 커널 데이터 구조를 유출하는 데 사용될 수 있습니다. 하지만 이러한 가젯 체인을 항상 찾을 수 있는 것은 아니며, 때로는 복잡한 단계로 나뉘기도 한다는 점을 지적할 필요가 있습니다. 예를 들어, 페이지 디렉토리 항목(PDE) 상수를 유출할 수 있는 가젯 체인은 다음과 같습니다.

MmUnloadSystemImage -> MiUnloadSystemImage -> MiGetPdeAddress

실제로 ETW 등록 핸들을 개략적으로 분석한 결과, 대부분의 경우 위에서 설명한 대로 사용할 수 있는 적절한 가젯 체인이 없는 것으로 나타났습니다.

방법 2: 메모리 스캔

이러한 ETW 등록 핸들을 유출하는 또 다른 주요 옵션은 라이브 커널 메모리 또는 디스크의 모듈에서 메모리 스캔을 사용하는 것입니다. 디스크에서 모듈을 스캔할 때 파일 오프셋을 RVA로 변환할 수 있다는 점을 기억하세요.

이 접근 방식은 고유한 바이트 패턴을 식별하고, 해당 패턴을 스캔하고, 마지막으로 패턴 일치의 오프셋에서 일부 작업을 수행하는 것으로 구성됩니다. 이를 더 잘 이해하기 위해 nt!EtwpInitialize를 다시 살펴보겠습니다.

nt!EtwpInitialize 부분 디컴파일의 코드 스크린샷
그림 12 - nt!EtwpInitialize 부분 디컴파일

nt!EtwRegister에 대한 15개의 호출 모두는 대부분 이 함수에서 함께 묶입니다. 여기서 주요 전략은 nt!EtwRegister에 대한 첫 번째 호출 전에 나타나는 고유한 패턴과 nt!EtwRegister에 대한 마지막 호출 후에 나타나는 두 번째 패턴을 찾는 것입니다. 이건 그다지 복잡하지 않습니다. 이식성을 높이기 위해 사용할 수 있는 한 가지 방법은 와일드카드 바이트 문자열을 처리할 수 있는 패턴 스캐너를 만드는 것입니다. 이는 독자에게 맡겨진 과제입니다.

시작 및 중지 인덱스가 식별되면 그 사이의 모든 지침을 살펴볼 수 있습니다.

  • 잠재적 CALL 명령어는 CALL의 연산 코드인 0xe8을 기반으로 식별할 수 있습니다.
  • 그런 다음, DWORD 크기의 읽기를 사용하여 잠재적인 CALL 명령어의 상대 오프셋을 계산합니다.
  • 그 다음에는 이 오프셋을 CALL의 상대 주소에 더하고 5씩 증분합니다(어셈블리 명령어의 크기).
  • 마지막으로, 이 새 값을 nt!EtwRegister와 비교하여 모든 유효한 CALL 위치를 찾을 수 있습니다.

모든  CALL 명령어가 발견되면 역방향으로 검색하여 함수 인수를 추출할 수 있습니다. 첫 번째는 ETW 공급자를 식별하는 GUID이며, 두 번째는 등록 핸들의 주소입니다. 이 정보를 바탕으로 등록 핸들에 대해 정보에 입각한 DKOM 공격을 수행하여 식별된 공급자의 운영에 영향을 미칠 수 있습니다.

Lazarus ETW 패치

ESET 백서에 언급된 FudModle DLL 샘플을 확보하여 분석해 보았습니다. 이 DLL은 인라인 XOR로 인코딩된 리소스에서 서명된 취약한 Dell 드라이버를 로드한 후 호스트에서 원격 측정을 제한하기 위해 드라이버가 많은 커널 구조를 패치하도록 파일럿합니다.

Lazarus FudModule 해시의 코드 스크린샷
그림 13 - Lazarus FudModule 해시

이 게시물의 마지막 부분에서는 Lazarus가 커널 ETW 등록 핸들을 찾는 데 사용하는 전략을 검토하고자 합니다. 이는 위에서 설명한 스캔 방법의 변형입니다.

검색 함수가 시작될 때 Lazarus는 nt!EtwRegister를 확인하고 이 주소를 사용하여 스캔을 시작합니다.

Lazarus FudModule 부분 ETW 검색 디컴파일의 코드 스크린샷
그림 14 - Lazarus FudModule 부분 ETW 검색 디컴파일

이 결정은 함수가 호출되는 위치와 관련하여 해당 함수가 존재하는 위치에 따라 달라지기 때문에 다소 이상합니다. 모듈에서 함수의 상대적 위치는 새 코드가 도입, 제거 또는 변경될 수 있으므로 버전마다 다를 수 있습니다. 그러나 모듈이 컴파일되는 방식 때문에 함수는 비교적 안정적인 순서를 유지할 것으로 예상됩니다. 검색 속도 최적화를 위한 것으로 추정됩니다.

ntoskrnl에서 nt!EtwRegister에 대한 참조를 찾을 때 이 기법을 사용하여 누락된 항목이 많지 않은 것으로 보입니다. Lazarus는 누락된 항목이 중요하지 않거나 패치가 필요하지 않다고 판단하기 위해 추가 분석을 수행했을 수도 있습니다. 누락된 항목은 아래에 강조 표시되어 있습니다. 이 전략을 사용하면 Lazarus가 스캔을 수행하는 동안 0x7b1de0 바이트를 건너뛸 수 있는데, 이는 스캐너가 느린 경우 상당한 양이 될 수 있습니다.

nt!EtwRegister에 대한 호출 인스턴스의 코드 스크린샷
그림 15 - nt!EtwRegister에 대한 호출 인스턴스

또한 스캔을 시작할 때 등록 핸들을 기록하기 전에 처음 다섯 개의 일치 항목을 건너뜁니다. 검색 함수의 일부가 아래에 나와 있습니다.

Lazarus FudModule 부분 ETW 검색 디컴파일의 코드 스크린샷
그림 16 - Lazarus FudModule 부분 ETW 검색 디컴파일

코드가 약간 난해하지만 줄거리의 주요 내용을 파악할 수 있습니다. 이 코드는 nt!EtwRegister에 대한 호출을 찾고, 등록 핸들을 추출하고, KASLR 바이패스를 사용하여 이 핸들을 라이브 주소로 변환하고, 멀웨어 구성 구조(초기화 시 할당됨) 내에서 이 목적을 위해 따로 설정된 배열에 포인터를 저장합니다.

마지막으로, Lazarus가 이러한 공급자를 비활성화하기 위해 무엇을 하는지 살펴보겠습니다.

Lazarus FudModule NULL ETW 등록 핸들의 코드 스크린샷
그림 17 - Lazarus FudModule NULL ETW 등록 핸들

이는 대부분 의미가 있습니다. 여기서 Lazarus가 하는 일은 앞서 본 전역 변수를 유출한 다음 해당 주소의 포인터를 NULL로 덮어쓰는 것입니다. 이렇게 하면 _ETW_REG_ENTRY 데이터 구조에 대한 참조가 존재하는 경우 이를 효과적으로 삭제합니다.

저는 다음의 몇 가지 이유로 표시된 공격 수법에 완전히 만족하지 않습니다.

  • 페이로드는 공급자 GUID를 캡처하지 않으므로 공급자 등록 핸들을 덮어써야 하는지 여부에 대한 현명한 결정을 내릴 수 없습니다.
  • 스캔의 오프셋이 ntoskrnl의 버전에 따라 다를 수 있기 때문에 ntoskrnl 내부의 오프셋에서 스캔을 시작하기로 한 결정은 의심스러운 것 같습니다.
  • 처음 5개 일치를 임의로 건너뛰는 것도 마찬가지로 의심스러워 보입니다. 이러한 결정에는 전략적 이유가 있을 수 있지만, 먼저 모든 공급자를 수집한 다음 프로그래밍 로직을 사용하여 결과를 필터링하는 것이 더 나은 접근 방식입니다.
  • _ETW_REG_ENTRY에 대한 포인터를 덮어쓰면 작동하지만 이 기술은 다소 분명합니다. _ETW_REG_ENTRY_ETW_GUID_ENTRY 또는 _TRACE_ENable_INFO의 속성을 덮어쓰는 것이 좋습니다.

저는 이 기법을 과학 연구에 다시 적용했지만, 공격 수법에 약간의 조정을 가했습니다.

  • 속도에 최적화된 검색 알고리즘을 사용하여 ntoskrnl에서 모든 0xe8 바이트를 찾습니다.
  • 그런 다음, 몇 가지 사후 처리를 수행하여 해당 명령어 중 유효한 CALL 명령어와 해당 목적지를 결정합니다.
  • 함수가 등록 핸들에 대한 동적 인수와 함께 호출되는 경우가 있기 때문에 nt!EtwRegister에 대한 모든 호출이 유용한 것은 아닙니다. 이 때문에 나머지 호출을 필터링하려면 몇 가지 추가 로직이 필요합니다.
  • 마지막으로 모든 GUID는 사람이 읽을 수 있는 형식으로 확인되고 등록 핸들이 열거됩니다.

전반적으로 조정 후 위의 기법이 이러한 유형의 열거를 수행하는 가장 좋은 방법임이 분명합니다. 최적화된 알고리즘을 사용하면 검색 시간이 무시할 수 있는 수준이므로 디스크의 전체 모듈을 스캔한 다음 몇 가지 추가 스캔 후 로직을 사용하여 결과를 필터링하는 것이 좋습니다.

ETW DKOM 영향

이러한 공격이 얼마나 큰 영향을 미칠 수 있는지 간략하게 평가하는 것이 좋습니다. 공급자 데이터가 완전히 줄어들거나 제거되면 정보가 손실되지만, 동시에 모든 공급자가 보안에 민감한 이벤트를 알리는 것은 아닙니다.

그러나 이러한 제공업체 중 일부 하위 집합은 보안에 민감합니다. 가장 확실한 예로는 현재 Defender for Endpoint(매우 혼란스러운)라고 불리는 Microsoft Defender Advanced Threat Protection(MDATP)의 핵심 데이터 소스인 Microsoft-Windows-Threat-Intelligence(EtwTi)가 있습니다. 이 제공업체에 대한 액세스는 엄격하게 제한되어 있으며, 맬웨어 조기 실행(ELAM) 드라이버만 이 제공업체에 등록할 수 있습니다. 마찬가지로 이러한 이벤트를 수신하는 사용자 랜드 프로세스는 보호 상태(ProtectedLight / Antimalware)여야 하며 ELAM 드라이버와 동일한 인증서로 서명되어야 합니다.

EtwExplorer를 사용하면 이 제공업체가 어떤 유형의 정보를 알릴 수 있는지 더 잘 파악할 수 있습니다.

ETW 탐색기 파일 페이지의 스크린샷
그림 18 — ETW 익스플로러

XML 매니페스트는 너무 커서 여기에 전체를 포함할 수 없지만, 아래에는 DKOM을 사용하여 표시하지 않을 수 있는 데이터 유형에 대한 아이디어를 제공하는 하나의 이벤트가 나와 있습니다.

EtwTi 부분 XML 매니페스트의 코드 스크린샷
그림 19 - EtwTi 부분 XML 매니페스트

결론

커널은 Microsoft 및 타사 제공업체가 운영 체제의 무결성을 보호하기 위해 노력해야 하는 중요하고 논쟁의 여지가 있는 영역이었으며 앞으로도 그럴 것입니다. 커널 내 데이터 손상은 단순한 악용 후 특징일 뿐만 아니라 커널 익스플로잇 개발의 핵심 요소이기도 합니다. Microsoft는 이미 가상화 기반 보안(VBS)과 그 구성 요소 중 하나인 커널 데이터 보호(KDP)를 도입하여 이 분야에서 많은 진전을 이루었습니다.

Windows 운영 체제의 소비자는 이러한 발전을 활용하여 공격자가 될 수 있는 사람에게 가능한 한 많은 비용을 부과해야 합니다. Windows Defender 애플리케이션 제어(WDAC)는 VBS 안전장치가 시행되고 잠재적으로 위험한 드라이버를 로드하는 것을 금지하는 정책이 존재하는지 확인하는 데 사용할 수 있습니다.

이러한 노력은 커널 공간에서 DKOM을 수행하기 위해 BYOVD 공격을 활용하는 상용 TA가 점점 더 많아지면서 더욱 중요해지고 있습니다.

 

Mixture of Experts | 12월 12일, 에피소드 85

AI 디코딩: 주간 뉴스 요약

세계적인 수준의 엔지니어, 연구원, 제품 리더 등으로 구성된 패널과 함께 불필요한 AI 잡음을 차단하고 실질적인 AI 최신 소식과 인사이트를 확인해 보세요.

추가 참조 자료

  • Veni, No Vidi, No Vici: ETW 블라인드 EDR 센서에 대한 공격(BHEU 2021 슬라이드) – 여기
  • Veni, No Vidi, No Vici: ETW Blind EDR 센서에 대한 공격(BHEU 2021 비디오) – 여기
  • Windows 보안 발전(BlueHat Shanghai 2019) – 여기
  • '간단한' 취약점 악용 - 35단계 이하로 쉽게 익히기! - 여기
  • '간단한' 취약점 악용 - 1.5부 - 정보 유출 - 여기
  • 위협 인텔리전스 ETW 소개 - 여기
  • TelemetrySourcerer - 여기
  • WDAC 정책 마법사 - 여기

여기에서 X-Force Red에 대해 자세히 보세요. 여기에서 X-Force와 무료 상담을 예약하세요.

관련 솔루션
엔터프라이즈 보안 솔루션

최대 규모 엔터프라이즈 보안 제공업체의 솔루션으로 보안 프로그램을 혁신하세요.

사이버 보안 솔루션 살펴보기
사이버 보안 서비스

사이버 보안 컨설팅, 클라우드 및 관리형 보안 서비스를 통해 비즈니스를 혁신하고 위험을 관리하세요.

    사이버 보안 서비스 살펴보기
    인공 지능(AI) 사이버 보안

    AI 기반 사이버 보안 솔루션으로 보안팀의 속도, 정확성, 생산성을 향상시키세요.

    AI 사이버 보안 살펴보기
    다음 단계 안내

    데이터 보안, 엔드포인트 관리, ID 및 액세스 관리(IAM) 솔루션 등 어떤 솔루션이 필요하든 IBM의 전문가들이 협력하여 엄격한 보안 태세를 갖추도록 도와드립니다.사이버 보안 컨설팅, 클라우드, 관리형 보안 서비스 분야의 글로벌 리더와 협력하여 기업을 혁신하고 리스크를 관리하세요.

    사이버 보안 솔루션 살펴보기 사이버 보안 서비스 알아보기