
Um Writeup para a versão de Sleirsgoevy da implementação do exploit de CVE-2018-4386 por Fire30 chamada Bad_Hoist.
[!Note] Informações de contexto sobre o PS4:
O console PlayStation 4 possui uma CPU AMD x86-64 personalizada (8 núcleos); seu sistema operacional Orbis é baseado em FreeBSD (v9.0) com partes de NetBSD também. Ele também inclui uma ampla variedade de softwares de código aberto adicionais, como Mono VM e WebKit.
O Navegador de Internet usado pelo PS4 é, na verdade, construído com o Projeto WebKit de código aberto. Este é o mecanismo de layout de código aberto que renderiza páginas web nos navegadores para iOS, Wii U, 3DS, PS Vita e PS4.
O Navegador de Internet do PS4 consiste, na verdade, em 2 processos separados. O processo que sequestramos para execução de código é o Processo WebKit Core (que lida, por exemplo, com o parsing de HTML e CSS, decodificação de imagens e execução de JavaScript). O outro lida com todo o resto: exibição de gráficos, recebimento de entradas do controle, gerenciamento de histórico e favoritos, etc.
O navegador WebKit do PS4 emprega múltiplos alocadores de heap, cada um atendendo a diferentes componentes. Eles são os seguintes:
O núcleo do CVE-2018-4386 é uma falha lógica no mecanismo JavaScriptCore (JSC) do WebKit (v605.1.15), que é a versão usada no firmware 6.XX do PS4. A falha reside na função BytecodeGenerator::hoistSloppyModeFunctionIfNecessary e envolve o tratamento inadequado do hoisting de variáveis em JavaScript em modo sloppy, especificamente dentro de laços for-in.
Componente Vulnerável (ForInContext):
O que visamos principalmente é o ForInContext. Esta é uma estrutura interna usada pelo JavaScriptCore para gerenciar o estado de um laço for-in, rastreando a variável de iteração atual e o conjunto de propriedades sendo enumeradas.
Quando uma declaração de função é içada (hoisted) dentro de um laço for-in, o mecanismo deve invalidar o objeto ForInContext associado se a variável de iteração for sobrescrita. No entanto, devido ao bug, essa invalidação não ocorre. Isso permite que a variável de iteração seja substituída por um objeto arbitrário. Apesar disso, o mecanismo continua a tratar a variável como um nome de propriedade de string.
Quando o manipulador de bytecode op_get_direct_pname é invocado posteriormente, ele usa a variável de iteração diretamente como um objeto string, sem verificação de tipo.
Ao passar um objeto especialmente criado em vez de uma string, levando a uma confusão de tipos, conseguimos explorar isso de uma forma que alcança corrupção de memória, e até mesmo primitivas de exploração úteis como addrof, fakeobj, e Leitura/Escrita Arbitrária.
Structure ID: Todo objeto no JavaScriptCore, incluindo representações internas como WTF::StringImpl, possui um structure ID (ou tag de tipo) que informa ao mecanismo que tipo de objeto é e como interpretar seus campos.
Confusão de Tipos: Nosso exploit abusa do bug CVE-2018-4386 para fazer com que um objeto JavaScript seja interpretado como um StringImpl. Este é o propósito da função create_impl(), que retorna um objeto WTF::StringImpl com confusão de tipos, que pode então ser passado para a função trigger(), como o objeto arbitrário do qual falamos anteriormente na parte mecanismo de vulnerabilidade deste write-up.
No entanto, para que isso funcione, o layout de memória e o structure ID devem ser "próximos o suficiente" do que o mecanismo espera para um objeto string real.
JSString::toIdentifier() faz internamente?Existe um método no mecanismo JavaScriptCore do WebKit do Navegador de Internet do PS4 cujo nome é JSString::toIdentifier(). Este método converte um objeto string JavaScript (JSString) em uma representação interna de Identifier.
Este Identifier é usado em todo o mecanismo para comparar, armazenar e pesquisar com eficiência nomes de propriedades, nomes de variáveis e outras strings que precisam ser referenciadas rápida e frequentemente pelo mecanismo JavaScript.
Ele verifica se o objeto é uma string válida e se o seu structure ID corresponde ao que o mecanismo espera para um objeto string. Se a string for uma rope (uma concatenação de strings), ele pode achatá-la antes de converter. Em seguida, ele recupera um Identifier existente para a string ou cria um novo se não existir.
Este Identifier é então usado internamente para buscas rápidas de propriedades e variáveis.
JSString::toIdentifier()Quando o exploit entra no laço for que itera 1024 vezes, cada iteração cria um novo objeto WTF::StringImpl com confusão de tipos, com 32 novos Structure IDs retornados pela função create_impl(). Quando esse objeto com confusão de tipos é usado e passado para trigger() como o objeto arbitrário, o JSC chama JSString::toIdentifier() nele.
JSString::toIdentifier() verifica certos bits no structure ID para confirmar se o objeto é uma string válida ou pode ser tratado como tal. Ao gerar muitos objetos com diferentes layouts e structure IDs, o exploit aumenta as chances de que pelo menos um tenha um structure ID que passe nas verificações internas de JSString::toIdentifier().