欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

深入剖析线程与进程句柄泄露漏洞(上)

来源:本站整理 作者:佚名 时间:2019-08-29 TAG: 我要投稿

多年来,笔者曾经遇到并利用过一些句柄泄露漏洞。当然,这些过程也特别有趣,因为并不是所有的句柄都被授予了`PROCESS_ALL_ACCESS`或`THREAD_ALL_ACCESS`权限,所以,要想顺利利用,还是要开动脑筋的。在这篇文章中,我们将为读者介绍句柄的各种访问权限,以及如何利用这些权限来实现代码执行。在这里为,我们将重点关注进程和线程句柄,因为这些是最常见的,当然,其他对象的句柄也可以以类似的方式加以利用。
虽然这种漏洞可能在各种情况下发生,但我遇到的最常见的情形是,当某个特权进程打开一个句柄,并将`bInheritHandle`设置为true时,就会出现该漏洞。一旦发生这种情况,该特权进程的所有子进程都会继承句柄及其授予的所有访问权限。例如,假设一个SYSTEM级的进程执行以下操作:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, GetCurrentProcessId());
由于它允许继承已经打开的句柄,所以任何子进程都可以访问该句柄。如果它们执行了模拟桌面用户的用户态(userland)代码——像服务经常做的那样,那么这些用户态进程将获得访问该句柄的权限。
已发现的漏洞
下面,我们列举几个已经公开的漏洞实例。例如,James Forshaw[0]在2016年就曾经利用过一个从具有`THREAD_ALL_ACCESS`访问权限的辅助登录服务中泄漏的特权线程的句柄。实际上,这是一种最“常见”的权限,但他却以一种当时我并不了解的新颖方式利用了它。
另一个是来自Ivan Fratric[1] 的例子,他曾经利用过一个被泄漏的、具有`PROCESS_DUP_HANDLE`权限的进程句柄。在他发表的“Bypassing Mitigations by Attacking JIT Server in Microsoft Edge”白皮书中,他指出JIT服务器进程会将内存映射到内容进程(content process)。为此,JIT进程需要用到一个句柄。内容进程将使用`PROCESS_DUP_HANDLE`来调用自身的`DuplicateHandle`,攻击者可以利用这一点来获取具有全部访问权限的句柄。
最近的一个例子是戴尔LPE [2],其中从特权进程获得了一个具有“THREAD_ALL_ACCESS”权限的句柄。攻击者能够通过下载的DLL和APC来利用该漏洞。
搭建测试环境
在这篇文章中,我想考察句柄所有可能的访问权限,以确定哪些权限是可以利用的,哪些权限是无法利用的。对于那些无法利用的权限,我会设法弄清楚需要结合哪些权限,才能正常加以利用。
为了完成相应的测试,我创建了一个简单的客户端和服务器:一个泄漏句柄的特权服务器和一个能够使用它的客户端。下面是服务器的代码:
#include "pch.h"
#include
#include
 
int main(int argc, char **argv)
{
    if (argc
在上面代码中,我获取了要模拟的令牌的句柄,打开了当前进程(以SYSTEM权限运行)的可继承句柄,然后派生了一个子进程。实际上,这个子进程就是客户端应用程序,它将尝试利用该句柄来完成相应的攻击。
当然,本文更多涉及到的还是客户端。接下来,我们先介绍如何获取泄漏的句柄。实际上,这一步可以通过`ntQuerySystemInformation`来完成,并且无需任何特权:
void ProcessHandles()
{
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    _NtQuerySystemInformation NtQuerySystemInformation =
        (_NtQuerySystemInformation)GetProcAddress(hNtdll, "NtQuerySystemInformation");
    _NtDuplicateObject NtDuplicateObject =
        (_NtDuplicateObject)GetProcAddress(hNtdll, "NtDuplicateObject");
    _NtQueryObject NtQueryObject =
        (_NtQueryObject)GetProcAddress(hNtdll, "NtQueryObject");
    _RtlEqualUnicodeString RtlEqualUnicodeString =
        (_RtlEqualUnicodeString)GetProcAddress(hNtdll, "RtlEqualUnicodeString");
    _RtlInitUnicodeString RtlInitUnicodeString =
        (_RtlInitUnicodeString)GetProcAddress(hNtdll, "RtlInitUnicodeString");
 
    ULONG handleInfoSize = 0x10000;
    NTSTATUS status;
    PSYSTEM_HANDLE_INFORMATION phHandleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize);
    DWORD dwPid = GetCurrentProcessId();
 
    printf("[+] Looking for process handles...\n");
 
    while ((status = NtQuerySystemInformation(
        SystemHandleInformation,
        phHandleInfo,
        handleInfoSize,
        NULL
    )) == STATUS_INFO_LENGTH_MISMATCH)
        phHandleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(phHandleInfo, handleInfoSize *= 2);
 
    if (status != STATUS_SUCCESS)
    {
        printf("NtQuerySystemInformation failed!\n");
        return;
    }
 
    printf("[+] Fetched %d handles\n", phHandleInfo->HandleCount);
 
    // iterate handles until we find the privileged process
    for (int i = 0; i HandleCount; ++i)

[1] [2] [3] [4]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载