Todos os dias, no JSOC CERT, encontramos eventos de diferentes caixas de proteção que funcionam como parte das soluções AntiAPT de nossos clientes e permitem que milhares de arquivos do tráfego da Web e de email passem por eles. Vale a pena notar que os modernos sistemas Sandbox em seu desenvolvimento foram muito mais além do que simplesmente interceptar chamadas do sistema no Modo Kernel e funções da API no Modo Usuário. Cada vez mais, eles usam seu próprio hypervisor, um sistema para emular a atividade do usuário, instrumentação dinâmica, hash e clustering em seções de código, análise de cobertura de código etc. Essa variedade de tecnologias cria a ilusão de que, se um arquivo não funciona na caixa de areia e não mostra sua “cara verdadeira”, então provavelmente é o APT ou uma tecnologia inovadora para detectar um ambiente virtual, do qual a comunidade IB ainda não está ciente. Mas ...

Como não conhecemos os recursos internos do trabalho de caixas de areia comerciais, em alguns casos, verificamos duas vezes - analisamos manualmente as amostras que passaram no teste. Recentemente, descobrimos várias vezes que algumas caixas de proteção comerciais (por razões objetivas, não podemos dizer quais) não detectaram determinados arquivos maliciosos durante a análise dinâmica e, se o analisador estático também estava silencioso, o arquivo foi ignorado.
A verificação da sandbox conseguiu ignorar famílias de malware conhecidas como Pony, Loki e Hawkeye. Apenas uma coisa os uniu - eles foram cobertos por um empacotador escrito em Visual Basic.
Dado que essas famílias HPE há muito não são novidade, o veredicto "positivo" da sandbox é muito deprimente. Portanto, decidimos descrever o princípio geral de operação desse empacotador e as observações feitas por nós ao longo de algum tempo.
O esquema geral do trabalho do empacotador é condicionalmente dividido em 4 etapas e é mostrado no diagrama abaixo.

O ponto de entrada de um arquivo malicioso parece típico dos aplicativos Visual Basic:

Nós encontramos várias opções para esse empacotador, e o código do VB Wrapper mudou com frequência, mas a tarefa executada permaneceu a mesma: transferir o controle para o código do Estágio 1. Em amostras anteriores, o controle foi transferido usando as funções de API da classe Enum * (por exemplo, EnumWindows, EnumCalendarInfo, etc.). e) para o qual o endereço Estágio 1 do código foi indicado como parâmetro. Recentemente, observamos que o controle é transferido diretamente.
Etapa 1
A gerência recebe o código Estágio 1. Este código não é criptografado, mas é ofuscado. Os métodos de ofuscação variam de amostra para amostra, mas o algoritmo de operação geral não muda:
- Um ciclo com muitas instruções (incluindo lixo) que gera a chave necessária para decodificar o código do Estágio 2. A peculiaridade desse trecho de código é que não há funções de suspensão, mas devido ao grande número de iterações, sua execução leva em média 1-2 minutos.
- Descriptografia (XOR regular) e transferência de controle para o código do Estágio 2.
A captura de tela abaixo mostra exemplos de métodos de ofuscação usados:

2 etapa
A principal tarefa do código no Estágio 2 é verificar o ambiente e implementar métodos anti-depuração. Algumas seções do código são criptografadas (descriptografadas antes da execução e, depois disso, criptografadas novamente com o mesmo algoritmo XOR) para dificultar a detecção por assinaturas. Após a descriptografia, os recursos característicos são visíveis, segundo os quais o código do Estágio 2 pode ser reconhecido por análise manual.

A lista de verificações é bastante grande e difere em diferentes versões do empacotador, por isso, forneceremos alguns métodos encontrados em todas as versões com capturas de tela e, no final, listaremos a lista inteira na tabela.
1) GetTickCount + Sleep
O registro de data e hora atual é obtido, o modo de suspensão é chamado por 2 segundos, após o qual outro registro de data e hora é obtido imediatamente.
Depois disso, a diferença entre as marcas é verificada (se 2 segundos realmente passaram).

2) SetErrorMode
Verifica a operação correta da chamada da API SetErrorMode. A função é chamada duas vezes seguidas com os parâmetros 0x800 e 0x0, após o qual o resultado da segunda chamada é verificado: deve ser igual a 0x800.

3) SetLastError
Primeiro, é chamado um SetLastError com um parâmetro 0x5, após o qual é verificado se o valor do último código de erro no TEB está definido corretamente (ou seja, é 0x5).

4) Verificando o movimento do cursor
O código entra em um loop sem fim, esperando o mouse se mover.

5) DbgBreakPoint e DbgUiRemoteBreakin
Essas funções são modificadas para impedir que o depurador se conecte ao processo.

Técnica
| Comentário
|
GetTickCount + Sleep
| Verificando timestamps
|
SetErrorMode
| Verificando se a função está funcionando corretamente
|
SetLastError
| Verificando se a função está funcionando corretamente
|
GetCursorPos
| Verifique o movimento do cursor
|
Dbgbreakpoint
| Modificação de função para impedir a conexão do depurador
|
DbgUiRemoteBreakin
| Modificação de função para impedir a conexão do depurador
|
Exclusão de gancho
| Os primeiros 5 bytes de funções são restaurados no ntdll.dll, caso haja ganchos
|
NtSetInformationThread
| Parâmetro 0x11 (ThreadHideFromDebugger)
|
GetThreadContext + verifique DR
| Registros de depuração DR0-DR3, DR6, DR7 são verificados.
|
Verificar pontos de interrupção
| As instruções INT3 (0xCC), int 3 (0xCD 0x03) e ud2 (0x0F 0x0B) no início de algumas funções são verificadas
|
cpuid (EAX = 0x0)
| Os registros EAX, ECX, EDX são verificados
|
cpuid (EAX = 0x40000000)
| Os registros EAX, ECX, EDX são verificados
|
cpuid (EAX = 0x1)
| 31º bit ECX verificado
|
PEB (sendo depurado)
| Verifica o valor 0x1
|
PEB (NtGlobalFlag)
| Valor verificado 0x70
|
NtQueryInformationProcess
| Chamado com sinalizadores ProcessDebugPort (0x7), ProcessDebugFlags (0x1F), ProcessDebugObjectHandle (0x1E)
|
Verificação do nome do processo
| As strings "amostra", "caixa de areia", "vírus", "malware", "auto". São verificadas
|
Se todas as técnicas do estágio 2 forem concluídas, a linha de comandos será verificada quanto à conformidade com o formato especial. Se a verificação falhar, as seguintes ações serão executadas:
1) A função CreateProcess é chamada com o sinalizador CREATE_SUSPENDED para reiniciar o processo atual. Nesse caso, a linha de comando tem o formato necessário.
2) Usando as funções GetContextThread e SetContextThread, o ponto de entrada é alterado para um novo, localizado no código do Estágio 1.
3) Repita as etapas 1 e 2 (incluindo um ciclo longo e todas as verificações). Dessa vez, a verificação da linha de comando é bem-sucedida e o processo prossegue para a próxima etapa.
3 etapa
Nesse estágio, o corpo do vírus principal é descriptografado e a técnica Process Hollowing é executada no processo atual, após o qual o controle é transferido para o ponto de entrada do vírus principal.
Lição aprendida
Não podemos dizer exatamente o que causa essa ou aquela sandbox nesse caso, mas quero acreditar que a possibilidade de usar as técnicas descritas no artigo por malware foi fornecida pelos fornecedores por um longo tempo, e o problema está apenas no longo atraso na primeira etapa do trabalho do empacotador .
Apesar de as caixas de areia modernas serem posicionadas principalmente como parte dos sistemas de proteção contra ataques de APT, nossas observações sugerem que mesmo famílias maliciosas conhecidas pela comunidade penetram na infraestrutura com constância invejável. Como não há garantias de que a amostra que contornou o sandbox não terá algumas técnicas de desvio de antivírus em seu arsenal, você não pode confiar nesse monte de soluções protetoras. Nesses casos, um processo de monitoramento adequadamente construído, incluindo eventos de segurança da informação dos hosts finais, pode garantir uma resposta oportuna e minimizar possíveis danos.