gpt4 book ai didi

c++ - JPEG of Death 漏洞如何运作?

转载 作者:行者123 更新时间:2023-12-01 17:50:26 25 4
gpt4 key购买 nike

我一直在阅读关于在 Windows XP 和 Windows Server 2003 上针对 GDI+ 的旧漏洞利用。我正在做的一个项目被称为死亡 JPEG。

漏洞利用在以下链接中有很好的解释:
http://www.infosecwriters.com/text_resources/pdf/JPEG.pdf

基本上,JPEG 文件包含一个名为 COM 的部分,其中包含一个(可能为空的)注释字段,以及一个包含 COM 大小的两字节值。如果没有注释,则大小为2。阅读器(GDI+)读取大小,减去2,并分配一个适当大小的缓冲区来复制堆中的注释。
攻击涉及放置一个值 0在该领域。 GDI+ 减去 2 ,导致值 -2 (0xFFFe)它被转换为无符号整数 0XFFFFFFFE来自 memcpy .

示例代码:

unsigned int size;
size = len - 2;
char *comment = (char *)malloc(size + 1);
memcpy(comment, src, size);

请注意 malloc(0)第三行应该返回一个指向堆上未分配内存的指针。怎么写 0XFFFFFFFE字节 ( 4GB !!!!) 可能不会使程序崩溃?这是否超出堆区域并写入其他程序和操作系统的空间?那会发生什么?

据我了解 memcpy ,它只是复制 n从目标到源的字符。在这种情况下,源应该在堆栈上,目标应该在堆上,而 n4GB .

最佳答案

这个漏洞绝对是heap overflow .

How can writing 0XFFFFFFFE bytes (4 GB!!!!) possibly not crash the program?



它可能会,但在某些情况下,您有时间在崩溃发生之前进行利用(有时,您可以让程序恢复正常执行并避免崩溃)。

当 memcpy() 启动时,拷贝将覆盖其他一些堆块或堆管理结构的某些部分(例如空闲列表、忙碌列表等)。

在某些时候,拷贝将遇到未分配的页面并在写入时触发 AV(访问冲突)。然后 GDI+ 将尝试在堆中分配一个新块(请参阅 ntdll!RtlAllocateHeap )……但是现在堆结构都搞砸了。

此时,通过精心制作 JPEG 图像,您可以使用受控数据覆盖堆管理结构。当系统尝试分配新块时,它可能会从空闲列表中取消链接(空闲)块。

块由(特别是)flink(前向链接;列表中的下一个块)和闪烁(后向链接;列表中的前一个块)指针管理。如果您同时控制 flink 和闪烁,您可能有一个可能的 WRITE4(写什么/在哪里条件),您可以在其中控制可以写的内容和可以写的位置。

在这一点上,您可以覆盖函数指针( SEH [Structured Exception Handlers] 指针在 2004 年是当时的选择目标)并获得代码执行。

见博文 Heap Corruption: A Case Study .

注意:虽然我写过使用 freelist 的漏洞利用,但攻击者可能会选择使用其他堆元数据的另一条路径(“堆元数据”是系统用来管理堆的结构;flink 和blink 是堆元数据的一部分),但是unlink 利用可能是“最简单”的一种。谷歌搜索“堆利用”将返回大量关于此的研究。

Does this write beyond the heap area and into the space of other programs and the OS?



从来没有。现代操作系统基于虚拟地址空间的概念,因此每个进程都有自己的虚拟地址空间,可以在 32 位系统上寻址多达 4 GB 的内存(实际上你只有一半在用户空间,其余的是内核)。

简而言之,一个进程不能访问另一个进程的内存(除非它通过某些服务/API 向内核请求它,但内核会检查调用者是否有权这样做)。

我决定在本周末测试这个漏洞,这样我们就可以很好地了解发生了什么,而不是纯粹的猜测。
这个漏洞现在已经有 10 年历史了,所以我认为可以写它,尽管我没有在这个答案中解释利用部分。

规划

最困难的任务是找到一个只有 SP1 的 Windows XP,就像 2004 年一样:)

然后,我下载了一个仅由单个像素组成的 JPEG 图像,如下所示(为简洁起见):
File 1x1_pixel.JPG
Address Hex dump ASCII
00000000 FF D8 FF E0|00 10 4A 46|49 46 00 01|01 01 00 60| ÿØÿà JFIF `
00000010 00 60 00 00|FF E1 00 16|45 78 69 66|00 00 49 49| ` ÿá Exif II
00000020 2A 00 08 00|00 00 00 00|00 00 00 00|FF DB 00 43| * ÿÛ C
[...]

JPEG 图片由二进制标记(引入段)组成。在上图中, FF D8是 SOI(图像开始)标记,而 FF E0例如,是一个应用程序标记。

标记段中的第一个参数(除了一些像 SOI 这样的标记)是一个两字节长度参数,它对标记段中的字节数进行编码,包括长度参数,不包括两字节标记。

我只是在 SOI 之后添加了一个 COM 标记(0x FFFE),因为标记没有严格的顺序。
File 1x1_pixel_comment_mod1.JPG
Address Hex dump ASCII
00000000 FF D8 FF FE|00 00 30 30|30 30 30 30|30 31 30 30| ÿØÿþ 0000000100
00000010 30 32 30 30|30 33 30 30|30 34 30 30|30 35 30 30| 0200030004000500
00000020 30 36 30 30|30 37 30 30|30 38 30 30|30 39 30 30| 0600070008000900
00000030 30 61 30 30|30 62 30 30|30 63 30 30|30 64 30 30| 0a000b000c000d00
[...]

COM 段的长度设置为 00 00来触发漏洞。我还在 COM 标记后立即注入(inject)了 0xFFFC 字节,其中包含一个循环模式,一个 4 字节的十六进制数字,这在“利用”漏洞时会变得很方便。

调试

双击图像将立即触发 Windows shell 程序中的错误(又名“explorer.exe”),位于 gdiplus.dll 中的某处。 , 在名为 GpJpegDecoder::read_jpeg_marker() 的函数中.

为图片中的每个标记调用这个函数,它只是:读取标记段大小,分配一个长度为段大小的缓冲区,并将段的内容复制到这个新分配的缓冲区中。

这里是函数的开始:
.text:70E199D5  mov     ebx, [ebp+arg_0] ; ebx = *this (GpJpegDecoder instance)
.text:70E199D8 push esi
.text:70E199D9 mov esi, [ebx+18h]
.text:70E199DC mov eax, [esi] ; eax = pointer to segment size
.text:70E199DE push edi
.text:70E199DF mov edi, [esi+4] ; edi = bytes left to process in the image
eax register 指向段大小和 edi是图像中剩余的字节数。

然后代码继续读取段大小,从最高有效字节开始(长度是 16 位值):
.text:70E199F7  xor     ecx, ecx        ; segment_size = 0
.text:70E199F9 mov ch, [eax] ; get most significant byte from size --> CH == 00
.text:70E199FB dec edi ; bytes_to_process --
.text:70E199FC inc eax ; pointer++
.text:70E199FD test edi, edi
.text:70E199FF mov [ebp+arg_0], ecx ; save segment_size

以及最低有效字节:
.text:70E19A15  movzx   cx, byte ptr [eax] ; get least significant byte from size --> CX == 0
.text:70E19A19 add [ebp+arg_0], ecx ; save segment_size
.text:70E19A1C mov ecx, [ebp+lpMem]
.text:70E19A1F inc eax ; pointer ++
.text:70E19A20 mov [esi], eax
.text:70E19A22 mov eax, [ebp+arg_0] ; eax = segment_size

完成后,段大小用于分配缓冲区,如下计算:

alloc_size = 段大小 + 2

这是通过以下代码完成的:
.text:70E19A29  movzx   esi, word ptr [ebp+arg_0] ; esi = segment size (cast from 16-bit to 32-bit)
.text:70E19A2D add eax, 2
.text:70E19A30 mov [ecx], ax
.text:70E19A33 lea eax, [esi+2] ; alloc_size = segment_size + 2
.text:70E19A36 push eax ; dwBytes
.text:70E19A37 call _GpMalloc@4 ; GpMalloc(x)

在我们的例子中,由于段大小为 0, 为缓冲区分配的大小为 2 字节 .

漏洞就在分配之后:
.text:70E19A37  call    _GpMalloc@4     ; GpMalloc(x)
.text:70E19A3C test eax, eax
.text:70E19A3E mov [ebp+lpMem], eax ; save pointer to allocation
.text:70E19A41 jz loc_70E19AF1
.text:70E19A47 mov cx, [ebp+arg_4] ; low marker byte (0xFE)
.text:70E19A4B mov [eax], cx ; save in alloc (offset 0)
;[...]
.text:70E19A52 lea edx, [esi-2] ; edx = segment_size - 2 = 0 - 2 = 0xFFFFFFFE!!!
;[...]
.text:70E19A61 mov [ebp+arg_0], edx

代码简单地从整个段大小(在我们的例子中为 0)中减去 segment_size 大小(段长度是 2 个字节的值),并以整数下溢结束: 0 - 2 = 0xFFFFFFFE

然后代码检查图像中是否有剩余字节要解析(这是真的),然后跳转到拷贝:
.text:70E19A69  mov     ecx, [eax+4]  ; ecx = bytes left to parse (0x133)
.text:70E19A6C cmp ecx, edx ; edx = 0xFFFFFFFE
.text:70E19A6E jg short loc_70E19AB4 ; take jump to copy
;[...]
.text:70E19AB4 mov eax, [ebx+18h]
.text:70E19AB7 mov esi, [eax] ; esi = source = points to segment content ("0000000100020003...")
.text:70E19AB9 mov edi, dword ptr [ebp+arg_4] ; edi = destination buffer
.text:70E19ABC mov ecx, edx ; ecx = copy size = segment content size = 0xFFFFFFFE
.text:70E19ABE mov eax, ecx
.text:70E19AC0 shr ecx, 2 ; size / 4
.text:70E19AC3 rep movsd ; copy segment content by 32-bit chunks

上面的片段显示拷贝大小是 0xFFFFFFFE 32 位块。源缓冲区被控制(图片的内容),目标是堆上的缓冲区。

写条件

当拷贝到达内存页的末尾(这可能来自源指针或目标指针)时,将触发访问冲突 (AV) 异常。当 AV 被触发时,堆已经处于易受攻击的状态,因为拷贝已经覆盖了所有后续的堆块,直到遇到非映射页面。

使此漏洞可利用的原因是 3 SEH(结构化异常处理程序;这是 try/except 在低级别)正在捕获这部分代码的异常。更准确地说,第一个 SEH 将展开堆栈,以便它返回来解析另一个 JPEG 标记,从而完全跳过触发异常的标记。

如果没有 SEH,代码就会使整个程序崩溃。所以代码会跳过 COM 段并解析另一个段。所以我们回到 GpJpegDecoder::read_jpeg_marker()使用新段以及当代码分配新缓冲区时:
.text:70E19A33  lea     eax, [esi+2] ; alloc_size = semgent_size + 2
.text:70E19A36 push eax ; dwBytes
.text:70E19A37 call _GpMalloc@4 ; GpMalloc(x)

系统将从空闲列表中取消链接块。碰巧元数据结构被图像内容覆盖;所以我们用受控元数据控制取消链接。堆管理器中系统(ntdll)某处的以下代码:
CPU Disasm
Address Command Comments
77F52CBF MOV ECX,DWORD PTR DS:[EAX] ; eax points to '0003' ; ecx = 0x33303030
77F52CC1 MOV DWORD PTR SS:[EBP-0B0],ECX ; save ecx
77F52CC7 MOV EAX,DWORD PTR DS:[EAX+4] ; [eax+4] points to '0004' ; eax = 0x34303030
77F52CCA MOV DWORD PTR SS:[EBP-0B4],EAX
77F52CD0 MOV DWORD PTR DS:[EAX],ECX ; write 0x33303030 to 0x34303030!!!

现在我们可以写我们想要的东西,我们想要的地方......

关于c++ - JPEG of Death 漏洞如何运作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28369097/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com