看到前辈们相关的文章,不太明白什么是句柄降权,于是专门去学习一下,过程有一点波折。当一个进程利用名称来创建或打开一个对象时,将获得一个句柄,该句柄指向所创建或打开的对象。以后,该进程无须使用名称来引用该对象,使用此句柄即可访问。这样做可以显著地提高引用对象的效率。句柄是一个在软件设计中被广泛使用的概念。例如,在C运行库中,文件操作使用句柄来表示,每当应用程序创建或打开一个文件时,只要此创建或打开操作成功,则C运行库返回一个句柄。以后应用程序对文件的读写操作都使用此句柄来标识该文件。而且,如果两个应用程序以共享方式打开了同一个文件,那么,它们将分别得到各自的句柄,且都可以通过句柄操作该文件。尽管两个应用程序得到的句柄的值并不相同,但是这两个句柄所指的文件却是同一个。因此,句柄只是一个对象引用,同一个对象在不同的环境下可能有不同的引用(句柄)值。上文中的"对象"指的是内核对象,我们在R3中所使用的文件、进程、线程在内核中都有对应内核对象。应用层每次创建或打开进程、文件都会对相应的内核对象创建一个句柄。当多个进程同时打开一个文件时,该文件在内核中只会存在一个文件内核对象,但每个进程都有一个各自的文件句柄,每个句柄会增加内核对象的引用计数,只有当内核对象的引用计数为0时,内核对象才会释放。eprocess指向一个ObjectTable,ObjectTbale中存在TableCode,这个指向的是这个进程的私有句柄表。同时ObjectTable中还有一个HandleTableList,这个是一个链表,通过HandleTableList 成员遍历得到所有进程的ObjectTable地址我们的目标是获取到_object_header结构体,这个结构体才是句柄的真正内容。但是不同版本系统下的取法不太一样,win7是直接指向句柄,win10则需要做一些偏移,这些偏移google没有资料,大多都是通过IDA静态分析函数才能得到。win中有一些根据_handle_table_entry获取进程句柄的函数,我这里没有做过多分析,直接使用前辈分析后的经验。分析目标ntoskrnl.exe下的ObpEnumFindHandleProcedure函数,可以看到如下可以在开头部分看到(handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64),这样才能获取到句柄内容,获取到_object_header.但是我自己尝试的时候没有获取到,直到我注意到帖子里面最后得到的值开头都是0xffff,这说明右移前面不是补充0,而是补充1所以地址计算实际是:(handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64) + 0xffff000000000000得到的就是_OBJECT_HEADER,这表示一个句柄头,句柄体在body的位置,我系统版本的偏移是0x30,进程句柄的话就是_eprocess结构体0x18位置的TypeIndex表示这个句柄对应的对象是一个什么类型的对象,比如文件、进程、线程等,0x30的Body就是便指向了该句柄对应的对象结构。若句柄对应的对象是一个进程对象那么0x30的位置存的就是对应进程对象的_EPROCESS的结构,可以从这个结构便获得进程名、进程ID等等信息。所以在内核中有一个链表存放了每一个进程的私有句柄表。句柄表是以页为单位,两层句柄表则是第一层存放第二层的指针,一般只有系统进程才会打开那么多的句柄,恶意进程通常只有一层。win10的机器上tablecode是存放了一页的handle_table_entry,每一个16字节,一页大小是4k,所以一页最多256个句柄。(32位系统的是一页512个句柄)怎么判断句柄表有几层?TableCode的最后2个bit表示层数(有的文章说是3个bit,我也不确定),但是目前我看最多的也只有两层,下面分别是0层和1层的情况。可以看到tablecode句柄表的内容不是句柄,而是_handle_table_entry,这不是一个结构体,是一个union从handle_table_entry到句柄还需要一些额外的计算变化,同时一个进程句柄的权限就标注在每个句柄对应的这个结构体当中以手动的方式从一个eprocess内存看到他下面的句柄表windbg以内核附加模式连接上虚拟机/真机后,首先我们需要一个EPROCESS结构体地址,使用!process 0 0查看所有进程的基本信息PROCESS的值就是EPROCESS的地址值, 我这里选用csrss.exe的PROCESS值ffffbf8eab1ea080定位ObjectTable的值再进一步查看_HANDLE_TABLE结构体,定位TableCode的值得到TableCode的值是0xffffd00b`16d58001,注意TableCode最后的一位是1,这表示有两层页表,第一层的值是指向的第二层的指针,所以先查看第一层句柄指针表看到有3个二层句柄表,我们选用第一张表可以看到从ffffd00b`16667010开始每16字节表示一个_handle_table_entry union体(handle_table_entry->LowValue >> 16) & 0xFFFFFFFFFFFFFFF0ui64)获取_object_header结构体,记得前面填充的要是1,计算一下值0xbf8eaaee9430ffff是ffffd00b16667010的值,对应的就是handle_table_entry->LowValue,得到地址0xffffbf8eaaee9430也就是_object_header可以看到整个object_header的内容,这只是句柄的头部,句柄的内容还在0x30偏移的位置,由于我调试发现这一个进程句柄,所以直接用eprocess展示这个句柄内容。这样就通过进程的私有句柄表获得了被打开句柄进程的信息。怎么样从一个句柄头(_object_header)判断出这是一个进程句柄(process handle),文件句柄(file handle)还是设备句柄(device handle)这个不同版本的系统判断方法不一样,win7/8/8.1是一样的 win10则不同。网络上大多都是win7, win8的我在这篇外网文章上才找到win10的判断方法这几个版本的_object_header结构大致如下一般直接TypeIndex表示这个句柄的类型index。相同的类型这个值会相同,还可以进一步查看这个index在nt!object_type这个表中的具体信息上面就是Typeindex = 7对应的意义,是进程句柄win10的Typeindex就不一样了,测试会发现,哪怕都是进程句柄这个Typeindex的值也会不同。需要将3个单字节的值异或起来,Typeindex ^ nt!ObHeaderCookie ^ 地址的第二个字节最后得到的才是真的Typeindex一个句柄的权限,表示句柄拥有者对这个句柄的操作权限,权限有以下几种当我们通过OpenProcess以多种权限申请打开进程时便将多种权限或运算
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-281625.htm
[原创]句柄降权绕过CallBacks检查
329 浏览
11 回复
感谢大佬
感谢分享
感谢分享。
感谢分享我只会使用CallBack api,遍历win10句柄表对我很有帮助网上都是win7,我一直想试试给ce提权。
感谢大佬
感谢大佬
感谢分享
好文
流畅,感谢分享
获取句柄权限那一步,可以导出windows中的匿名api,定义下面函数头即可
UINT_PTR ObGetObjectType(PVOID object);
UINT_PTR ObGetObjectType(PVOID object);
感谢分享