为什么您不应该始终信任MSDN:查找句柄所需的实际访问权限

翻译文章:https://rayanfam.com/topics/finding-the-real-access-rights-needed-by-handles/

原作者:Mohammad Sina Karvandi

翻译:Lonely

提示:文章为机器翻译+人工修正,免不了有疏漏的地方。

介绍

嗨,大家好,

该主题的标题有些奇怪,如果您认为MSDN中的所有内容都与MiC++rosoft在Windows中实现的功能(如我以前所认为的)100%匹配,那么您肯定是错误的,本文显示了一些证明,最后一部分,我将为您解决ACCESS_RIGHTS问题。

在开始之前,让我们谈谈“ ACCESS_MASK ”的一些背景。

背景资料

所述 ACCESS_MASK 数据类型是一个 DWORD 值,该值定义了标准,特定和通用权利。这些权限用在 访问控制项 (ACE)中,并且是指定请求或授予对对象的访问权限的主要方法。

安全对象是可以具有安全描述符的对象。所有命名的Windows对象都是安全的。一些未命名的对象(例如进程和线程对象)也可以具有安全描述符。对于大多数安全对象,可以在创建对象的函数调用中指定对象的安全描述符。例如,您可以在CreateFile 和 CreateProcess 函数中指定安全描述符 。

每种类型的可保护对象都定义了自己的一组特定访问权限以及它自己的通用访问权限映射。有关每种安全对象的特定和通用访问权限的信息,请参阅该对象的概述。

这是Windows中(来自Windows内部)的某些对象的列表,这些对象具有安全描述符,当然还有ACCESS_MASK。

1.       Files, directories and volumes (NTFS file system)
2.       Devices
3.       MAIlslots
4.       Named and anonymous pipes
5.       Jobs
6.       Processes
7.       Threads
8.       Events, keyed events and event pairs
9.       Mutexes, semaphores
10.    Shared memory sections
11.    I/O completion ports
12.    LPC ports
13.    Waitable timers
14.    Access tokens
15.    Windows stations
16.   Desktops
17.    Network shares
18.    Services

动机

从理论上讲,这已经足够了,创建这篇文章的原因是在过去的几天里,我读的是《关于UAC的阅读方法》,它是有关Windows Access令牌的深入研究。

对我而言,最有趣的部分是詹姆斯·福肖(James Forshaw)写道:

这是怎么回事?基本上,文档是错误的,您不需要QueryInformation即可仅通过QueryLimitedInformation打开流程令牌。如果您不相信我,可以在内核中反汇编NtOpenProcessTokenEx:

NTSTATUS NtOpenProcessTokenEx(HANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
DWORD HandleAttributes,
PHANDLE TokenHandle) {
EPROCESS* ProcessObject;
NTSTATUS status = ObREFERenceObjectByHandle(
ProcessHandle,
PROCESS_QUERY_LIMITED_INFORMATION,
PsProcessType,
&ProcessObject,
NULL);
...
}

回到Vista,与文档相反,通常只需要QueryLimitedInformation。

第一次对我来说很奇怪,然后我问一些朋友关于与MSDN文档相反的事情,是的,似乎有些与MSDN相反的事情。

这足以使我失去对Microsoft的信任(至少用于检查句柄的ACCESS_MASK),因此我必须以某种方式自行检查所有内容,但是,这太无聊了,不是吗?

因此,我决定为此目的编写一个IDA Python插件,但让我们看看我们真正需要寻找的东西。

我们需要知道的第一件事是Windows如何检查所需的访问权限。该ObReferenceObjectByHandle函数的功能是专为这一目的,正如你可以从MSDN看到:

该 ObReferenceObjectByHandle函数 程序提供访问验证对象的句柄,并且,如果访问可以授予,返回相应的指向对象的身体。

如果使用IDA Pro在Ntoskrnl.exe中搜索此功能,您将看到类似以下内容:

ObReferenceObjectByHandleInIDA 检查句柄访问权限的功能

此外,似乎还有其他功能可用于此目的,如果查看NtOpenProcessTokenEx的反编译源,您会发现ObReferenceObjectByHandle不再存在,而是由ObpReferenceObjectByHandleWithTag代替,因此我们也应将此函数添加到列表中。

NtOpenProcessTokenEx NtOpenProcessToken反编译源

ObpReferenceObjectByHandleWithTag中,您可以看到该对象仍然保留在MSDN中。如果您查看ObpReferenceObjectByHandleWithTag的第二个参数,您会看到0x1000,它当然是PROCESS_QUERY_LIMITED_INFORMATION,而MSDN提到了其他东西。

PROCESS_QUERY_LIMITED_INFORMATION PROCESS_QUERY_LIMITED_INFORMATION

OpenProcessToken的文档:

OpenProcessToken OpenProcessToken

测试功能

实际上,我们只需要测试以下功能(从IDA搜索结果中提供的功能中进行测试):

 CmObReferenceObjectByHandle
 ObReferenceObjectByHandleObp
 ReferenceObjectByHandleWithTag
 ObReferenceObjectByHandleWithTag 

其他功能对我们而言并不真正重要,例如,我看到了它们的XREF:

VerifierObReferenceObjectByHandle VerifierObReferenceObjectByHandle

如您所见,从我们的XREF结果来看,上述功能似乎没有什么价值。

IDA Python脚本

以下代码是IDA Python脚本,用于查找目标函数的XREF,然后从反编译源中查找第二个参数:

 from idautils import *
from idaapi import *
from idc import *
 FunctionName = "ObReferenceObjectByHandle"
 def PrintDecompiledLine(line):
print "Decompiled Line : " + line
print "ACCESS_MASK : " + line.split(FunctionName)[1].split(",")[1]
print ""
 # Finding Function
ea = BeginEA()
#for funcAddr in Functions(SegStart(ea), SegEnd(ea)):
for funcAddr in Functions(0x140001000, 0x140909410):
funcName = GetFunctionName(funcAddr)
if funcName == FunctionName:
print "Function %s is located at 0x%x" % (funcName, funcAddr)
print "====================================================="
ea = funcAddr
 for ref in CodeRefsTo(ea, 1):
print "Function : " + GetFunctionName(ref)
print ""
try:
cfunc=idaapi.decompile(ref)
except:
print "Failed to decompile : " + GetFunctionName(ref)
 multiline = False
multilinestRing = ""
# print cfunc
for item in str(cfunc).split("\n"):
if multiline :
if ")" in item and not "(" in item :
multiline = False
multilinestring += item.strip()
PrintDecompiledLine(multilinestring)
else :
multilinestring += item.strip()
elif FunctionName in item:
if ")" in item :
PrintDecompiledLine(item.strip())
else:
multiline = True
multilinestring = item.strip()
 print "-------------------------------------------------" 

结果

这是结果,如果没有IDA Pro,可以使用它们:

result 结果

结果的完整列表:

https://ntdebug.com/files/access_masks/ObReferenceObjectByHandle.txt

https://ntdebug.com/files/access_masks/ObReferenceObjectByHandleWithTag.txt

https://ntdebug.com/files/access_masks/CmObReferenceObjectByHandle.txt

https://ntdebug.com/files/access_masks/ObpReferenceObjectByHandleWithTag.txt

注意:如果要使用结果,您可以找到以(NT *)开头的函数,因为这些函数在用户模式ntdll中具有一对,因此,如果您的用户模式函数以ntdll本机函数结尾,则可以搜索相同的功能并查看实际的访问权限。

结论

在本文中,您看到了实际的实现可能会与文档有所不同,因此,如果您是安全研究人员,可以得出结论,最好检查真实的源代码(反编译),而不是仅仅信任文档。

就是这样,希望您喜欢。

anime-for-never-trust-msdn

参考文献

[1]阅读有关UAC的方法(第2部分)–(https://tyranidslair.blogspot.com/2017/05/reading-your-way-around-uac-part-2.html

[2] ObReferenceObjectByHandle函数–(https://docs.microsoft.com/zh-cn/windows-hardware/drivers/ddi/content/wdm/nf-wdm-obreferenceobjectbyhandle

[3]流程安全和访问权限–(https://docs.microsoft.com/zh-cn/windows/desktop/procthread/process-security-and-access-rights

[4]在IDAPython中枚举所有XefsTo到一个细分–(https://reverseengineering.stackexchange.com/questions/3669/enumerate-all-xefsto-a-segment-in-idapython

[5] OpenProcessToken函数–(https://docs.microsoft.com/zh-cn/windows/desktop/api/processthreadsapi/nf-processthreadsapi-openprocesstoken

[6]关于ACCESS_MASK结构–(https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/

[7] ACCESS_MASK –(https://docs.microsoft.com/zh-cn/windows/desktop/SecAuthZ/access-mask

[8] ObpReferenceProcessObjectByHandle –(http://www.codewarrior.cn/ntdoc/wrk/ob/ObpReferenceProcessObjectByHandle.htm

标签