-
-
[翻译]通过回溯调试分析脆弱性的根本原因
-
发表于: 2019-10-30 21:17 7217
-
https://darungrim.com/research/2019-10-10-vulnerability-root-cause-analysis-with-time-travel-debugging.html
找脆弱性有许多方法。最广泛使用的是fuzzing。本质上,fuzzing是一种暴力方法。多数例子中,先通过畸形输入造成程序崩溃。获得一次崩溃后,下一步是理解奔溃的根本原因。合适的RCA(根本原因分析)是理解bug本质的基础。这有助于决定bug是否可利用及付出更多努力开发该脆弱性的利用是否有意义。从安全工程师的视角,适当地对脆弱性进行归类并理解bug的本质对确定修复策略很有帮助。简单说,RCA是进行利用开发和制定防护策略的起始。
时间回溯调试是微软的一款记录程序执行过程并能够随时离线重现的工具。它用于从微软客户处搜集非可再现的软件bug。一旦bug在某个开启记录功能的客户环境中重现,客户可以提交记录内容到微软以便工程师分析、检查。
众所周知,理解脆弱性的本质是一个非常痛苦和乏味的过程。如果能访问源代码,可以通过重编译并调试来完全理解bug位置的上下文。如果不能,这就变成了一个试验、错误、猜测的重复游戏。
TTD(时间回溯调试)基于其记录、重现能力有助于RCA过程。TTD建立在Nirvana和iDNA技术基础上。Nirvana是一个二进制插装技术。程序执行过程由iDNA踪迹记录器记录并保存为踪迹文件。踪迹文件可以随后通过iDNA踪迹阅读器运行。这个和Pin很像,但TTD将保存执行日志和重现执行日志的功能都整合进了Windbg内,实用性更强。
有一个关于Adobe Acrobat Reader脆弱性的报告。和一段简短的描述一起,还包含一个POC。描述中说这是一个malformed JP2 stream record引起的double free问题。
下面是该POC引起奔溃的数据流和控制流的总览图。这个视图是通过TTD技术得到的。在接下来的内容中,我将解释如何通过一个有效的方法获得这个视图。
要进行TTD记录需要以管理器权限启动WinDbg,并附加到目标进程(pid为2668的AcroRd32.exe进程)。
附加TTD后,通过打开从exploit-db下载的恶意格式PDF文档来重现奔溃。
我在这里分享了我的TTD运行过程记录文件,方便大家跟随我的分析过程。Archive的密码时DarunGrim。
打开TTD文件后,在WinDbg会话中会得到一个命令提示符,输入g(go)命令到达记录的末尾,在这里能看到在哪里发生了奔溃。
下面显示程序执行过程中遇到一个异常。
0x1fb1c84f处的一字节长度空间被破坏了,我们想找到修改这个位置的代码。可以对这个地址使用ba命令来找到修改指令。
对内存0x1fb1c850执行了两次free操作。现在检查一下这个内存位置在第一次free之后是否被重新分配了。一种方法是对TTD对象使用LINQ请求。
下面的命令会返回所有返回值为0x1fb1c850的MSVCR120!malloc调用。
对所有调用进行检查后,发现0x14e29处是最后的调用,并且在两次free之前。所以很确定这是一个double-free问题。
发生double-free的代码形似:
其反编译代码如下。
0x010cda74 (ebp-98h)和0x010cda7c (ebp-90h)存的都是指向0x1fb1c850的指针。现在需要看看为什么两个内存值相同。
向后回溯找到这两个内存地址被分配的位置,对这两个位置使用ba(访问断点)命令。
定位到两个代码位置。
总的来说,根据memory_block_type参数的不同,该函数将通过不同偏移位置和memory_block_base地址返回一个内存块指针。
两次调用都通过下面的代码获取内存块。两次调用的其它参数都相同。所以,使用相同memory_block_type值调用两次GetMemoryBlock将给两个不同域分配相同的地址,最终导致double-free问题。
位于0x6A18AC6A的第二次调用的memory_block_type参数来自于下面指令。
通过下面的命令回溯这个内存值来自哪里。这里的地址0x010cdaf1是[ebp-1Bh]的内容。
0x010cdaf4处的4字节从0x010cdb54处复制来。0x010cdaf1处的4字节从0x010cdb51处复制来。通过多次使用ba命令并结合静态分析,我发现memory_block_type由函数CalcMemoryBlockType计算得到。在0x6a17be56,al寄存器存的就是memory_block_type的值。
0x6a17be1d处寄存器ax里的值是在ReadInt函数中按一定规则从字节值转换得到的int值。
在这里转换为int的字节是位于0x0763beac处的两个字节。
通过ba命令,可以发现这个内存位置的值是从0x0746ba24处复制来的。但是,地址0x0746ba24在执行到调用ReadInt之前没有被写过。TTD无法追踪内核代码执行的内存修改操作。到目前为止,可以假设0x0746ba24处的内容实在内核函数中赋值的,可能是ReadFile。为了验证我的猜测,我利用TTD请求查找针对内存0x0746ba24进行的ReadFile操作。ReadFile函数的第一个参数是缓存地址,第二个参数是缓存尺寸。
这个命令只返回一个值0x7B,通过进一步分析可以确认这块内容是通过ReadFile函数从PDF文件中读到的。
执行完这个ReadFile函数后,目标内存看起来如下:
这些内容来自于文件的0x130DF偏移处。
也就是说,内存地址0x0746ba24处的内容来自于文件偏移0x172B3处,并直接影响内存分配行为。
Fuzzed文档中被修改的字节和我通过内存回溯发现的一样。
修改前的字节“00 1C”经过ReadInt转换后是0x1D。0x1D在CalcMemoryBlockType函数中被与3异或,将导致memory_block_type值为1,而不是修改为“00 ff” 后得到的0。从GetMemoryBlock函数重复得到相同的内存块是导致double-free的本质原因。
现在看总览图就更清楚了。一个被fuzzed的字节通过影响内存类型域导致重复使用一块内存。看上去这个字节无法直接影响内存的内容。
当bug发生时,引起bug的输入数据往往是通过多个数据拷贝过程产生的。有些输入数据经过了多段代码的一轮又一轮复制,并在引起bug或脆弱性前经过一些算数运算。RCA是回溯数据和控制流来确定引起bug或脆弱性原因的逆向工程技术。本文中显示的方法很像是个使用说明。结合符号执行及一些启发式方法,有可能使用二进制插装技术构建一个有效的bug分类系统。
时间回溯调试是微软的一款记录程序执行过程并能够随时离线重现的工具。它用于从微软客户处搜集非可再现的软件bug。一旦bug在某个开启记录功能的客户环境中重现,客户可以提交记录内容到微软以便工程师分析、检查。
众所周知,理解脆弱性的本质是一个非常痛苦和乏味的过程。如果能访问源代码,可以通过重编译并调试来完全理解bug位置的上下文。如果不能,这就变成了一个试验、错误、猜测的重复游戏。
TTD(时间回溯调试)基于其记录、重现能力有助于RCA过程。TTD建立在Nirvana和iDNA技术基础上。Nirvana是一个二进制插装技术。程序执行过程由iDNA踪迹记录器记录并保存为踪迹文件。踪迹文件可以随后通过iDNA踪迹阅读器运行。这个和Pin很像,但TTD将保存执行日志和重现执行日志的功能都整合进了Windbg内,实用性更强。
一个Adobe Arcobat Reader脆弱性
有一个关于Adobe Acrobat Reader脆弱性的报告。和一段简短的描述一起,还包含一个POC。描述中说这是一个malformed JP2 stream record引起的double free问题。
下面是该POC引起奔溃的数据流和控制流的总览图。这个视图是通过TTD技术得到的。在接下来的内容中,我将解释如何通过一个有效的方法获得这个视图。
重现并记录崩溃
要进行TTD记录需要以管理器权限启动WinDbg,并附加到目标进程(pid为2668的AcroRd32.exe进程)。
附加TTD后,通过打开从exploit-db下载的恶意格式PDF文档来重现奔溃。
我在这里分享了我的TTD运行过程记录文件,方便大家跟随我的分析过程。Archive的密码时DarunGrim。
奔溃点
打开TTD文件后,在WinDbg会话中会得到一个命令提示符,输入g(go)命令到达记录的末尾,在这里能看到在哪里发生了奔溃。
有一个关于Adobe Acrobat Reader脆弱性的报告。和一段简短的描述一起,还包含一个POC。描述中说这是一个malformed JP2 stream record引起的double free问题。
下面是该POC引起奔溃的数据流和控制流的总览图。这个视图是通过TTD技术得到的。在接下来的内容中,我将解释如何通过一个有效的方法获得这个视图。
重现并记录崩溃
要进行TTD记录需要以管理器权限启动WinDbg,并附加到目标进程(pid为2668的AcroRd32.exe进程)。
附加TTD后,通过打开从exploit-db下载的恶意格式PDF文档来重现奔溃。
我在这里分享了我的TTD运行过程记录文件,方便大家跟随我的分析过程。Archive的密码时DarunGrim。
奔溃点
打开TTD文件后,在WinDbg会话中会得到一个命令提示符,输入g(go)命令到达记录的末尾,在这里能看到在哪里发生了奔溃。
要进行TTD记录需要以管理器权限启动WinDbg,并附加到目标进程(pid为2668的AcroRd32.exe进程)。
附加TTD后,通过打开从exploit-db下载的恶意格式PDF文档来重现奔溃。
我在这里分享了我的TTD运行过程记录文件,方便大家跟随我的分析过程。Archive的密码时DarunGrim。
奔溃点
打开TTD文件后,在WinDbg会话中会得到一个命令提示符,输入g(go)命令到达记录的末尾,在这里能看到在哪里发生了奔溃。
打开TTD文件后,在WinDbg会话中会得到一个命令提示符,输入g(go)命令到达记录的末尾,在这里能看到在哪里发生了奔溃。
(2dc.13a0): Unknown exception - code c0000374 (first/second chance not available) TTD: End of trace reached. (2dc.13a0): Break instruction exception - code 80000003 (first/second chance not available) Time Travel Position: 224FB2:1 eax=000d0004 ebx=00000000 ecx=ffffd8f0 edx=770d2330 esi=00003a98 edi=00000000 eip=67ce7001 esp=010cc25c ebp=010cc2a8 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 67ce7001 0970ce or dword ptr [eax-32h],esi ds:002b:000cffd2=????????
0:001> kp 10 # ChildEBP RetAddr 00 010cd970 7712b763 ntdll!RtlpReportHeapFailure 01 010cd980 770d16cf ntdll!RtlpHeapHandleError+0x1c 02 010cd9b0 770e23be ntdll!RtlpLogHeapFailure+0x9f 03 010cd9e4 6b1becfa ntdll!RtlFreeHeap+0x4abce 04 010cd9f8 6a18b2a7 MSVCR120!free(void * pBlock = 0x1fb1c850)+0x1a [f:\dd\vctools\crt\crtw32\heap\free.c @ 51] WARNING: Stack unwind information not available. Following frames may be wrong. 05 010cdb0c 6a17bc96 AcroRd32!CTJPEGTiledContentWriter::operator=+0x12c83 06 010cdcd4 6a179c26 AcroRd32!CTJPEGTiledContentWriter::operator=+0x3672 07 010cdd08 6a171033 AcroRd32!CTJPEGTiledContentWriter::operator=+0x1602 08 010cdd1c 6a1654a7 AcroRd32!AX_PDXlateToHostEx+0x271448 09 010cddc4 69c7c595 AcroRd32!AX_PDXlateToHostEx+0x2658bc 0a 010cdde0 69c7c4a9 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x22d4d 0b 010cde00 69c119d7 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x22c61 0c 010cde28 69c1198d AcroRd32!AcroWinBrowserMain+0x19eb3 0d 010cde3c 69cb0c16 AcroRd32!AcroWinBrowserMain+0x19e69 0e 010cde54 69d8d21a AcroRd32!CTJPEGWriter::CTJPEGWriter+0x573ce 0f 010cdea8 6a0ee398 AcroRd32!CTJPEGDecoderHasMoreTiles+0xf4a
Time Travel Position: 222B02:4E9 eax=00000000 ebx=7715c908 ecx=00000002 edx=00000000 esi=00000002 edi=1fb1c848 eip=7712cfb0 esp=010cd974 ebp=010cd980 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!RtlpReportHeapFailure: 7712cfb0 8bff mov edi,edi
堆破坏
Time Travel Position: 222B02:67 eax=6ae3fb4c ebx=1fb1c850 ecx=1fb1c850 edx=00000000 esi=1fb1c848 edi=01670000 eip=77097851 esp=010cd9c8 ebp=010cd9e4 iopl=0 ov up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000a16 ntdll!RtlFreeHeap+0x61: 77097851 f646073f test byte ptr [esi+7],3Fh ds:002b:1fb1c84f=80
0x1fb1c84f处的一字节长度空间被破坏了,我们想找到修改这个位置的代码。可以对这个地址使用ba命令来找到修改指令。
ba w1 1fb1c84f g-
0:001> kp 10 # ChildEBP RetAddr 00 010cd970 7712b763 ntdll!RtlpReportHeapFailure 01 010cd980 770d16cf ntdll!RtlpHeapHandleError+0x1c 02 010cd9b0 770e23be ntdll!RtlpLogHeapFailure+0x9f 03 010cd9e4 6b1becfa ntdll!RtlFreeHeap+0x4abce 04 010cd9f8 6a18b2a7 MSVCR120!free(void * pBlock = 0x1fb1c850)+0x1a [f:\dd\vctools\crt\crtw32\heap\free.c @ 51] WARNING: Stack unwind information not available. Following frames may be wrong. 05 010cdb0c 6a17bc96 AcroRd32!CTJPEGTiledContentWriter::operator=+0x12c83 06 010cdcd4 6a179c26 AcroRd32!CTJPEGTiledContentWriter::operator=+0x3672 07 010cdd08 6a171033 AcroRd32!CTJPEGTiledContentWriter::operator=+0x1602 08 010cdd1c 6a1654a7 AcroRd32!AX_PDXlateToHostEx+0x271448 09 010cddc4 69c7c595 AcroRd32!AX_PDXlateToHostEx+0x2658bc 0a 010cdde0 69c7c4a9 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x22d4d 0b 010cde00 69c119d7 AcroRd32!CTJPEGWriter::CTJPEGWriter+0x22c61 0c 010cde28 69c1198d AcroRd32!AcroWinBrowserMain+0x19eb3 0d 010cde3c 69cb0c16 AcroRd32!AcroWinBrowserMain+0x19e69 0e 010cde54 69d8d21a AcroRd32!CTJPEGWriter::CTJPEGWriter+0x573ce 0f 010cdea8 6a0ee398 AcroRd32!CTJPEGDecoderHasMoreTiles+0xf4a
Time Travel Position: 222B02:4E9 eax=00000000 ebx=7715c908 ecx=00000002 edx=00000000 esi=00000002 edi=1fb1c848 eip=7712cfb0 esp=010cd974 ebp=010cd980 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!RtlpReportHeapFailure: 7712cfb0 8bff mov edi,edi
堆破坏
Time Travel Position: 222B02:67 eax=6ae3fb4c ebx=1fb1c850 ecx=1fb1c850 edx=00000000 esi=1fb1c848 edi=01670000 eip=77097851 esp=010cd9c8 ebp=010cd9e4 iopl=0 ov up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000a16 ntdll!RtlFreeHeap+0x61: 77097851 f646073f test byte ptr [esi+7],3Fh ds:002b:1fb1c84f=80
0x1fb1c84f处的一字节长度空间被破坏了,我们想找到修改这个位置的代码。可以对这个地址使用ba命令来找到修改指令。
ba w1 1fb1c84f g-
Time Travel Position: 222B02:4E9 eax=00000000 ebx=7715c908 ecx=00000002 edx=00000000 esi=00000002 edi=1fb1c848 eip=7712cfb0 esp=010cd974 ebp=010cd980 iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!RtlpReportHeapFailure: 7712cfb0 8bff mov edi,edi
堆破坏
Time Travel Position: 222B02:67 eax=6ae3fb4c ebx=1fb1c850 ecx=1fb1c850 edx=00000000 esi=1fb1c848 edi=01670000 eip=77097851 esp=010cd9c8 ebp=010cd9e4 iopl=0 ov up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000a16 ntdll!RtlFreeHeap+0x61: 77097851 f646073f test byte ptr [esi+7],3Fh ds:002b:1fb1c84f=80
0x1fb1c84f处的一字节长度空间被破坏了,我们想找到修改这个位置的代码。可以对这个地址使用ba命令来找到修改指令。
ba w1 1fb1c84f g-
Time Travel Position: 222B02:67 eax=6ae3fb4c ebx=1fb1c850 ecx=1fb1c850 edx=00000000 esi=1fb1c848 edi=01670000 eip=77097851 esp=010cd9c8 ebp=010cd9e4 iopl=0 ov up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000a16 ntdll!RtlFreeHeap+0x61: 77097851 f646073f test byte ptr [esi+7],3Fh ds:002b:1fb1c84f=80
0x1fb1c84f处的一字节长度空间被破坏了,我们想找到修改这个位置的代码。可以对这个地址使用ba命令来找到修改指令。
ba w1 1fb1c84f g-
Time Travel Position: 222B02:67 eax=6ae3fb4c ebx=1fb1c850 ecx=1fb1c850 edx=00000000 esi=1fb1c848 edi=01670000 eip=77097851 esp=010cd9c8 ebp=010cd9e4 iopl=0 ov up ei pl nz ac pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000a16 ntdll!RtlFreeHeap+0x61: 77097851 f646073f test byte ptr [esi+7],3Fh ds:002b:1fb1c84f=80
0x1fb1c84f处的一字节长度空间被破坏了,我们想找到修改这个位置的代码。可以对这个地址使用ba命令来找到修改指令。
ba w1 1fb1c84f g-
Time Travel Position: 222B02:B eax=0317022d ebx=1fb1c848 ecx=8317022d edx=0317022d esi=0317022d edi=078410c0 eip=77097953 esp=010cd990 ebp=010cd9bc iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!RtlpLowFragHeapFree+0x93: 77097953 c6430780 mov byte ptr [ebx+7],80h ds:002b:1fb1c84f=88
0:001> kp # ChildEBP RetAddr 00 010cd9bc 7709787d ntdll!RtlpLowFragHeapFree+0x93 01 010cd9e4 6b1becfa ntdll!RtlFreeHeap+0x8d Unable to load image C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.dll, Win32 error 0n2 02 010cd9f8 6a18b296 MSVCR120!free(void * pBlock = 0x1fb1c850)+0x1a [f:\dd\vctools\crt\crtw32\heap\free.c @ 51] WARNING: Stack unwind information not available. Following frames may be wrong. 03 010cdb0c 6a17bc96 AcroRd32!CTJPEGTiledContentWriter::operator=+0x12c72 04 010cdcd4 6a179c26 AcroRd32!CTJPEGTiledContentWriter::operator=+0x3672 05 010cdd08 6a171033 AcroRd32!CTJPEGTiledContentWriter::operator=+0x1602 06 010cdd1c 6a1654a7 AcroRd32!AX_PDXlateToHostEx+0x271448
Time Travel Position: 222B02:B eax=0317022d ebx=1fb1c848 ecx=8317022d edx=0317022d esi=0317022d edi=078410c0 eip=77097953 esp=010cd990 ebp=010cd9bc iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 ntdll!RtlpLowFragHeapFree+0x93: 77097953 c6430780 mov byte ptr [ebx+7],80h ds:002b:1fb1c84f=88
0:001> kp # ChildEBP RetAddr 00 010cd9bc 7709787d ntdll!RtlpLowFragHeapFree+0x93 01 010cd9e4 6b1becfa ntdll!RtlFreeHeap+0x8d Unable to load image C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.dll, Win32 error 0n2 02 010cd9f8 6a18b296 MSVCR120!free(void * pBlock = 0x1fb1c850)+0x1a [f:\dd\vctools\crt\crtw32\heap\free.c @ 51] WARNING: Stack unwind information not available. Following frames may be wrong. 03 010cdb0c 6a17bc96 AcroRd32!CTJPEGTiledContentWriter::operator=+0x12c72 04 010cdcd4 6a179c26 AcroRd32!CTJPEGTiledContentWriter::operator=+0x3672 05 010cdd08 6a171033 AcroRd32!CTJPEGTiledContentWriter::operator=+0x1602 06 010cdd1c 6a1654a7 AcroRd32!AX_PDXlateToHostEx+0x271448
对内存0x1fb1c850执行了两次free操作。现在检查一下这个内存位置在第一次free之后是否被重新分配了。一种方法是对TTD对象使用LINQ请求。
下面的命令会返回所有返回值为0x1fb1c850的MSVCR120!malloc调用。