.. | ||
macos-ipc-inter-process-communication | ||
macos-library-injection | ||
macos-.net-applications-injection.md | ||
macos-chromium-injection.md | ||
macos-dirty-nib.md | ||
macos-electron-applications-injection.md | ||
macos-function-hooking.md | ||
macos-java-apps-injection.md | ||
macos-perl-applications-injection.md | ||
macos-python-applications-injection.md | ||
macos-ruby-applications-injection.md | ||
README.md |
macOS Process Abuse
{% hint style="success" %}
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
Informações Básicas sobre Processos
Um processo é uma instância de um executável em execução, no entanto, os processos não executam código, esses são threads. Portanto, os processos são apenas contêineres para threads em execução fornecendo a memória, descritores, portas, permissões...
Tradicionalmente, os processos eram iniciados dentro de outros processos (exceto PID 1) chamando fork
, que criaria uma cópia exata do processo atual e então o processo filho geralmente chamaria execve
para carregar o novo executável e executá-lo. Então, vfork
foi introduzido para tornar esse processo mais rápido sem cópias de memória.
Então posix_spawn
foi introduzido combinando vfork
e execve
em uma única chamada e aceitando flags:
POSIX_SPAWN_RESETIDS
: Redefinir ids efetivos para ids reaisPOSIX_SPAWN_SETPGROUP
: Definir afiliação do grupo de processosPOSUX_SPAWN_SETSIGDEF
: Definir comportamento padrão do sinalPOSIX_SPAWN_SETSIGMASK
: Definir máscara de sinalPOSIX_SPAWN_SETEXEC
: Exec no mesmo processo (comoexecve
com mais opções)POSIX_SPAWN_START_SUSPENDED
: Iniciar suspenso_POSIX_SPAWN_DISABLE_ASLR
: Iniciar sem ASLR_POSIX_SPAWN_NANO_ALLOCATOR:
Usar o alocador Nano do libmalloc_POSIX_SPAWN_ALLOW_DATA_EXEC:
Permitirrwx
em segmentos de dadosPOSIX_SPAWN_CLOEXEC_DEFAULT
: Fechar todas as descrições de arquivo no exec(2) por padrão_POSIX_SPAWN_HIGH_BITS_ASLR:
Randomizar os bits altos do deslizamento ASLR
Além disso, posix_spawn
permite especificar um array de posix_spawnattr
que controla alguns aspectos do processo gerado, e posix_spawn_file_actions
para modificar o estado dos descritores.
Quando um processo morre, ele envia o código de retorno para o processo pai (se o pai morreu, o novo pai é PID 1) com o sinal SIGCHLD
. O pai precisa obter esse valor chamando wait4()
ou waitid()
e até que isso aconteça, o filho permanece em um estado zumbi onde ainda está listado, mas não consome recursos.
PIDs
PIDs, identificadores de processo, identificam um processo único. No XNU, os PIDs são de 64 bits aumentando monotonamente e nunca se reiniciam (para evitar abusos).
Grupos de Processos, Sessões e Coalizões
Processos podem ser inseridos em grupos para facilitar seu manuseio. Por exemplo, comandos em um script de shell estarão no mesmo grupo de processos, então é possível sinalizá-los juntos usando kill, por exemplo.
Também é possível agrupar processos em sessões. Quando um processo inicia uma sessão (setsid(2)
), os processos filhos são colocados dentro da sessão, a menos que iniciem sua própria sessão.
Coalizão é outra maneira de agrupar processos no Darwin. Um processo que se junta a uma coalizão permite acessar recursos do pool, compartilhando um livro-razão ou enfrentando Jetsam. As coalizões têm diferentes papéis: Líder, serviço XPC, Extensão.
Credenciais e Personas
Cada processo mantém credenciais que identificam seus privilégios no sistema. Cada processo terá um uid
primário e um gid
primário (embora possa pertencer a vários grupos).
Também é possível mudar o id do usuário e do grupo se o binário tiver o bit setuid/setgid
.
Existem várias funções para definir novos uids/gids.
A syscall persona
fornece um conjunto alternativo de credenciais. Adotar uma persona assume seu uid, gid e associações de grupo de uma só vez. No código-fonte é possível encontrar a struct:
struct kpersona_info { uint32_t persona_info_version;
uid_t persona_id; /* overlaps with UID */
int persona_type;
gid_t persona_gid;
uint32_t persona_ngroups;
gid_t persona_groups[NGROUPS];
uid_t persona_gmuid;
char persona_name[MAXLOGNAME + 1];
/* TODO: MAC policies?! */
}
Informações Básicas sobre Threads
- POSIX Threads (pthreads): O macOS suporta threads POSIX (
pthreads
), que fazem parte de uma API de threading padrão para C/C++. A implementação de pthreads no macOS é encontrada em/usr/lib/system/libsystem_pthread.dylib
, que vem do projetolibpthread
disponível publicamente. Esta biblioteca fornece as funções necessárias para criar e gerenciar threads. - Criando Threads: A função
pthread_create()
é usada para criar novas threads. Internamente, essa função chamabsdthread_create()
, que é uma chamada de sistema de nível inferior específica para o kernel XNU (o kernel no qual o macOS é baseado). Esta chamada de sistema aceita várias flags derivadas depthread_attr
(atributos) que especificam o comportamento da thread, incluindo políticas de agendamento e tamanho da pilha.
- Tamanho da Pilha Padrão: O tamanho da pilha padrão para novas threads é de 512 KB, o que é suficiente para operações típicas, mas pode ser ajustado através de atributos de thread se mais ou menos espaço for necessário.
- Inicialização da Thread: A função
__pthread_init()
é crucial durante a configuração da thread, utilizando o argumentoenv[]
para analisar variáveis de ambiente que podem incluir detalhes sobre a localização e o tamanho da pilha.
Terminação de Threads no macOS
- Saindo de Threads: Threads são tipicamente terminadas chamando
pthread_exit()
. Esta função permite que uma thread saia de forma limpa, realizando a limpeza necessária e permitindo que a thread envie um valor de retorno de volta para qualquer thread que a tenha juntado. - Limpeza de Threads: Ao chamar
pthread_exit()
, a funçãopthread_terminate()
é invocada, que lida com a remoção de todas as estruturas de thread associadas. Ela desaloca portas de thread Mach (Mach é o subsistema de comunicação no kernel XNU) e chamabsdthread_terminate
, uma syscall que remove as estruturas de nível de kernel associadas à thread.
Mecanismos de Sincronização
Para gerenciar o acesso a recursos compartilhados e evitar condições de corrida, o macOS fornece várias primitivas de sincronização. Estas são críticas em ambientes de multi-threading para garantir a integridade dos dados e a estabilidade do sistema:
- Mutexes:
- Mutex Regular (Assinatura: 0x4D555458): Mutex padrão com uma pegada de memória de 60 bytes (56 bytes para o mutex e 4 bytes para a assinatura).
- Mutex Rápido (Assinatura: 0x4d55545A): Semelhante a um mutex regular, mas otimizado para operações mais rápidas, também com 60 bytes de tamanho.
- Variáveis de Condição:
- Usadas para esperar que certas condições ocorram, com um tamanho de 44 bytes (40 bytes mais uma assinatura de 4 bytes).
- Atributos de Variável de Condição (Assinatura: 0x434e4441): Atributos de configuração para variáveis de condição, com tamanho de 12 bytes.
- Variável Once (Assinatura: 0x4f4e4345):
- Garante que um trecho de código de inicialização seja executado apenas uma vez. Seu tamanho é de 12 bytes.
- Locks de Leitura-Gravação:
- Permite múltiplos leitores ou um escritor por vez, facilitando o acesso eficiente a dados compartilhados.
- Lock de Leitura-Gravação (Assinatura: 0x52574c4b): Tamanho de 196 bytes.
- Atributos de Lock de Leitura-Gravação (Assinatura: 0x52574c41): Atributos para locks de leitura-gravação, com 20 bytes de tamanho.
{% hint style="success" %} Os últimos 4 bytes desses objetos são usados para detectar estouros. {% endhint %}
Variáveis Locais de Thread (TLV)
Variáveis Locais de Thread (TLV) no contexto de arquivos Mach-O (o formato para executáveis no macOS) são usadas para declarar variáveis que são específicas para cada thread em uma aplicação multi-threaded. Isso garante que cada thread tenha sua própria instância separada de uma variável, proporcionando uma maneira de evitar conflitos e manter a integridade dos dados sem precisar de mecanismos de sincronização explícitos como mutexes.
Em C e linguagens relacionadas, você pode declarar uma variável local de thread usando a palavra-chave __thread
. Aqui está como funciona em seu exemplo:
cCopy code__thread int tlv_var;
void main (int argc, char **argv){
tlv_var = 10;
}
Este trecho define tlv_var
como uma variável local de thread. Cada thread que executa este código terá seu próprio tlv_var
, e as alterações que uma thread faz em tlv_var
não afetarão tlv_var
em outra thread.
No binário Mach-O, os dados relacionados a variáveis locais de thread são organizados em seções específicas:
__DATA.__thread_vars
: Esta seção contém os metadados sobre as variáveis locais de thread, como seus tipos e status de inicialização.__DATA.__thread_bss
: Esta seção é usada para variáveis locais de thread que não são explicitamente inicializadas. É uma parte da memória reservada para dados inicializados com zero.
Mach-O também fornece uma API específica chamada tlv_atexit
para gerenciar variáveis locais de thread quando uma thread sai. Esta API permite que você registre destrutores—funções especiais que limpam dados locais de thread quando uma thread termina.
Prioridades de Thread
Entender as prioridades de thread envolve observar como o sistema operacional decide quais threads executar e quando. Essa decisão é influenciada pelo nível de prioridade atribuído a cada thread. No macOS e em sistemas semelhantes ao Unix, isso é tratado usando conceitos como nice
, renice
e classes de Qualidade de Serviço (QoS).
Nice e Renice
- Nice:
- O valor
nice
de um processo é um número que afeta sua prioridade. Cada processo tem um valor nice que varia de -20 (a maior prioridade) a 19 (a menor prioridade). O valor nice padrão quando um processo é criado é tipicamente 0. - Um valor nice mais baixo (mais próximo de -20) torna um processo mais "egoísta", dando-lhe mais tempo de CPU em comparação com outros processos com valores nice mais altos.
- Renice:
renice
é um comando usado para alterar o valor nice de um processo que já está em execução. Isso pode ser usado para ajustar dinamicamente a prioridade dos processos, aumentando ou diminuindo sua alocação de tempo de CPU com base em novos valores nice.- Por exemplo, se um processo precisar de mais recursos de CPU temporariamente, você pode diminuir seu valor nice usando
renice
.
Classes de Qualidade de Serviço (QoS)
As classes de QoS são uma abordagem mais moderna para lidar com prioridades de thread, particularmente em sistemas como o macOS que suportam Grand Central Dispatch (GCD). As classes de QoS permitem que os desenvolvedores classifiquem o trabalho em diferentes níveis com base em sua importância ou urgência. O macOS gerencia a priorização de threads automaticamente com base nessas classes de QoS:
- Interativo do Usuário:
- Esta classe é para tarefas que estão atualmente interagindo com o usuário ou requerem resultados imediatos para proporcionar uma boa experiência ao usuário. Essas tarefas recebem a maior prioridade para manter a interface responsiva (por exemplo, animações ou manipulação de eventos).
- Iniciado pelo Usuário:
- Tarefas que o usuário inicia e espera resultados imediatos, como abrir um documento ou clicar em um botão que requer cálculos. Estas têm alta prioridade, mas abaixo da interativa do usuário.
- Utilitário:
- Essas tarefas são de longa duração e normalmente mostram um indicador de progresso (por exemplo, download de arquivos, importação de dados). Elas têm prioridade inferior em relação às tarefas iniciadas pelo usuário e não precisam ser concluídas imediatamente.
- Fundo:
- Esta classe é para tarefas que operam em segundo plano e não são visíveis para o usuário. Estas podem ser tarefas como indexação, sincronização ou backups. Elas têm a menor prioridade e impacto mínimo no desempenho do sistema.
Usando classes de QoS, os desenvolvedores não precisam gerenciar os números de prioridade exatos, mas sim focar na natureza da tarefa, e o sistema otimiza os recursos de CPU de acordo.
Além disso, existem diferentes políticas de agendamento de thread que fluem para especificar um conjunto de parâmetros de agendamento que o escalonador levará em consideração. Isso pode ser feito usando thread_policy_[set/get]
. Isso pode ser útil em ataques de condição de corrida.
Abuso de Processos no MacOS
O macOS, como qualquer outro sistema operacional, fornece uma variedade de métodos e mecanismos para processos interagirem, se comunicarem e compartilharem dados. Embora essas técnicas sejam essenciais para o funcionamento eficiente do sistema, elas também podem ser abusadas por agentes de ameaça para realizar atividades maliciosas.
Injeção de Biblioteca
A Injeção de Biblioteca é uma técnica em que um atacante força um processo a carregar uma biblioteca maliciosa. Uma vez injetada, a biblioteca é executada no contexto do processo alvo, fornecendo ao atacante as mesmas permissões e acesso que o processo.
{% content-ref url="macos-library-injection/" %} macos-library-injection {% endcontent-ref %}
Hooking de Função
O Hooking de Função envolve interceptar chamadas de função ou mensagens dentro de um código de software. Ao fazer o hooking de funções, um atacante pode modificar o comportamento de um processo, observar dados sensíveis ou até mesmo ganhar controle sobre o fluxo de execução.
{% content-ref url="macos-function-hooking.md" %} macos-function-hooking.md {% endcontent-ref %}
Comunicação entre Processos
A Comunicação entre Processos (IPC) refere-se a diferentes métodos pelos quais processos separados compartilham e trocam dados. Embora a IPC seja fundamental para muitas aplicações legítimas, ela também pode ser mal utilizada para subverter a isolação de processos, vazar informações sensíveis ou realizar ações não autorizadas.
{% content-ref url="macos-ipc-inter-process-communication/" %} macos-ipc-inter-process-communication {% endcontent-ref %}
Injeção de Aplicações Electron
Aplicações Electron executadas com variáveis de ambiente específicas podem ser vulneráveis à injeção de processos:
{% content-ref url="macos-electron-applications-injection.md" %} macos-electron-applications-injection.md {% endcontent-ref %}
Injeção de Chromium
É possível usar as flags --load-extension
e --use-fake-ui-for-media-stream
para realizar um ataque man in the browser permitindo roubar pressionamentos de tecla, tráfego, cookies, injetar scripts em páginas...:
{% content-ref url="macos-chromium-injection.md" %} macos-chromium-injection.md {% endcontent-ref %}
NIB Sujo
Arquivos NIB definem elementos da interface do usuário (UI) e suas interações dentro de um aplicativo. No entanto, eles podem executar comandos arbitrários e o Gatekeeper não impede que um aplicativo já executado seja executado se um arquivo NIB for modificado. Portanto, eles poderiam ser usados para fazer programas arbitrários executarem comandos arbitrários:
{% content-ref url="macos-dirty-nib.md" %} macos-dirty-nib.md {% endcontent-ref %}
Injeção de Aplicações Java
É possível abusar de certas capacidades do Java (como a variável de ambiente _JAVA_OPTS
) para fazer um aplicativo Java executar código/comandos arbitrários.
{% content-ref url="macos-java-apps-injection.md" %} macos-java-apps-injection.md {% endcontent-ref %}
Injeção de Aplicações .Net
É possível injetar código em aplicações .Net abusando da funcionalidade de depuração do .Net (não protegida por proteções do macOS, como o endurecimento em tempo de execução).
{% content-ref url="macos-.net-applications-injection.md" %} macos-.net-applications-injection.md {% endcontent-ref %}
Injeção de Perl
Verifique diferentes opções para fazer um script Perl executar código arbitrário em:
{% content-ref url="macos-perl-applications-injection.md" %} macos-perl-applications-injection.md {% endcontent-ref %}
Injeção de Ruby
Também é possível abusar de variáveis de ambiente Ruby para fazer scripts arbitrários executarem código arbitrário:
{% content-ref url="macos-ruby-applications-injection.md" %} macos-ruby-applications-injection.md {% endcontent-ref %}
Injeção de Python
Se a variável de ambiente PYTHONINSPECT
estiver definida, o processo Python entrará em um CLI Python assim que terminar. Também é possível usar PYTHONSTARTUP
para indicar um script Python a ser executado no início de uma sessão interativa.
No entanto, observe que o script PYTHONSTARTUP
não será executado quando PYTHONINSPECT
criar a sessão interativa.
Outras variáveis de ambiente, como PYTHONPATH
e PYTHONHOME
, também podem ser úteis para fazer um comando Python executar código arbitrário.
Observe que executáveis compilados com pyinstaller
não usarão essas variáveis ambientais, mesmo que estejam sendo executados usando um Python embutido.
{% hint style="danger" %}
No geral, não consegui encontrar uma maneira de fazer o Python executar código arbitrário abusando de variáveis de ambiente.
No entanto, a maioria das pessoas instala Python usando Homebrew, que instalará Python em um local gravável para o usuário admin padrão. Você pode sequestrá-lo com algo como:
mv /opt/homebrew/bin/python3 /opt/homebrew/bin/python3.old
cat > /opt/homebrew/bin/python3 <<EOF
#!/bin/bash
# Extra hijack code
/opt/homebrew/bin/python3.old "$@"
EOF
chmod +x /opt/homebrew/bin/python3
Mesmo root executará este código ao rodar python.
{% endhint %}
Detecção
Shield
Shield (Github) é um aplicativo de código aberto que pode detectar e bloquear ações de injeção de processo:
- Usando Variáveis Ambientais: Ele monitorará a presença de qualquer uma das seguintes variáveis ambientais:
DYLD_INSERT_LIBRARIES
,CFNETWORK_LIBRARY_PATH
,RAWCAMERA_BUNDLE_PATH
eELECTRON_RUN_AS_NODE
- Usando chamadas
task_for_pid
: Para descobrir quando um processo deseja obter o port de tarefa de outro, o que permite injetar código no processo. - Parâmetros de aplicativos Electron: Alguém pode usar o argumento de linha de comando
--inspect
,--inspect-brk
e--remote-debugging-port
para iniciar um aplicativo Electron em modo de depuração e, assim, injetar código nele. - Usando symlinks ou hardlinks: Normalmente, o abuso mais comum é colocar um link com nossos privilégios de usuário e apontá-lo para um local de maior privilégio. A detecção é muito simples tanto para hardlinks quanto para symlinks. Se o processo que cria o link tem um nível de privilégio diferente do arquivo de destino, criamos um alerta. Infelizmente, no caso de symlinks, o bloqueio não é possível, pois não temos informações sobre o destino do link antes da criação. Esta é uma limitação do framework EndpointSecurity da Apple.
Chamadas feitas por outros processos
No este post do blog você pode encontrar como é possível usar a função task_name_for_pid
para obter informações sobre outros processos que injetam código em um processo e, em seguida, obter informações sobre esse outro processo.
Observe que, para chamar essa função, você precisa ser o mesmo uid que o que está executando o processo ou root (e ela retorna informações sobre o processo, não uma maneira de injetar código).
Referências
- https://theevilbit.github.io/shield/
- https://medium.com/@metnew/why-electron-apps-cant-store-your-secrets-confidentially-inspect-option-a49950d6d51f
{% hint style="success" %}
Aprenda e pratique Hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP: HackTricks Training GCP Red Team Expert (GRTE)
Support HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para os repositórios do HackTricks e HackTricks Cloud.