CVE-2018-4121 JavaScriptCore WebAssembly wasm文件解析错误导致越界写漏洞分析
Posted dwfault
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CVE-2018-4121 JavaScriptCore WebAssembly wasm文件解析错误导致越界写漏洞分析相关的知识,希望对你有一定的参考价值。
目标:WebKit/Safari javascriptCore
版本:0727216
补丁commit:https://github.com/WebKit/webkit/commit/c6deeea41e524d071382a5d0fe380fbd7b634c32#diff-c6e5751bdf812034a2c73a2ec261c800
原始PoC:
out.html new.wasm
https://bugs.chromium.org/p/project-zero/issues/detail?id=1522
这个漏洞分析起来比较简单,整个过程就是文件解析型漏洞分析。那么相应的漏洞挖掘也可以按照文件型进行尝试。
PART 1
编译JavaScriptCore,加上-DENABLE_WEBASSEMBLY参数,然后稍微修改成让jsc处理的形式,有ASAN的报错:
ASSERTION FAILED: size() < capacity()
DerivedSources/ForwardingHeaders/wtf/Vector.h(1381) : void WTF::Vector< <template-parameter-1-1>, <anonymous>, <template-parameter-1-3>, <anonymous>, <template-parameter-1-5> >::uncheckedAppend(U&&) [with U = unsigned int&; T = unsigned int; long unsigned int inlineCapacity = 0ul; OverflowHandler = WTF::CrashOnOverflow; long unsigned int minCapacity = 16ul; Malloc = WTF::FastMalloc]
1 0x7f03680c8467 WTFReportBacktrace
2 0x7f03680c8567 WTFCrash
3 0x7f0367ee8294 JSC::Wasm::ModuleParser::parseFunction()
4 0x7f0367ef88ad JSC::Wasm::ModuleParser::parse()
5 0x7f0367e75e9f JSC::Wasm::BBQPlan::parseAndValidateModule()
6 0x7f0367e8f9a0 JSC::Wasm::BBQPlan::work(JSC::Wasm::Plan::CompilationEffort)
7 0x7f0367f38ec3 JSC::Wasm::Worklist::Thread::work()
8 0x7f03680ce686
9 0x7f036812b972 WTF::Thread::entryPoint(WTF::Thread::NewThreadContext*)
10 0x7f03681ddc69
11 0x7f03654486ba
12 0x7f036517e41d clone
ASAN:DEADLYSIGNAL
=================================================================
==80531==ERROR: AddressSanitizer: SEGV on unknown address 0x0000977537dd (pc 0x7f03680c8567 bp 0x7f031fdfc890 sp 0x7f031fdfc780 T1)
#0 0x7f03680c8566 in WTFCrash (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x26de566)
#1 0x7f0367ee8293 in JSC::Wasm::ModuleParser::parseFunction() (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x24fe293)
#2 0x7f0367ef88ac in JSC::Wasm::ModuleParser::parse() (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x250e8ac)
#3 0x7f0367e75e9e in JSC::Wasm::BBQPlan::parseAndValidateModule() (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x248be9e)
#4 0x7f0367e8f99f in JSC::Wasm::BBQPlan::work(JSC::Wasm::Plan::CompilationEffort) (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x24a599f)
#5 0x7f0367f38ec2 in JSC::Wasm::Worklist::Thread::work() (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x254eec2)
#6 0x7f03680ce685 in WTF::Function<void ()>::CallableWrapper<WTF::AutomaticThread::start(WTF::AbstractLocker const&)::{lambda()#1}>::call() (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x26e4685)
#7 0x7f036812b971 in WTF::Thread::entryPoint(WTF::Thread::NewThreadContext*) (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x2741971)
#8 0x7f03681ddc68 in WTF::wtfThreadEntryPoint(void*) (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x27f3c68)
#9 0x7f03654486b9 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x76b9)
#10 0x7f036517e41c in clone (/lib/x86_64-linux-gnu/libc.so.6+0x10741c)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x26de566) in WTFCrash
Thread T1 (AutomaticThread) created by T0 here:
#0 0x7f03691f64e8 in __interceptor_pthread_create (/usr/lib/x86_64-linux-gnu/libasan.so.3+0x314e8)
#1 0x7f03681dfa51 in WTF::Thread::establishHandle(WTF::Thread::NewThreadContext*) (/home/default/Desktop/webkit-0727216-debug-asan/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x27f5a51)
==80531==ABORTIN
调试时的栈回溯更清晰:
调试栈:
#0 0x00007ffff6d92b5d in WTFCrash () at ../../Source/WTF/wtf/Assertions.cpp:272
#1 0x00007ffff6c705ba in WTF::Vector<unsigned int, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>::uncheckedAppend<unsigned int&> (this=0x52ab40, value=@0x7fffb1073bd8: 0x2)
at DerivedSources/ForwardingHeaders/wtf/Vector.h:1381
#2 0x00007ffff6c64c00 in JSC::(anonymous namespace)::ModuleParser::parseFunction (this=0x7fffb1073d20) at ../../Source/JavaScriptCore/wasm/WasmModuleParser.cpp:227
#3 0x00007ffff6c62e81 in JSC::(anonymous namespace)::ModuleParser::parse (this=0x7fffb1073d20) at ../../Source/JavaScriptCore/wasm/WasmModuleParser.cpp:83
#4 0x00007ffff6c0cbfb in JSC::(anonymous namespace)::BBQPlan::parseAndValidateModule (this=0x52b620) at ../../Source/JavaScriptCore/wasm/WasmBBQPlan.cpp:105
#5 0x00007ffff6c0ef8a in JSC::(anonymous namespace)::BBQPlan::work (this=0x52b620, effort=JSC::(anonymous namespace)::Plan::Partial)
at ../../Source/JavaScriptCore/wasm/WasmBBQPlan.cpp:351
#6 0x00007ffff6c92dbf in JSC::(anonymous namespace)::Worklist::Thread::work (this=0x63e970) at ../../Source/JavaScriptCore/wasm/WasmWorklist.cpp:106
#7 0x00007ffff6d95120 in WTF::AutomaticThread::<lambda()>::operator()(void) const (__closure=0x63ec58) at ../../Source/WTF/wtf/AutomaticThread.cpp:222
#8 0x00007ffff6d959e8 in WTF::Function<void()>::CallableWrapper<WTF::AutomaticThread::start(const WTF::AbstractLocker&)::<lambda()> >::call(void) (this=0x63ec50)
at ../../Source/WTF/wtf/Function.h:101
#9 0x00007ffff6dad364 in WTF::Function<void()>::operator()(void) const (this=0x7fffb1073ed0) at ../../Source/WTF/wtf/Function.h:56
#10 0x00007ffff6dc5a13 in WTF::Thread::entryPoint (newThreadContext=0x52aca0) at ../../Source/WTF/wtf/Threading.cpp:129
#11 0x00007ffff6e0eddd in WTF::wtfThreadEntryPoint (context=0x52aca0) at ../../Source/WTF/wtf/ThreadingPthreads.cpp:223
#12 0x00007ffff41a66ba in start_thread (arg=0x7fffb1074700) at pthread_create.c:333
#13 0x00007ffff3b5041d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
WasmModuleParser.cpp:
在switch(section)下断点,有:
b WasmModuleParser.cpp:77
gdb-peda$ p section
$4985 = JSC::(anonymous namespace)::Section::Type
$4986 = JSC::(anonymous namespace)::Section::Import
$4987 = JSC::(anonymous namespace)::Section::Function
$4988 = JSC::(anonymous namespace)::Section::Custom
$4989 = JSC::(anonymous namespace)::Section::Function
继续运行多次,崩溃时Function段出现了两次。
WasmModuleParser.cpp:
gdb-peda$ b WasmModuleParser.cpp:218
gdb-peda$ p m_info.m_ptr.internalFunctionSignatureIndices
第一次:
$4996 = {
<WTF::VectorBuffer<unsigned int, 0ul, WTF::FastMalloc>> = {
<WTF::VectorBufferBase<unsigned int, WTF::FastMalloc>> = {
m_buffer = 0x7fffac000c30,
m_capacity = 0x3e7,
m_size = 0x0,
m_mask = 0x3ff
}, <No data fields>}, <No data fields>}
第二次,size已经等于capacity:
$4997 = {
<WTF::VectorBuffer<unsigned int, 0ul, WTF::FastMalloc>> = {
<WTF::VectorBufferBase<unsigned int, WTF::FastMalloc>> = {
m_buffer = 0x7fffac000c30,
m_capacity = 0x3e7,
m_size = 0x3e7,
m_mask = 0x3ff
}, <No data fields>}, <No data fields>}
继续往下面执行m_info->internalFunctionSignatureIndices.uncheckedAppend(signatureIndex);
这是一个Vector对象,这里自然而然地发生了越界写。
因为没有开启-DNDEBUG,Vector当中的一些Assert会检查到错误然后自行崩溃,在启用了-DNDEBUG的Release版本中,Assert检查会被编译器抹去,因而可以覆写到其他内存区域,进而实施漏洞利用。
PART 2
使用wabt(WebAssembly Binary Toolkit)分析样本。
dwfault[~/Desktop]>> ./wasm-objdump -s new.wasm
new.wasm: file format wasm 0x1
0000420: error: section Function out of order
Contents of section Type:
000000a: 0260 017f 0060 0000 .`...`..
Contents of section Import:
0000014: 0107 696d 706f 7274 730d 696d 706f 7274 ..imports.import
0000024: 6564 5f66 756e 6300 00 ed_func..
Contents of section Function:
0000030: e707 0101 0101 0101 0101 0101 0101 0101 ................
0000040: 0101 0101 0101 0101 0101 0101 0101 0101 ................
…
00003f0: 0101 0101 0101 0101 0101 0101 0101 0101 ................
0000400: 0101 0101 0101 0101 0101 0101 0101 0101 ................
0000410: 0101 0101 0101 0101 01 .........
Contents of section Custom:
000041b: 0000 ..
xxd查看文件内容:
dwfault[~/Desktop]>> xxd new.wasms
00000000: 0061 736d0100 000001080260 017f 0060 .asm.......`...`
00000010: 000002190107 696d 706f 7274 730d 696d ......imports.im
00000020: 706f 7274 6564 5f66 756e 6300 0003e907 ported_func..... e907 -> 07 e9 -> 0x03e9 -> 1001
00000030: e707 0101 0101 0101 0101 0101 0101 0101 ……………. e707 -> 07 e7 -> 0x03e7 -> 999
00000040: 0101 0101 0101 0101 0101 0101 0101 0101 ................
...
000003f0: 0101 0101 0101 0101 0101 0101 0101 0101 ................
00000400: 0101 0101 0101 0101 0101 0101 0101 0101 ................
00000410: 0101 0101 0101 0101 01000200 0003e907 ................
00000420: e707 0101 0101 0101 0101 0101 0101 0101 ................
00000430: 0101 0101 0101 0101 0101 0101 0101 0101 ................
…
000007e0: 0101 0101 0101 0101 0101 0101 0101 0101 ................
000007f0: 0101 0101 0101 0101 0101 0101 0101 0101 ................
00000800: 0101 0101 0101 0101 01071101 0d65 7870 .............exp
00000810: 6f72 7465 645f 6675 6e63 00010a080106 orted_func......
00000820: 0041 2a10 000b .A*...
根据文档可以人工把这个文件内容解析清楚,其实与Windows PE文件非常相似,图例有:
加粗:Section类型:
下划线:Section大小,其按照小端序的LEB128可变长度编码,每7位一个整体对一个二进制数进行编码,那么有e707 -> 07 e7 -> 0x03e7 -> 999
而函数区段的内存格式有:
函数区段声明了模块中所有函数的签名,也是按照可变长度的编码存放。
到这里这个漏洞成因就很清楚了,文件中含有两个函数区段,但是相应的处理代码按照默认只有一个函数区段来准备的,两次用来存放解析所得信息的也是同一个Vector。
PART 3
但是其实通过阅读源码我们可以发现,漏洞版本中并非没有对区段顺序的检查:
Section::Custom == 0,代码用这个来判断作为“顺序递增”规则在“遇见第一个区段”时的例外,但是这段代码不与“遇见第一个区段”完全等价,从而导致限制被绕过,漏洞由此产生。这是一个假设与实际不完全等价的问题。参考修补方法:
参考:
https://github.com/WebKit/webkit/commit/c6deeea41e524d071382a5d0fe380fbd7b634c32#diff-c6e5751bdf812034a2c73a2ec261c800
https://bugs.chromium.org/p/project-zero/issues/detail?id=1522
https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md
https://www.yhspy.com/blog/
以上是关于CVE-2018-4121 JavaScriptCore WebAssembly wasm文件解析错误导致越界写漏洞分析的主要内容,如果未能解决你的问题,请参考以下文章