Como agressores abusam da Manipulação de Token de Acesso (ATT&CK T1134)
Em nosso post do blog anterior sobre tokens de acesso do Windows para profissionais de segurança, abordamos:
- A relação entre sessões de logon e tokens de acesso
- Como a autenticação de rede funciona em ambientes Windows
Depois de abordar alguns dos principais conceitos de segurança do Windows, agora vamos nos basear nesse conhecimento e começar a analisar como os invasores podem abusar da funcionalidade legítima do Windows para se mover lateralmente e comprometer os domínios do Active Directory.
Este blog tentou deliberadamente abstrair o funcionamento de protocolos específicos de autenticação de rede do Windows (por exemplo, NTLM e Kerberos) sempre que possível. Como consequência, pode haver casos em que o comportamento exclusivo desses protocolos seja diferente do comportamento descrito abaixo. Ele também pressupõe algum conhecimento básico do protocolo de autenticação Kerberos1.
Além disso, o material abordado nesta série de posts foi usado para uma apresentação do BlackHat 2020, “Detectando a manipulação de tokens de acesso”. A apresentação pode ser encontrada aqui e os slides aqui.
Manipulação de tokens de acesso (técnica ATT & CK: T1134)
Depois de explicar os princípios básicos de como as sessões de logon e os tokens de acesso funcionam em nossos posts anteriores do blog, tanto localmente quanto para aplicativos distribuídos, esta seção explicará como os invasores podem abusar dos tokens de acesso e atingir as relações de confiança fundamentais nos domínios do Windows para comprometer redes inteiras. O objetivo desta seção é descrever as técnicas de manipulação de tokens de acesso usadas pelos invasores no contexto de um comprometimento simulado.
Como nota, já existe um extenso conjunto de excelentes pesquisas sobre manipulação de tokens de acesso (que serão abordadas liberalmente ao longo deste post). Este blog tenta desenvolver esse corpo de conhecimento considerando a manipulação de tokens de acesso a partir de uma abordagem diferente, ou seja, por meio da relação entre tokens de acesso, sessões de logon e credenciais armazenadas em cache. Na opinião do autor, qualquer descrição da manipulação de tokens sem considerar essas relações representa apenas a ponta do iceberg. Como consequência, a definição deste blog sobre manipulação de tokens de acesso talvez seja muito mais ampla do que comumente se entende.
Compromisso inicial
Caso um invasor obtenha uma posição em uma rede por meio de spear phishing, ele normalmente acabará com um shell em execução no contexto de segurança do usuário comprometido. Isso pode ser feito gerando um novo processo ou injetando diretamente na memória (dependendo da carga útil), mas o resultado final é o mesmo: o código do invasor está sendo executado em um processo que tem um token de acesso pertencente ao usuário comprometido.
Isso significa que qualquer verificação de acesso local usará o token de acesso do usuário comprometido e qualquer tentativa de autenticação remota usará as credenciais em cache do usuário comprometido 2. Portanto, o invasor pode, tanto localmente quanto em toda a rede, realizar todas as ações que o usuário comprometido. Por exemplo, se algum aplicativo web interno usar o Windows SSO, um invasor poderá acessá-lo como se fosse o usuário.
Manipulação de Token: A “Arte do Possível”
Normalmente, um invasor deseja passar do endpoint comprometido para outro host o mais rápido possível3. Ao considerar o movimento lateral do ponto de vista da manipulação de tokens, o invasor tem efetivamente três opções4, cada uma delas limitada pela relação fundamental entre tokens de acesso, sessões de logon e credenciais armazenadas em cache, conforme ilustrado abaixo:

Se um invasor quiser se mover lateralmente por meio do Windows SSO, todos esses três links devem estar em vigor (por exemplo, eles têm um identificador para um token). que está vinculado a uma sessão de logon apoiada por suas credenciais de destino). Caso contrário, a liberdade de movimento de um invasor depende da criação de novos links (por exemplo, novas sessões de logon) ou da modificação dos existentes (por exemplo, da alteração das credenciais em cache ou da sessão de logon para a qual o token de acesso aponta). Essas restrições são discutidas com mais detalhes nas três opções abaixo:
1. Roubar o token de um usuário privilegiado já conectado (logon sem rede)
Se outro usuário privilegiado já estiver conectado ao host comprometido, um invasor poderá aumentar seus privilégios e obter um identificador para um token de acesso representando esse usuário. Independentemente de o invasor se passar pelo token roubado ou iniciar um novo processo, se esse token estiver vinculado a uma sessão de logon que não seja da rede, ele terá as credenciais armazenadas em cache e, portanto, o invasor poderá se autenticar imediatamente em outro host 5. Portanto, essa técnica permite que um invasor use as credenciais de outro usuário para acessar hosts remotos na rede (via Windows SSO) e, logo, alterne sem precisar despejar as credenciais 6.
Como nota, os ataques de manipulação de tokens geralmente se relacionam a dois objetivos distintos: movimentação lateral (com a qual este blog se preocupa) e escalonamento de privilégios locais 7. O roubo de tokens tende a estar associado ao último (por exemplo, roubar/personificar um token com o objetivo de contornar as verificações de acesso local, em vez de usar as credenciais em cache para autenticação remota) e, portanto, este blog não discutirá o assunto com mais detalhes, mas os seguintes recursos são úteis para leitura posterior:
- https://posts.specterops.io/understanding-and-defending-against-access-token-theft-finding-alternatives-to-winlogon-exe-80696c8a73b
- https://foxglovesecurity.com/2016/09/26/rotten-potato-privilege-escalation-from-service-accounts-to-system/
- https://labs.f-secure.com/assets/BlogFiles/mwri-security-implications-of-windows-access-tokens-2008-04-14.pdf
2. Criar uma nova sessão de login com credenciais roubadas e personificar o token retornado ou gerar um novo processo com ele
Nesse caso, não há nenhum usuário privilegiado já conectado (e, portanto, nenhum token de acesso/sessão de login útil correspondente), mas o invasor ainda precisa encontrar uma maneira de alterar seu contexto de segurança.
Portanto, o invasor deve encontrar credenciais em outro lugar e usar essas credenciais roubadas para criar uma nova sessão de logon como usuário comprometido. Como o Windows armazenará automaticamente as credenciais de determinados tipos de logon, o invasor agora pode obter um token de acesso recém-criado, que é respaldado pelas credenciais roubadas. Depois que o invasor identifica um token representando o usuário comprometido, ele pode se autenticar imediatamente usando o processo padrão de autenticação de sign-on único do Windows.
Normalmente, as credenciais de texto simples são encontradas pelos atacantes por meio do Kerberoasting ou da busca por credenciais de texto simples não seguras em todos os recursos acessíveis, como compartilhamentos de rede, Sharepoint, wikis internos, GitHub corporativo, Zendesk, etc.8
3. Alterar as credenciais em cache associadas ao token de acesso atual para credenciais roubadas (por exemplo, legitimamente por meio de uma API ou “ilegitimamente”modificando diretamentea memória lsass)
Nesse cenário, em vez de criar uma nova sessão de login, o invasor modifica as credenciais em cache associadas ao token de acesso atual (e, portanto, à sessão de logon). Como veremos, muitos provedores de suporte de segurança (SSPs) do Windows oferecem formas nativas de fazer isso (e que não exigem privilégios elevados).
Como alternativa, os invasores podem seguir a rota “direta” e modificar manualmente as credenciais em cache armazenadas no lsass. Isso requer privilégios elevados para obter um identificador de gravação (por exemplo, PROCESS_VM_WRITE) para passar por meio do OpenProcess. Isso é típico de ataques do tipo pass-the-hash, como abordaremos mais adiante.
Ataques de manipulação de tokens de acesso
Este post do blog analisará quatro técnicas comuns usadas por atacantes (todas elas podem ser classificadas como variações da opção 3 acima):
- O sinalizador NETONLY
- Pass-The-Ticket
- Pass-The-Hash
- Overpass-The-Hash
1. O sinalizador NETONLY
A API do Windows fornece a função LogonUser para criar uma nova sessão de login para um determinado usuário (ou principal)9:
BOOL LogonUserW(
LPCWSTR lpszUsername,
LPCWSTR lpszDomain,
LPCWSTR lpszPassword,
DWORD dwLogonType,
DWORD dwLogonProvider,
PHANDLE PHToken)
;
O principal parâmetro a ser observado aqui está o dwLogonType, que especifica o tipo de logon a ser executado. Por exemplo, no caso de um usuário fazer login físico em sua estação de trabalho, ela será definida como LOGON32_LOGON_INTERACTIVE. O tipo de logon especificado determinará o tipo e os privilégios do token retornado.
Por exemplo, no caso de um logon interativo, o LogonUserW retornará um token de acesso primário e, se o UAC estiver ativado, esse token será um token filtrado (o que significa que terá integridade média e não elevado). Existe uma exceção: se o usuário for uma conta de administrador local (por exemplo, um SID *-500), o Windows retornará automaticamente um token 10elevado.
No caso de um logon de rede (LOGON32_LOGON_NETWORK), um token de representação é retornado (já que normalmente seria usado por um servidor para realizar trabalhos em nome de clientes remotos). Além disso, se o usuário estiver no grupo de administradores locais, o token será elevado e terá todos os privilégios habilitados11.
Essas permutações de LogonUser são capturadas na tabela abaixo:
dwLogonType | Token devolvido | Credenciais de cache? | O token devolvido é elevado? (se for administrador) |
Interativo ((LOGON32_LOGON_INTERACTIVE) | Primário | Sim | Não (aplica-se o UAC) |
Interativo (conta de administrador local, por exemplo, rid-500) | Primário | Sim | Sim |
Rede (LOGON32_LOGON_NETWORK) | Representação | Não12 | Sim (+ todos os privilégios ativados) |
Rede (Conta de administrador local, por exemplo, rid-500 | Representação | Não | Depende das configurações remotas do UAC13 |
O ponto principal é que LogonUser retorna um identificador para um um token recém-criado, que agora pode ser usado para representação.
Se o token retornado for um token primário, ele deverá primeiro ser convertido em um token de representação via DuplicateTokenEx, passando um TokenType de TokenImpersonate 14:
,: BOOL DuplicateTokenEx(
HANDLE, HExistingToken,
DWORD, dwDesiredAccess,
LPSECURITY_ATTRIBUTES, lpTokenAttributes,
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TOKEN_TYPE,
PHANDLE, phNewToken
);
A função SetThreadToken pode então ser usada para atribuir o token de representação retornado ao thread atual:
BOOL setThreadToken(
PHANDLE Thread,
HANDLE Token
);
Como alternativa, a API do Windows fornece a função ImpersonateLoggedOnUser, que permitirá que o thread de chamada se faça passar pelo contexto de segurança do usuário representado pelo token passado:
BOOL ImpersonateLoggedOnUser(
HANDLE hToken
);
impersonateLoggedOnUser tem o benefício adicional de verificar automaticamente o tipo do token passado e converta-o em um token de representação (via ntDuplicateToken) se um token primário foi passado (já que esse tipo de token não pode ser usado por um thread para representar)15.
Observe que, do ponto de vista da evasão de defesa, essas duas APIs de representação são agrupamentos leves sobre a syscall não documentada NTSetInformationThread (por exemplo, chamada com uma ThreadInformationClass de Token de representação de tópico). Portanto, eles são um bom alvo para os invasores usarem syscalls diretas para contornar os ganchos do modo de usuário por meio de técnicas como https://github.com/jthuraisamy/SysWhispers.
Além disso, é importante enfatizar que o Windows tem regras rígidas sobre falsificação de identidade. Elas estão listadas abaixo e foram retiradas da página MSDN para ImpersonateLoggedOnUser:
Todas as funções de representação, incluindo ImpersonateLoggedOnUser, permitem a representação solicitada se uma das seguintes afirmações for verdadeira:
- O nível de representação solicitado do token é menor que SecurityImpersonation, como SecurityIdentification ou SecurityAnonymous
- O chamador tem o privilégio seImpersonatePrivilege.
- Um processo (ou outro processo na sessão de login do chamador) criou o token usando credenciais explícitas por meio da função LogonUser ou LsalogonUser.
A identidade autenticada é a mesma do chamador
Além disso, o nível de integridade do token representado também deve ser menor ou igual ao nível de integridade do processo de chamada, caso contrário, a chamada de representação também falhará16. Portanto, supondo que um invasor não elevado faça login em um usuário administrador interativamente por meio de credenciais roubadas e o UAC esteja ativado, ele receberá de volta um token não elevado (por exemplo, filtrado) e, portanto, não terá problemas em se passar pelo usuário devolvido e se mover lateralmente, etc.
“O curioso sinalizador/NETONLY”17
No entanto, um invasor pode descobrir que a tentativa de fazer login em um usuário com credenciais roubadas falha. Isso pode ser devido a vários motivos, como as credenciais serem válidas, mas a conta não tem permissões para fazer login nessa estação de trabalho específica, elas são válidas apenas em um domínio diferente, etc. Além disso, o invasor também pode querer evitar totalmente o login em uma conta altamente privilegiada, pois isso pode parecer altamente anômalo em certos contextos (por exemplo, um administrador de domínio fazendo login no host de um usuário corporativo com poucos privilégios deve ser extremamente suspeito).18
Nesse cenário, o sinalizador LOGON32_LOGON_NEW_CREDENTIALS vem em socorro do invasor. Se um invasor chamar a função LogonUserW com esse sinalizador e passar um conjunto válido de credenciais (por exemplo, encontrado ao vasculhar compartilhamentos de arquivos), o Windows permitirá que o chamador duplique seu token atual, mas o direcionará para uma nova sessão de logon, conhecida como sessão de logon de Novas Credenciais, que armazena em cache as credenciais roubadas. Como resultado, o usuário ainda tem o mesmo contexto de segurança localmente (por exemplo, ele ainda tem uma cópia do mesmo token de acesso; ele apenas aponta para uma nova sessão de logon); no entanto, qualquer tentativa de autenticação remota fornecerá as novas credenciais passadas na chamada para LogonUserW19. Isso é ilustrado no diagrama abaixo:

Portanto, o sinalizador LOGON32_LOGON_NEW_CREDENTIALS fornece um mecanismo nativo para tornar seu acesso atualo token aponta para uma sessão de logon diferente e, portanto, credenciais diferentes.20
Observe que chamar LogonUserW com o sinalizador LOGON32_LOGON_NEW_CREDENTIALS não valida as credenciais quando a chamada é feita (elas podem ser totalmente inúteis), mas só são validadas por um controlador de domínio no momento de qualquer solicitação de autenticação remota.
Como outro exemplo, uma rápida revisão do código da tarefa 'MakeToken' a partir do código aberto .NET C2 framework Covenant revela exatamente a mesma abordagem: ele usa uma combinação de nome de usuário/senha e cria uma nova sessão/token de logon com eles passando o sinalizador LOGON32_LOGON_NEW_CREDENTIALS antes de continuar representando o token retornado.
Além disso, você pode replicar exatamente o mesmo comportamento com createProcessWithLogonW passando um dwLogonFlags de LOGON_NETCREDENTIALS_ONLY. 21
BOOL CreateProcessWithLogonW(
LPCWSTR lpUsername,
LPCWSTR lpDomain,
LPCWSTR lpPassword,
DWORD dwLogonFlags,
LPCWSTR lpApplicationName,
LPWSTR LP Commandline,
DWORD
DWCreationFlags, LPVOID LP Environment,
LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW, lpStartupInfo,
LPPROCESS_INFORMATION (lpProcessInformation
);
A principal diferença é que isso envolve a geração de um novo processo com o token retornado, em oposição à representação intra-processo discutido anteriormente. Na verdade, o utilitário incorporado do Windows, runas, é um invólucro simples em torno de createProcessWithLogonW e o sinalizador /NETONLY fornece uma maneira nativa de gerar um novo processo com diferentes credenciais somente de rede, conforme demonstrado abaixo:

Exatamente da mesma forma descrita anteriormente, o novo prompt de comando parece estar sendo executado localmente pelo mesmo usuário (ou seja, os atributos armazenados em cache no token são os mesmos para qualquer verificação de acesso local; portanto, whoami retorna 'astro\ cosmo'), mas qualquer tentativa de autenticação remota será realizada usando as credenciais roubadas do usuário 'ASTRO\ Administrator'.
Essas sessões de logon podem ser visualizadas usando a ferramenta LogonSessions da SysInternals. As sessões de logon que foram criadas com o sinalizador NewCredentials podem ser determinadas pelo campo Tipo de logon, conforme mostrado abaixo:

Além disso, as anômalas sessões de login do NewCredentials (por exemplo, produzidas por meio do gadget NETONLY) deixam artefatos nos registros de eventos do Windows. Eles podem ser identificados por meio do ID de evento 4642 e de um LogonType de 9. Um exemplo é mostrado na imagem abaixo:

Observe que o usuário original é mostrado pelo campo SubjectUsername e somente pela rede especificada as credenciais (por exemplo, as credenciais passadas) são exibidas nos campos targetOutboundUser/DomainName.22
Elevação automática
Outra peculiaridade do ponto de vista de escalonamento de privilégios local é que, para contas rid-500, createProcessWithLogonW elevará automaticamente o token retornado para logons interativos (por exemplo, ignorará o UAC). Portanto, createProcessWithLogonW pode receber uma conta de administrador local/de domínio para executar um processo elevado a partir de um contexto médio/não elevado.
Esse comportamento pode ser verificado usando runas. Por exemplo, quando runas é usado para gerar um processo usando uma conta de administrador local (por exemplo, runas /user: "Administrator" cmd.exe), o processo resultante será elevado (por exemplo, alta integridade). No entanto, quando uma conta não rid-500 é usada (mas que ainda está no grupo de administradores locais), o processo resultante não será elevado (por exemplo, será um token filtrado/integridade média).
Observe que esse comportamento é consistente com as permutações listadas para LogonUserW na Tabela 1. Portanto, um invasor não elevado também pode fazer login em um usuário administrador (não rid-500) como logon de rede e receber um token elevado com todos os privilégios habilitados.
No entanto, de acordo com as regras de representação descritas anteriormente, o invasor não deve realmente ser capaz de fazer nada com esse token, pois qualquer tentativa de personificar o token elevado deve falhar, pois ele tem um nível de integridade mais alto do que o chamador. No entanto, na verdade, é possível duplicar o token elevado, reduzir o nível de integridade do token copiado para médio (o NB 'isElevated' ainda é verdadeiro)23 e começar a representar o token elevado a partir de um contexto de integridade não elevada/média 24. Portanto, do ponto de vista do token de representação, você pode ignorar o comportamento padrão do Windows de apenas elevar determinadas contas e se passar por um token elevado, independentemente de a conta ser uma conta rid-500 ou não.
Criação do processo
Observe que, por padrão, quando você cria um processo secundário, ele herda seu token primário, mesmo que você esteja se passando por outro contexto de segurança 25. Por exemplo, se você estiver se passando por um token SYSTEM e chamar CreateProcess(), ele ainda herdará uma cópia do token do processo primário (em vez de herdar o contexto de segurança SYSTEM do thread).26
Portanto, se um invasor quiser gerar um novo processo em um contexto de segurança diferente, ele pode
- :Usar createProcessWithLogonW com credenciais explícitas (conforme discutido anteriormente)
- Chamar createProcessWithTokenW ou createProcessAsUserW e passar um identificador para um token (por exemplo, com o token retornado do LogonUser ou, mais comumente, por meio de um token roubado)
Ambas as funções podem ser passadas como um identificador para um token que representa o contexto de segurança do novo processo.27
BOOL CreateProcessWithTokenW (
HANDLE hToken,
DWORD dwLogonFlags,
LPCWSTR lpApplicationName,
LPWSTR lp Commandline,
DWORD dwCreationFlags,
LPVOID lp Environment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcess Information
);
BOOL CreateProcessAsUserW (
HANDLE hToken,
LPCWSTR lpApplicationName,
LPWSTR lpCommandline,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL binHeritHandles,
DWORD dwCreationFlags,
LPVOID, lpEnvironment,
LPCWSTR, lpCurrentDirectory,
LPSTARTUPINFOW, lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
Por exemplo, CreateProcessAsUserW normalmente é usado pelo próprio sistema operacional para gerar o shell do usuário após um logon bem-sucedido (também é usado pelo serviço de Logon Secundário quando um usuário chama creatProcessWithLogon (W). Nesse sentido, ele permite que um usuário “injete um processo na sessão de logon de sua escolha”28. Como observação, essas duas APIs são agrupamentos em torno de CreateProcessInternalW (localizado em KernelBase.dll).
A principal diferença aqui é que o chamador deve ter certos privilégios para chamar essas duas APIs29. Do ponto de vista do invasor, porém, o objetivo aqui é o mesmo; obter a execução do código no contexto de segurança do usuário alvo com o objetivo de se mover lateralmente.
Uma peculiaridade interessante é que a estrutura do PowerShell Empire foi forçada a adotar essa abordagem de geração de processos (que é sem dúvida muito mais ruidosa do ponto de vista da detecção) devido às limitações de como o PowerShell lida com personificação e multithreading, conforme as notas aquiexplicam com mais detalhes.
De qualquer forma, o fluxo de trabalho para usar técnicas de manipulação de tokens de geração de processos permanece o mesmo. Depois que o invasor obtém um identificador para o token (via OpenProcess/OpenProcessToken, se for o token primário, ou OpenThread/OpenThreadToken (no caso de representação de um thread), o invasor deve chamar DuplicateTokenEx para criar uma cópia local (primária) do token de destino e, em seguida, fornecer essa cópia às funções createProcessWithTokenW ou createProcessAsUserW.
Observe que, novamente, nesse caso, os invasores estão interessados apenas em sessões de logon privilegiadas que não sejam logins de rede, pois os logins de rede não armazenam credenciais em cache e, portanto não podem se autenticar em outros hosts.
2. Pass-The-Ticket
O Windows fornece um método nativo para executar uma técnica muito semelhante ao sinalizador NETONLY usando o Kerberos30. Essa técnica é ainda mais poderosa no sentido de que não exige que um invasor crie uma nova sessão de logon, mas altere arbitrariamente as credenciais do Kerberos armazenadas em cache (por exemplo, TGT) associadas à sua sessão de logon (e, portanto, ao token de acesso atual), conforme demonstrado abaixo:

Para começar a interagir com o SSP Kerberos e gerenciar o cache de tíquetes Kerberos, um processo pode chamar LsaCallAuthenticationPackage (localizado em Sspicl.dll):
NTSTATUS LsaCallAuthenticationPackage(
HANDLE LsaHandle,
ULONG AuthenticationPackage,
PVOID ProtocolSubmitBuffer,
ULONG SubmitBufferLength,
PVOID *ProtocolReturnBuffer,
PULONG ReturnBuffer,
PNT STATUS
);
Observe que o usuário precisará ter chamado anteriormente LsaConnectUntrusted para obter um identificador de conexão com o servidor LSA e LsaLookupAuthenticationPackage para encontrar o id do pacote kerberos (MICROSOFT_KERBEROS_NAME_A). Além disso, a inspeção dessas funções no IDA (novamente, elas podem estar localizadas em Sspicl.dll) revelará que elas estão se conectando ao Lsa via RPC.31.
Por meio do LsaCallAuthenticationPackage, um usuário pode fazer várias solicitações confidenciais, embora as solicitações exatas disponíveis para o usuário dependam de serem elevadas ou não. Por exemplo, um usuário não elevado pode realizar ações básicas de gerenciamento de tickets32, como enumerar seus tickets ativos atuais, limpar o cache de tickets e aplicar tickets arbitrários à sua sessão de logon atual 33. Portanto, isso permite efetivamente que um usuário altere as credenciais armazenadas em cache com sua sessão de logon atual e, portanto, especifique credenciais arbitráriassomente de rede.
Além disso, a partir de um contexto elevado 34, um invasor pode enumerar e descartar tickets (por exemplo, credenciais) pertencentes a outros usuários, fornecendo, portanto, uma funcionalidade semelhante à mimikatz sem precisarabrir um identificador para a lsass35.
Uma lista completa dos tipos de mensagens que podem ser enviadas para o pacote de autenticação Kerberos pode ser encontrada aqui. Para alterar o TGT atual associado a uma determinada sessão de logon, pode-se passar o KerbSubmitTicketMessage, que usa a seguinte estrutura de mensagem:
estrutura Typedef _KERB_SUBMIT_TKT_REQUEST {
KERB_PROTOCOL_MESSAGE_TYPE MessageType;
LUID LogonId;
ULONG Flags;
KERB_CRYPTO_KEY32 Key;
ULONG KerbCredSize;
ULONG KerbCredOffset;
} KERB_SUBMIT_TKT_REQUEST, *PKERB_SUBMIT_TKT_REQUEST
Portanto, para um KerbSubmitTicketMessage, o parâmetro ProtocolSubmitBuffer simplesmente aponta para um bloco de memória que consiste em uma estrutura KERB_SUBMIT_TKT_REQUESTseguida imediatamente por um Tíquete Kerberos codificado em ASN (que é o tíquete a ser aplicado à sessão de logon especificada). O código relevante em mimikatz para enviar solicitações de KerbSubmitTicketMessage pode ser encontrado aqui e no Rubeus aqui.
Após a chamada para LsaCallAuthenticationPackage, o TGT do usuário agora foi atualizado para o tíquete roubado. Desse ponto em diante, qualquer tentativa de acessar recursos de rede por qualquer processo/thread vinculado ao token de acesso/sessão de logon interativo do usuário será autenticada automaticamente pelo Kerberos usando o TGT roubado (por exemplo, solicitando tíquetes de serviço/TGs diferentes para recursos em todo o domínio).
Observe que um usuário só pode ter um TGT associado à sua sessão de logon atual. Portanto, a aplicação de um novo tíquete apagará o tíquete anterior do usuário. E se um invasor quiser preservar seu TGT atual? Nesse caso, mais uma vez, o sinalizador NETONLY vem em socorro - um invasor pode criar um processo NETONLY “sacrificial” por meio de CreateProcessWithLogonW com credenciais arbitrárias/inúteis. Isso criará um novo processo fictício e, o mais importante, uma nova sessão de logon (e, portanto, um token de acesso) à qual um TGT roubado pode ser aplicado (e, portanto, preservar o tíquete atual do usuário)36.
Uma conclusão importante a ser extraída dessa técnica para profissionais de defesa é que, como toda a atividade é executada por meio de LsaCallAuthenticationPackage (e, portanto, por RPC), ela não requer nenhuma interação direta com lsass (N.B. direct aqui se refere à abertura de um identificador para lsass via OpenProcess). Além disso, para esse caso de uso específico (ptt), toda a atividade é via RPC local até que um invasor tente se autenticar em um host remoto (o que gerará novos logons).
Como outro exemplo, o README para Rubeus inclui a seguinte declaração:
“O Rubeus não tem nenhum código para tocar no LSASS (e nenhum é intencional), portanto, sua funcionalidade se limita à extração de tickets Kerberos por meio do uso da API LsaCallAuthenticationPackage ()”
Portanto, qualquer lógica de detecção baseada no acesso ao lsass (por exemplo, por meio de uma rotina do kernel ObjectPreCallback para um processo específico ou operação de manipulador de thread, ou um gancho de modo de usuário (em OpenProcess/NtOpenProcess) pode perder essa atividade. Portanto, é um potencial ponto cego para, digamos, defensores que confiam nos eventos de acesso ao processo da Sysmon para alertar sobre o acesso suspeito ao manipulador de processos.
3. Pass-the-hash (PtH)
As duas últimas técnicas que este blog abordará são exemplos de um invasor alterando as credenciais em cache associadas à sua sessão atual de token/logon de acesso “ilegitimamente” ao modificar diretamente a memória lsass. No cenário de PtH, o token de acesso do invasor permanece inalterado e aponta para a mesma sessão de logon, mas as credenciais em cache associadas são sobrescritas diretamente em um hash roubado. A partir desse ponto, qualquer tentativa de autenticação remota usará o hash roubado, conforme demonstrado abaixo:

Nesse sentido, tanto o PtH quanto o OPtH podem ser considerados funcionalmente idênticos à técnica NETONLY discutida anteriormente.
O fluxo de trabalho típico de um ataque de PtH é:
- Abra um identificador de gravação para lsass (por exemplo, via OpenProcess/NtOpenProcess com o acesso desejado de PROCESS_VM_WRITE)
- Enumere a lista vinculada de logon de sessões
- Localize a sessão de logon de interesse e identifique o pacote de autenticação necessário (no caso de PtH/NTLM, esse é o pacote de autenticação MSV1_0)
- Atualize as credenciais em cache associadas.
Observe que essas técnicas geralmente dependem da análise e modificação de estruturas.não documentadas do Windows. Isso não é algo que será abordado neste blog, mas mais informações sobre como isso é feito podem ser encontradas aqui e aqui.
Portanto, depois que as credenciais em cache forem atualizadas na memória, elas serão usadas automaticamente para autenticação remota, conforme o design usual de autenticação de sign-on único do Windows, quando qualquer processo/thread executado como esse token tentar acessar um recurso remoto.
Observe que, nesse caso simples, não foram criados tokens adicionais de sessão/acesso de login. No entanto, de forma semelhante aos ataques pass-the-ticket, essas ferramentas também precisarão frequentemente criar novos processos/sessões de login inúteis do NETONLY para preservar as credenciais existentes ou aplicar as credenciais roubadas.
Como observação, para obter um identificador de gravação para lsass, o malware normalmente usa duas abordagens:
- Adquirir SeDebugPrivilege37
- Roubar e se fazer passar por um token SYSTEM
A primeira abordagem foi discutida na primeira parte desta série de blog, no entanto, a última abordagem é um exemplo típico de roubar/falsificar um token com o objetivo de contornar as verificações de acesso locais (por exemplo,roubar um token SYSTEM com um privilégio específico ativado (por exemplo, SeTcbPrivilege). Um token SYSTEM geralmente é obtido roubando o token primário do winlogon.
4. Overpass-the-hash (OPtH)
A técnica Overpass-the-hash aplica o mesmo conceito de pass-the-hash com uma diferença fundamental: ela converte um hash em um tíquete TGT completo.
Quando um usuário faz login pela primeira vez em uma estação de trabalho Windows, como parte do processo de autenticação Kerberos, o hash da senha do usuário é usado para criptografar um carimbo de data/hora a fim de validar a identidade do usuário no Controlador de Domínio/Centro de Distribuição de Chaves (KDC) e receber um TGT. O Overpass-the-hash modifica esses hashes em cache38 na memória e, em seguida, inicia o protocolo de autenticação Kerberos normal (AS-REQ/AS_REP etc.) para obter um TGT completo para um hash roubado.39
Essa técnica pode ser executada por meio do comando pth de mimikatz (que é erroneamente rotulado de pth quando na verdade está executando overpass-the-hash under the hood):
mimikatz # sekurlsa: :pth /user:Administrator /domain:ASTRO.testlab /ntlm: c0f969f35beb20e8f09ce86ef42ccd51
Isso essencialmente executa o mesmas etapas do PtH, exceto que ele tem como alvo o SSP Kerberos (e, portanto, o kerberos.dll).40

Como essa técnica envolve mais uma vez apagar o TGT atual associado à sessão de logon do usuário, um invasor pode usar um processo NETONLY (com uma sessão de logon fictícia associada) para preservar seu TGT atual, que é exatamente como o mimikatz executa o overpass-the-hash por padrão.
Primeiro, ele gera um novo processo em um estado suspenso por meio de CreateProcessWithLogonW com o sinalizador LOGON_NETCREDENTIALS_ONLY. Em seguida, ele obtém um identificador para o token primário desse processo suspenso e recupera o ID de autenticação da nova sessão fictícia de logon por meio de GetTokenInformation. Essa função é usada para consultar informações armazenadas em cache no token por meio da enumeração TOKEN_INFORMATION_CLASS, que nesse caso é TokenStatistics.
Depois de obter o ID de autenticação, o mimikatz agora pode começar a enumerar a lista vinculada de sessões de logon no lsass, procurando a sessão de logon recém-criada. Depois de encontrar a sessão de logon de destino (por meio do ID de autenticação), ele poderá prosseguir com a atualização das credenciais do Kerberos associadas a ela. Depois que as credenciais são atualizadas, o token (cuja sessão de logon correspondente agora está vinculada ao hash roubado) pode ser convertido em um token de representação via DuplicateTokenEx e representado via SetThreadToken, como vimos anteriormente.
Mais uma vez, nesse estágio, qualquer tentativa que um invasor fizer para acessar recursos na rede usará a combinação de hash de domínio\ usuário e senha fornecida como argumentos para o mimikatz para autenticação. Portanto, todas as interações remotas serão realizadas com o acesso e os privilégios das credenciais roubadas.
Conclusão
O objetivo desta série de blog em duas partes foi explicar como os conceitos fundamentais da Security do Windows funcionam nos bastidores e mostrar como os invasores abusam desses recursos para comprometer os domínios do Windows. Este blog demonstrou que, independentemente das ferramentas ou provedores de autenticação que sejam abusados, os atacantes agem sob um conjunto de restrições que resultam nos mesmos sinais anômalos para manipulação de token de acesso (por exemplo, logins anômalos apenas de rede). Essas restrições são determinadas pela relação fundamental entre tokens de acesso, sessões de login e credenciais em cache.Pronto para uma proteção holística de dados com o Elastic Security? Experimente gratuitamente hoje mesmo ou experimente nossa versão mais recente do Serviço Elasticsearch no Elastic Cloud. E aproveite nosso treinamento de Início Rápido para se preparar para o sucesso.
Referências
1. Para um visão geral de como funciona a autenticação Kerberos, veja Programando Segurança do Windows, Keith Brown ou https://posts.specterops.io/kerberosity-killed-the.... Além disso, o Rubeus, que é um kit de ferramentas para interagir com o Kerberos, tem um readme extremamente informativo, recomendado para leituras adicionais.
2. Lembre-se, o Windows autenticará automaticamente com as credenciais armazenadas em cache na sessão de login sempre que um usuário tentar acessar um recurso de rede, conforme o mecanismo de autenticação de sign-on único do Windows. Credenciais em cache aqui podem se referir a qualquer provedor de autenticação (por exemplo, hashes NTLM ou tickets Kerberos). Nota: isso pressupõe que o usuário esteja logado interativamente (fora da rede).
3. Isso geralmente serve para evitar perder um ponto de apoio devido à resposta a incidentes ou isolamento do host.
4. Isso obviamente só se aplica à atividade do invasor em um host comprometido, ao contrário de um invasor executando código de outra fonte, por exemplo, remotamente via impacket.
5. https://clymb3r.wordpress.com/2013/11/03/powershel...
6. Veja o comando 'steal_token' do Cobalt Strike como exemplo dessa técnica: https://www.cobaltstrike.com/help-beacon
7. Este comentário do framework arquivado do PowerSploit também deve fornecer mais esclarecimentos sobre essa distinção entre roubo de tokens para escalonamento local de privilégios versus movimento lateral.
8. Alternativamente, atacantes também podem optar pelo método de spray de senha ou tentar usar ataques NTLM de sniffing/reproduzir por meio de ferramentas como responder.
9. Note que tanto LogonUserA/W são simples wrappers ao redor do LogonUserExExW em SspiCli.dll
10. Da mesma forma, o CreateProcessWithLogonW pode receber uma conta de administrador local (rid-500) para executar um processo elevado a partir de um contexto médio/não elevado.
11. Existem opções remotas de registro UAC que podem modificar esse comportamento.
12. Existe um tipo adicional de logon, LOGON32_LOGON_NETWORK_CLEARTEXT, que é essencialmente um logon de rede, mas com credenciais em cache. Consulte Programação de Segurança do Windows, Keith Brown para mais informações.
13. Veja para mais informações:
https://blueteamer.blogspot.com/2018/12/disabling-...
https://support.microsoft.com/en-gb/help/951016/de...
https://labs.f-secure.com/blog/enumerating-remote-...
14. Nota: também existe uma função DuplicateToken , mas ela só retorna um token de personificação.
15. Isso pode ser verificado examinando a função no IDA. Alternativamente, confira aqui no ReactOS.
16. Este resumo é uma leve simplificação da segurança por personificação. Para uma visão mais detalhada, veja os slides "Introdução à Escalada de Privilégios Lógicos no Windows" de James Forshaw (p26): https://conference.hitb.org/hitbsecconf2017ams/mat...
17. Este título foi retirado de um excelente blog de Raphael Mudge: Tokens de Acesso ao Windows e Credenciais Alternativas.
18. Esse é tipicamente o principal motivo pelo qual a opção 2 não é comumente usada pelos atacantes.
19. Portanto, executar 'whoami' ainda mostrará o mesmo usuário (já que o token é o mesmo), apesar de o token duplicado ter credenciais de rede diferentes. Isso é uma fonte comum de confusão ao usar o comando make_token do Cobalt Strike (que executa a mesma técnica descrita abaixo).
20. As APIs do Windows RPC/COM também permitem que o usuário especifique credenciais apenas de rede. Por exemplo, isso pode ser alcançado para RPC chamando RpcBindingSetAuthInfoExW e passando uma estrutura SEC_WINNT_AUTH_IDENTITY via o parâmetro AuthIdentity. Para mais informações, veja Programando Segurança do Windows, Keith Brown e https://docs.microsoft.com/en-us/windows/win32/wmisdk/setting-authentication-using-c-.
21. Embora os dois sinalizadores tenham nomes diferentes, seu significado é o mesmo; Essas credenciais devem ser usadas apenas na rede.
22. Note que ainda existem maneiras de evitar criar logs de eventos suspeitos para sessões de login anômalas.
23. Este é um truque de James Forshaw - veja o blog a seguir para mais detalhes: https://www.tiraniddo.dev/2017/05/reading-your-way.... Além disso, o TokenViewer é uma excelente ferramenta para experimentar esse tipo de técnica.
24. Com esse token de personificação resultante, é possível escrever um arquivo para System32, etc.
25. Ainda pode haver motivos legítimos para se passar por alguém antes de chamar uma API, como obter um privilégio que você não tem atualmente antes de chamar uma API que o exija (embora note que algumas APIs ativam automaticamente privilégios).
26. Existem algumas formas de contornar isso. Por exemplo, você pode gerar um processo como filho de um processo SYSTEM obtendo um handle para um processo SYSTEM via OpenProcess com o direito de acesso PROCESS_CREATE_PROCESS . Esse HANDLE pode então ser passado para o NtCreateProcess como o parâmetro ParentProcess. Isso também pode ser alcançado via parâmetro PROC_THREAD_ATTRIBUTE_PARENT_PROCESS e CreateProcess: https://gist.github.com/xpn/a057a26ec81e736518ee50...
27. Curiosamente, o CreateProcessWithTokenW adota o argumento dwLogonFlags apesar de também exigir um handle para um token existente, que por definição já deveria ter uma sessão de login correspondente. Parece provável que isso tenha a ver com o carregamento do perfil do usuário.
28. Programando Segurança Windows, Keith Brown
29. Especificamente, SE_IMPERSONATE_NAME para CreateProcessWithTokenW e SE_INCREASE_QUOTA_NAME (&) SE_ASSIGNPRIMARYTOKEN_NAME (se o token não for atribuíble) para CreateProcessAsUsuárioW
30. Um resumo da autenticação Kerberos pode ser encontrado aqui e veja o seguinte para mais informações sobre ataques relacionados a kerberos: https://www.blackhat.com/docs/us-14/materials/us-1..., , https://github.com/GhostPack/Rubeus#readme
31. https://googleprojectzero.blogspot.com/2019/12/cal...
32. Por exemplo, a ferramenta nativa do Windows, klist , oferece funcionalidades semelhantes e é claramente um wrapper em torno do LsaCallAuthenticationPackage.
33. Note que um usuário não elevado só pode aplicar tickets à sua própria sessão de logon; privilégios elevados são necessários para aplicar um TGT a uma sessão de login diferente.
34. Há algumas ressalvas/sutilezas nessa afirmação que são melhor respondidas pelo readme Rubeus. Resumindo, porém, o chamador precisa registrar uma conexão LSA via LsaRegisterLsaProcess , que requer o privilégio SeTcbPrivilege (ou seja, o chamador faz parte da base de computação confiável).
35. Como observação, você também pode falar com o pacote de autenticação msv1_0 via LsaCallAuthenticationPackage e enviar os seguintes tipos de mensagens: https://docs.microsoft.com/en-us/windows/win32/api..., embora eu não tenha investigado se também é possível recuperar credenciais NTLM por meio dessa interface.
36. Para mais informações, veja o repositório do Rubeus no GitHub readme, que tem um excelente resumo de muitas funcionalidades relacionadas ao kerberos e considerações de opsec.
37. Veja aqui para um exemplo de como habilitar um privilégio
38. Isso pode ser verificado olhando o PsOpenProcess/Thread no IDA e procurando uma chamada para o SePrivilegeCheck.
39. Note que adquirir SeDebugPrivilege tende a ser muito ruidoso do ponto de vista da lógica de detecção.
40. Note que o hash/chave pode ser rc4_hmac (por exemplo, NTLM), aes128_hmac, aes256_hmac etc. Veja aqui para mais informações.
41. Veja para mais detalhes: https://www.blackhat.com/docs/us-14/materials/us-1...
42. Como observação, a funcionalidade asktgt de Rubeus realiza uma variante de overpass-the-hash ao construir tráfego AS-REQ bruto para um determinado hash a partir de um contexto não elevado e sem precisar tocar no LSASS.