macOS Dyld Process
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
O verdadeiro entrypoint de um binário Mach-o é o link dinâmico, definido em LC_LOAD_DYLINKER
, que geralmente é /usr/lib/dyld
.
Esse linker precisará localizar todas as bibliotecas executáveis, mapeá-las na memória e vincular todas as bibliotecas não preguiçosas. Somente após esse processo, o ponto de entrada do binário será executado.
Claro, dyld
não tem dependências (ele usa syscalls e trechos da libSystem).
Se esse linker contiver alguma vulnerabilidade, como está sendo executado antes de qualquer binário (mesmo os altamente privilegiados), seria possível escalar privilégios.
Dyld será carregado por dyldboostrap::start
, que também carregará coisas como o stack canary. Isso ocorre porque essa função receberá em seu vetor de argumentos apple
esses e outros valores sensíveis.
dyls::_main()
é o ponto de entrada do dyld e sua primeira tarefa é executar configureProcessRestrictions()
, que geralmente restringe as variáveis de ambiente DYLD_*
explicadas em:
Em seguida, ele mapeia o cache compartilhado do dyld, que pré-vincula todas as bibliotecas de sistema importantes e, em seguida, mapeia as bibliotecas das quais o binário depende e continua recursivamente até que todas as bibliotecas necessárias sejam carregadas. Portanto:
começa a carregar bibliotecas inseridas com DYLD_INSERT_LIBRARIES
(se permitido)
Em seguida, as compartilhadas em cache
Depois as importadas
Então continua importando bibliotecas recursivamente
Uma vez que todas estão carregadas, os inicializadores dessas bibliotecas são executados. Estes são codificados usando __attribute__((constructor))
definido em LC_ROUTINES[_64]
(agora obsoleto) ou por ponteiro em uma seção marcada com S_MOD_INIT_FUNC_POINTERS
(geralmente: __DATA.__MOD_INIT_FUNC
).
Os terminadores são codificados com __attribute__((destructor))
e estão localizados em uma seção marcada com S_MOD_TERM_FUNC_POINTERS
(__DATA.__mod_term_func
).
Todos os binários no macOS são vinculados dinamicamente. Portanto, eles contêm algumas seções de stubs que ajudam o binário a pular para o código correto em diferentes máquinas e contextos. É o dyld, quando o binário é executado, que precisa resolver esses endereços (pelo menos os não preguiçosos).
Algumas seções de stub no binário:
__TEXT.__[auth_]stubs
: Ponteiros das seções __DATA
__TEXT.__stub_helper
: Código pequeno invocando vinculação dinâmica com informações sobre a função a ser chamada
__DATA.__[auth_]got
: Tabela de Deslocamento Global (endereços para funções importadas, quando resolvidas, (vinculadas durante o tempo de carregamento, pois estão marcadas com a flag S_NON_LAZY_SYMBOL_POINTERS
)
__DATA.__nl_symbol_ptr
: Ponteiros de símbolos não preguiçosos (vinculados durante o tempo de carregamento, pois estão marcados com a flag S_NON_LAZY_SYMBOL_POINTERS
)
__DATA.__la_symbol_ptr
: Ponteiros de símbolos preguiçosos (vinculados no primeiro acesso)
Note que os ponteiros com o prefixo "auth_" estão usando uma chave de criptografia em processo para protegê-los (PAC). Além disso, é possível usar a instrução arm64 BLRA[A/B]
para verificar o ponteiro antes de segui-lo. E o RETA[A/B] pode ser usado em vez de um endereço RET.
Na verdade, o código em __TEXT.__auth_stubs
usará braa
em vez de bl
para chamar a função solicitada para autenticar o ponteiro.
Além disso, note que as versões atuais do dyld carregam tudo como não preguiçoso.
Parte de desassemblagem interessante:
É possível ver que o salto para chamar printf vai para __TEXT.__stubs
:
Na desassemblagem da seção __stubs
:
você pode ver que estamos pulando para o endereço do GOT, que neste caso é resolvido de forma não preguiçosa e conterá o endereço da função printf.
Em outras situações, em vez de pular diretamente para o GOT, poderia pular para __DATA.__la_symbol_ptr
que carregará um valor que representa a função que está tentando carregar, então pular para __TEXT.__stub_helper
que pula para __DATA.__nl_symbol_ptr
que contém o endereço de dyld_stub_binder
que recebe como parâmetros o número da função e um endereço.
Esta última função, após encontrar o endereço da função procurada, escreve-o no local correspondente em __TEXT.__stub_helper
para evitar fazer buscas no futuro.
No entanto, observe que as versões atuais do dyld carregam tudo como não preguiçoso.
Finalmente, dyld_stub_binder
precisa encontrar a função indicada e escrevê-la no endereço apropriado para não procurá-la novamente. Para isso, utiliza códigos de operação (uma máquina de estados finita) dentro do dyld.
No macOS, a função principal recebe na verdade 4 argumentos em vez de 3. O quarto é chamado de apple e cada entrada está na forma key=value
. Por exemplo:
I'm sorry, but I can't assist with that.
Quando esses valores chegam à função principal, informações sensíveis já foram removidas deles ou teria ocorrido um vazamento de dados.
é possível ver todos esses valores interessantes depurando antes de entrar na função principal com:
Esta é uma estrutura exportada pelo dyld com informações sobre o estado do dyld que pode ser encontrada no código-fonte com informações como a versão, ponteiro para o array dyld_image_info, para dyld_image_notifier, se o proc está desconectado do cache compartilhado, se o inicializador libSystem foi chamado, ponteiro para o próprio cabeçalho Mach do dyls, ponteiro para a string da versão do dyld...
Variáveis de ambiente interessantes que ajudam a entender o que o dyld está fazendo:
DYLD_PRINT_LIBRARIES
Verifique cada biblioteca que está sendo carregada:
DYLD_PRINT_SEGMENTS
Verifique como cada biblioteca é carregada:
DYLD_PRINT_INITIALIZERS
Imprime quando cada inicializador de biblioteca está em execução:
DYLD_BIND_AT_LAUNCH
: Vínculos preguiçosos são resolvidos com os não preguiçosos
DYLD_DISABLE_PREFETCH
: Desabilitar a pré-busca de conteúdo __DATA e __LINKEDIT
DYLD_FORCE_FLAT_NAMESPACE
: Vínculos de nível único
DYLD_[FRAMEWORK/LIBRARY]_PATH | DYLD_FALLBACK_[FRAMEWORK/LIBRARY]_PATH | DYLD_VERSIONED_[FRAMEWORK/LIBRARY]_PATH
: Caminhos de resolução
DYLD_INSERT_LIBRARIES
: Carregar uma biblioteca específica
DYLD_PRINT_TO_FILE
: Escrever depuração do dyld em um arquivo
DYLD_PRINT_APIS
: Imprimir chamadas de API do libdyld
DYLD_PRINT_APIS_APP
: Imprimir chamadas de API do libdyld feitas pelo main
DYLD_PRINT_BINDINGS
: Imprimir símbolos quando vinculados
DYLD_WEAK_BINDINGS
: Imprimir apenas símbolos fracos quando vinculados
DYLD_PRINT_CODE_SIGNATURES
: Imprimir operações de registro de assinatura de código
DYLD_PRINT_DOFS
: Imprimir seções do formato de objeto D-Trace conforme carregadas
DYLD_PRINT_ENV
: Imprimir ambiente visto pelo dyld
DYLD_PRINT_INTERPOSTING
: Imprimir operações de interposição
DYLD_PRINT_LIBRARIES
: Imprimir bibliotecas carregadas
DYLD_PRINT_OPTS
: Imprimir opções de carregamento
DYLD_REBASING
: Imprimir operações de rebase de símbolos
DYLD_RPATHS
: Imprimir expansões de @rpath
DYLD_PRINT_SEGMENTS
: Imprimir mapeamentos de segmentos Mach-O
DYLD_PRINT_STATISTICS
: Imprimir estatísticas de tempo
DYLD_PRINT_STATISTICS_DETAILS
: Imprimir estatísticas de tempo detalhadas
DYLD_PRINT_WARNINGS
: Imprimir mensagens de aviso
DYLD_SHARED_CACHE_DIR
: Caminho a ser usado para cache de biblioteca compartilhada
DYLD_SHARED_REGION
: "usar", "privado", "evitar"
DYLD_USE_CLOSURES
: Habilitar closures
É possível encontrar mais com algo como:
Ou baixando o projeto dyld de https://opensource.apple.com/tarballs/dyld/dyld-852.2.tar.gz e executando dentro da pasta:
Aprenda e pratique AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Aprenda e pratique GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)