Translated ['macos-hardening/macos-security-and-privilege-escalation/mac

This commit is contained in:
Translator 2023-11-06 00:53:51 +00:00
parent 25f71be691
commit f647ee4daf
2 changed files with 29 additions and 23 deletions

View file

@ -208,7 +208,7 @@ int main(int argc, char** argv) {
return 1;
}
printf("Message sent successfully!\n");
printf("Message sent to securityd\n");
return 0;
}
@ -272,20 +272,20 @@ printf("Sent a message\n");
### Portas Privilegiadas
* **Porta do host**: Se um processo tem o privilégio de **enviar** sobre esta porta, ele pode obter **informações** sobre o **sistema** (por exemplo, `host_processor_info`).
* **Porta privilégiada do host**: Um processo com o direito de **enviar** sobre esta porta pode realizar **ações privilegiadas**, como carregar uma extensão do kernel. O **processo precisa ser root** para obter essa permissão.
* **Porta do host**: Se um processo tem o **privilégio de envio** sobre esta porta, ele pode obter **informações** sobre o **sistema** (por exemplo, `host_processor_info`).
* **Porta de privilégio do host**: Um processo com o direito de **envio** sobre esta porta pode realizar **ações privilegiadas**, como carregar uma extensão do kernel. O **processo precisa ser root** para obter essa permissão.
* Além disso, para chamar a API **`kext_request`**, é necessário ter outras permissões **`com.apple.private.kext*`**, que são concedidas apenas a binários da Apple.
* **Porta do nome da tarefa**: Uma versão não privilegiada da _porta da tarefa_. Ela faz referência à tarefa, mas não permite controlá-la. A única coisa que parece estar disponível através dela é `task_info()`.
* **Porta da tarefa** (também conhecida como porta do kernel)**:** Com permissão de envio sobre esta porta, é possível controlar a tarefa (ler/escrever memória, criar threads...).
* Chame `mach_task_self()` para **obter o nome** desta porta para a tarefa chamadora. Esta porta é **herdada** apenas através do **`exec()`**; uma nova tarefa criada com `fork()` recebe uma nova porta de tarefa (como um caso especial, uma tarefa também recebe uma nova porta de tarefa após `exec()` em um binário suid). A única maneira de criar uma tarefa e obter sua porta é realizar a ["dança de troca de porta"](https://robert.sesek.com/2014/1/changes\_to\_xnu\_mach\_ipc.html) enquanto faz um `fork()`.
* Estas são as restrições para acessar a porta (de `macos_task_policy` do binário `AppleMobileFileIntegrity`):
* Estas são as restrições para acessar a porta (do `macos_task_policy` do binário `AppleMobileFileIntegrity`):
* Se o aplicativo tiver a permissão **`com.apple.security.get-task-allow`**, processos do **mesmo usuário podem acessar a porta da tarefa** (comumente adicionado pelo Xcode para depuração). O processo de **notarização** não permitirá isso em lançamentos de produção.
* Aplicativos com a permissão **`com.apple.system-task-ports`** podem obter a **porta da tarefa para qualquer** processo, exceto o kernel. Em versões mais antigas, era chamada **`task_for_pid-allow`**. Isso é concedido apenas a aplicativos da Apple.
* Aplicativos com a permissão **`com.apple.system-task-ports`** podem obter a **porta da tarefa para qualquer** processo, exceto o kernel. Em versões mais antigas, era chamado **`task_for_pid-allow`**. Isso é concedido apenas a aplicativos da Apple.
* **Root pode acessar portas de tarefas** de aplicativos **não** compilados com um tempo de execução **fortificado** (e não da Apple).
### Injeção de Shellcode em thread via Porta da Tarefa
Você pode obter um shellcode de:
Você pode obter um shellcode em:
{% content-ref url="../../macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md" %}
[arm64-basic-assembly.md](../../macos-apps-inspecting-debugging-and-fuzzing/arm64-basic-assembly.md)
@ -543,7 +543,7 @@ gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
```
### Injeção de Dylib em thread via porta de tarefa
No macOS, as **threads** podem ser manipuladas através do **Mach** ou usando a API **posix `pthread`**. A thread que geramos na injeção anterior foi gerada usando a API Mach, portanto, **não é compatível com posix**.
No macOS, as **threads** podem ser manipuladas via **Mach** ou usando a **API posix `pthread`**. A thread que geramos na injeção anterior foi gerada usando a API Mach, portanto, **não é compatível com posix**.
Foi possível **injetar um shellcode simples** para executar um comando porque não era necessário trabalhar com APIs compatíveis com posix, apenas com Mach. Injeções **mais complexas** precisariam que a **thread** também fosse **compatível com posix**.
@ -751,7 +751,7 @@ kr = mach_vm_write(remoteTask, // Porta da tarefa
if (kr != KERN_SUCCESS)
{
fprintf(stderr, "Não foi possível escrever na memória do thread remoto: Erro %s\n", mach_error_string(kr));
fprintf(stderr, "Não foi possível escrever na memória da thread remota: Erro %s\n", mach_error_string(kr));
return (-3);
}
@ -761,7 +761,7 @@ kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EX
if (kr != KERN_SUCCESS)
{
fprintf(stderr, "Não foi possível definir as permissões de memória para o código do thread remoto: Erro %s\n", mach_error_string(kr));
fprintf(stderr, "Não foi possível definir as permissões de memória para o código da thread remota: Erro %s\n", mach_error_string(kr));
return (-4);
}
@ -770,12 +770,12 @@ kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_P
if (kr != KERN_SUCCESS)
{
fprintf(stderr, "Não foi possível definir as permissões de memória para a pilha do thread remoto: Erro %s\n", mach_error_string(kr));
fprintf(stderr, "Não foi possível definir as permissões de memória para a pilha da thread remota: Erro %s\n", mach_error_string(kr));
return (-4);
}
// Crie um thread para executar o shellcode
// Crie uma thread para executar o shellcode
struct arm_unified_thread_state remoteThreadState64;
thread_act_t remoteThread;
@ -791,14 +791,14 @@ remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT;
remoteThreadState64.ts_64.__pc = (u_int64_t)remoteCode64;
remoteThreadState64.ts_64.__sp = (u_int64_t)remoteStack64;
printf("Pilha remota 64 0x%llx, Código remoto é %p\n", remoteStack64, p);
printf("Pilha Remota 64 0x%llx, Código Remoto é %p\n", remoteStack64, p);
kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64,
(thread_state_t)&remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT, &remoteThread);
if (kr != KERN_SUCCESS)
{
fprintf(stderr, "Não foi possível criar o thread remoto: erro %s", mach_error_string(kr));
fprintf(stderr, "Não foi possível criar a thread remota: erro %s", mach_error_string(kr));
return (-3);
}

View file

@ -129,7 +129,7 @@ Lembre-se de que as restrições de **Validação de Biblioteca anteriores tamb
Do **`man dlopen`**:
* Quando o caminho **não contém um caractere de barra** (ou seja, é apenas um nome de folha), o **dlopen() fará uma busca**. Se **`$DYLD_LIBRARY_PATH`** foi definido no lançamento, o dyld primeiro **procurará nesse diretório**. Em seguida, se o arquivo mach-o chamador ou o executável principal especificar um **`LC_RPATH`**, o dyld **procurará nesses** diretórios. Em seguida, se o processo estiver **sem restrições**, o dyld procurará no **diretório de trabalho atual**. Por último, para binários antigos, o dyld tentará algumas alternativas. Se **`$DYLD_FALLBACK_LIBRARY_PATH`** foi definido no lançamento, o dyld procurará nesses diretórios, caso contrário, o dyld procurará em **`/usr/local/lib/`** (se o processo estiver sem restrições) e depois em **`/usr/lib/`** (essas informações foram retiradas do **`man dlopen`**).
* Quando o caminho **não contém um caractere de barra** (ou seja, é apenas um nome de folha), o **dlopen() fará a busca**. Se **`$DYLD_LIBRARY_PATH`** foi definido no lançamento, o dyld primeiro **procurará nesse diretório**. Em seguida, se o arquivo mach-o chamador ou o executável principal especificar um **`LC_RPATH`**, o dyld **procurará nesses** diretórios. Em seguida, se o processo estiver **sem restrições**, o dyld procurará no **diretório de trabalho atual**. Por último, para binários antigos, o dyld tentará algumas alternativas. Se **`$DYLD_FALLBACK_LIBRARY_PATH`** foi definido no lançamento, o dyld procurará nesses diretórios, caso contrário, o dyld procurará em **`/usr/local/lib/`** (se o processo estiver sem restrições) e depois em **`/usr/lib/`** (essas informações foram retiradas do **`man dlopen`**).
1. `$DYLD_LIBRARY_PATH`
2. `LC_RPATH`
3. `CWD`(se sem restrições)
@ -156,7 +156,7 @@ Se for um caminho de framework, a maneira de sequestrá-lo seria:
* Se o processo não tiver restrições, abusar do **caminho relativo do diretório de trabalho** e das variáveis de ambiente mencionadas (mesmo que não seja mencionado na documentação se o processo está restrito, as variáveis de ambiente DYLD\_\* são removidas)
{% endhint %}
* Quando o caminho **contém uma barra, mas não é um caminho de framework** (ou seja, um caminho completo ou um caminho parcial para um dylib), o dlopen() primeiro procura (se definido) em **`$DYLD_LIBRARY_PATH`** (com a parte final do caminho). Em seguida, o dyld **tenta o caminho fornecido** (usando o diretório de trabalho atual para caminhos relativos, mas apenas para processos não restritos). Por último, para binários mais antigos, o dyld tentará alternativas. Se **`$DYLD_FALLBACK_LIBRARY_PATH`** foi definido no lançamento, o dyld pesquisará nesses diretórios, caso contrário, o dyld procurará em **`/usr/local/lib/`** (se o processo não tiver restrições) e depois em **`/usr/lib/`**.
* Quando o caminho **contém uma barra, mas não é um caminho de framework** (ou seja, um caminho completo ou um caminho parcial para um dylib), o dlopen() primeiro procura (se definido) em **`$DYLD_LIBRARY_PATH`** (com a parte final do caminho). Em seguida, o dyld **tenta o caminho fornecido** (usando o diretório de trabalho atual para caminhos relativos, mas apenas para processos sem restrições). Por último, para binários mais antigos, o dyld tentará alternativas. Se **`$DYLD_FALLBACK_LIBRARY_PATH`** foi definido no lançamento, o dyld pesquisará nesses diretórios, caso contrário, o dyld procurará em **`/usr/local/lib/`** (se o processo não tiver restrições) e depois em **`/usr/lib/`**.
1. `$DYLD_LIBRARY_PATH`
2. caminho fornecido (usando o diretório de trabalho atual para caminhos relativos se não houver restrições)
3. `$DYLD_FALLBACK_LIBRARY_PATH`
@ -172,7 +172,7 @@ Se houver barras no nome e não for um framework, a maneira de sequestrá-lo ser
{% hint style="info" %}
Observação: Não existem arquivos de configuração para **controlar a busca do dlopen**.
Observação: Se o executável principal for um binário **set\[ug]id ou tiver assinatura com entitlements**, então **todas as variáveis de ambiente são ignoradas**, e apenas um caminho completo pode ser usado ([verifique as restrições do DYLD\_INSERT\_LIBRARIES](../../macos-dyld-hijacking-and-dyld\_insert\_libraries.md#check-dyld\_insert\_librery-restrictions) para obter informações mais detalhadas)
Observação: Se o executável principal for um binário **set\[ug\]id ou tiver assinatura com entitlements**, então **todas as variáveis de ambiente são ignoradas**, e apenas um caminho completo pode ser usado ([verifique as restrições do DYLD\_INSERT\_LIBRARIES](../../macos-dyld-hijacking-and-dyld\_insert\_libraries.md#check-dyld\_insert\_librery-restrictions) para obter informações mais detalhadas)
Observação: As plataformas da Apple usam arquivos "universais" para combinar bibliotecas de 32 bits e 64 bits. Isso significa que **não existem caminhos de busca separados para 32 bits e 64 bits**.
@ -228,13 +228,17 @@ Se você compilar e executar, você pode ver **onde cada biblioteca foi procurad
```bash
sudo fs_usage | grep "dlopentest"
```
## Remover variáveis de ambiente `DYLD_*` e `LD_LIBRARY_PATH`
## Desvio de Caminho Relativo
No arquivo `dyld-dyld-832.7.1/src/dyld2.cpp`, é possível encontrar a função **`pruneEnvironmentVariables`**, que irá remover qualquer variável de ambiente que **comece com `DYLD_`** e **`LD_LIBRARY_PATH=`**.
Se um **binário/aplicativo privilegiado** (como um SUID ou algum binário com privilégios poderosos) estiver **carregando uma biblioteca de caminho relativo** (por exemplo, usando `@executable_path` ou `@loader_path`) e tiver a **Validação de Biblioteca desativada**, pode ser possível mover o binário para um local onde o atacante possa **modificar a biblioteca carregada pelo caminho relativo** e abusá-la para injetar código no processo.
Também irá definir como **nulo** especificamente as variáveis de ambiente **`DYLD_FALLBACK_FRAMEWORK_PATH`** e **`DYLD_FALLBACK_LIBRARY_PATH`** para binários **suid** e **sgid**.
## Remover as variáveis de ambiente `DYLD_*` e `LD_LIBRARY_PATH`
Essa função é chamada a partir da função **`_main`** do mesmo arquivo, se o alvo for o OSX, da seguinte forma:
No arquivo `dyld-dyld-832.7.1/src/dyld2.cpp`, é possível encontrar a função **`pruneEnvironmentVariables`**, que removerá qualquer variável de ambiente que **comece com `DYLD_`** e **`LD_LIBRARY_PATH=`**.
Também definirá como **nulo** especificamente as variáveis de ambiente **`DYLD_FALLBACK_FRAMEWORK_PATH`** e **`DYLD_FALLBACK_LIBRARY_PATH`** para binários **suid** e **sgid**.
Essa função é chamada a partir da função **`_main`** do mesmo arquivo, se estiver direcionando o OSX da seguinte maneira:
```cpp
#if TARGET_OS_OSX
if ( !gLinkContext.allowEnvVarsPrint && !gLinkContext.allowEnvVarsPath && !gLinkContext.allowEnvVarsSharedCache ) {
@ -292,11 +296,13 @@ sudo chmod -s hello
The `__RESTRICT` section is a segment in macOS that is used to restrict the execution of certain processes. This section is designed to prevent unauthorized access and privilege escalation by limiting the capabilities of processes.
The `__restrict` segment is specifically used to enforce restrictions on library injection. Library injection is a technique where a malicious library is injected into a legitimate process, allowing the attacker to execute arbitrary code within the context of that process.
The `__restrict` segment is a specific area within the `__RESTRICT` section that contains code and data that is restricted from being modified or accessed by other processes. This segment is crucial for maintaining the integrity and security of the system.
By utilizing the `__restrict` segment, macOS can prevent library injection by restricting the loading of libraries from certain locations or by enforcing code signing requirements. This helps to ensure the integrity and security of the system by preventing unauthorized modifications to processes.
By leveraging vulnerabilities or weaknesses in the macOS system, an attacker may attempt to abuse the `__restrict` segment to inject malicious code or gain unauthorized access to sensitive information. This technique, known as macOS library injection, allows the attacker to execute arbitrary code within the context of a legitimate process.
It is important for developers and system administrators to understand the functionality of the `__RESTRICT` section and the `__restrict` segment in order to effectively secure macOS systems against privilege escalation and unauthorized access.
To protect against macOS library injection attacks, it is important to implement proper security measures such as regular system updates, using trusted software sources, and employing strong access controls. Additionally, monitoring for any suspicious activities or unauthorized modifications to the `__restrict` segment can help detect and mitigate potential attacks.
By understanding the purpose and significance of the `__RESTRICT` section and the `__restrict` segment, system administrators and security professionals can better safeguard their macOS systems against privilege escalation and unauthorized access.
```bash
gcc -sectcreate __RESTRICT __restrict /dev/null hello.c -o hello-restrict
DYLD_INSERT_LIBRARIES=inject.dylib ./hello-restrict