Plataformas afetadas: Windows
Usuários afetados: Usuários do Windows
Impacto: Coleta informações confidenciais e executa módulos maliciosos adicionais
Nível de gravidade: Crítico
Um relatório de ameaças publicado por especialistas em outubro de 2021 chamou nossa atenção recentemente. Ele discute um agente de ameaças desconhecido conduzindo uma campanha de espionagem no Sudeste Asiático usando um novo arsenal de malware personalizado. O que mais despertou nossa curiosidade foi a menção de uma carga útil DLL carregada do registro que ainda não havia sido descoberta.
A razão pela qual o módulo era difícil de encontrar ficou aparente depois de analisar seu carregador. O módulo é armazenado como um blob compactado com um cabeçalho personalizado no registro. Ele nunca é gravado em disco, tornando improvável que apareça em conjuntos de dados como o VirusTotal.
E assim, embarcamos em uma jornada para caçar o módulo perdido. Descobrimos agora uma amostra do módulo e uma infinidade de componentes e variantes que datam de 2017. A engenharia reversa das amostras nos permitiu observar a progressão do desenvolvimento desse malware ao longo dos anos. Com o tempo, o código personalizado foi adicionado, os componentes foram atualizados, os recursos foram expandidos, o código ficou mais organizado e a modularidade aumentou.
Este blog examinará os diferentes componentes desse malware e sua progressão ao longo do tempo, mapeando assim a evolução da estrutura do malware Soul.
Teoria da evolução
Na fase inicial, os invasores usaram um backdoor que incorporava código das ferramentas de código aberto Gh0st RAT e NetBot Attacker, embora com modificações consideráveis. O backdoor é incorporado como um blob compactado em seu executável dropper, que o grava no disco e o executa.
Dentro de um ano, o código do backdoor foi refatorado e teve código personalizado adicionado a ele, completando sua transformação no que chamamos de módulo Soul. Seu carregador, que apelidamos de SoulSearcher, também mudou. Em vez de descartar a carga no disco, o módulo compactado é armazenado no registro e carregado na memória.
Desde o início de 2020, detectamos variantes do SoulSearcher cada vez mais complexas, algumas das quais suportam o carregamento de vários módulos do registro. Eles se transformaram significativamente ao longo do tempo e seus artefatos de configuração lançam luz sobre os possíveis recursos do módulo Soul.
Além dos backdoors, ferramentas adicionais foram usadas, como keyloggers e uma ferramenta 7zr compilada de forma personalizada (7-zip autônomo reduzido).
Uma linha do tempo completa dos vários componentes é descrita abaixo, começando em 2017 com o primeiro keylogger e backdoor e terminando com as recentes variantes do SoulSearcher encontradas em novembro de 2021.
Nota: Esta linha do tempo é baseada em carimbos de data e hora de compilação e, embora possam ser adulterados, no caso desta estrutura de malware, consideramos que eles são autênticos. Isso ocorre em parte porque a distribuição de tempo das amostras coletadas se correlaciona com nossa compreensão das capacidades dos componentes e seus avistamentos na natureza. Além disso, amostras relacionadas, como carregadores e suas cargas úteis, foram compiladas em segundos umas das outras.
Várias características são comuns em todos os componentes que encontramos:
- Ofuscação de importação do DynamiCall do código-fonte vazado do notório Hacking Team’s
- Backdoor RCS
- Empilhar strings
- Semelhança de estrutura de dados, como a estrutura de configuração
- Algoritmos de criptografia e compressão
- Nomes de mutexes, eventos e mapeamentos de arquivos
- Timestamps de compilação adjacentes
Funcionamento do Backdoor
Este é o backdoor usado na fase inicial pelo agente da ameaça. Ele foi compilado em outubro de 2017 e usou código revisado de repositórios públicos e outros malwares vazados online, como:
- DynamiCall
- Gh0st RATO
- Funções de manipulação de arquivos
- Código de shell CMD
- Mensagens e estruturas de comunicação
- HTran (uma ferramenta de segurança de conexão de código aberto)
- 7zip
O backdoor é uma DLL colocada no disco por um simples conta-gotas. O dropper LZMA descompacta o backdoor e uma configuração que ambos compartilham. O dropper grava o backdoor em um caminho especificado pela configuração e anexa a configuração a ele como uma sobreposição. Dependendo do argumento de linha de comando passado para o dropper, o backdoor é executado com LoadLibrary ou rundll32.exe. Finalmente, o conta-gotas se exclui do disco.
Configuração
O backdoor lê sua configuração de sua sobreposição de arquivo e a descriptografa subtraindo e fazendo XOR de cada byte com 0x13.
A configuração começa com uma sequência de bytes cujo significado é desconhecido, mas é idêntico em todas as amostras que encontramos. Outros campos são o nome do arquivo backdoor ou caminho completo, o endereço C2 e a porta em little-endian. A configuração também contém um nome de serviço e uma descrição, ambos não utilizados. Em uma amostra, a string “NetBot” é definida como o nome do arquivo.
Dois outros campos, uma matriz de DWORDs e um sinalizador (destacado no deslocamento 0x1f0), controlam se e quando o backdoor deve suprimir a comunicação relacionada ao comando.
Os valores do array determinam os dias e horas para aceitar comandos. Nesta amostra, todos os valores são 2. Cada índice representa um determinado dia da semana e a hora do dia. Se o valor em um determinado índice for 0, as solicitações de comandos serão retidas no dia e hora correspondentes.
O sinalizador determina se o recebimento de comandos deve ser suprimido enquanto houver atividade na máquina, de acordo com as seguintes condições:
- Além da sessão atual, há uma sessão de console ativa no sistema
- Além da sessão atual, há uma sessão RDP ativa ou conectada no sistema
As sessões são monitoradas usando as APIs WTSRegisterSessionNotification e WTSEnumerateSessions.
Dependendo da configuração, o backdoor pode receber comandos em modo ativo (como cliente) ou passivo (como servidor). Existem dois números de porta, um para cada modo.
- Se a porta do servidor não for 0, o backdoor entrará em contato com o servidor para receber comandos.
- Se a porta de escuta não for 0, o backdoor escuta nessa porta e aguarda os comandos das conexões de entrada (apenas uma conexão pode estar ativa por vez).
Comunicação
As mensagens para o servidor C2, incluindo solicitações de comandos, têm uma estrutura fixa. Cada solicitação ao servidor é composta de cabeçalhos HTTP codificados que representam o tráfego de rede legítimo para taboola[.]com.
A estrutura do corpo HTTP enviado ao servidor é mostrada abaixo. CompressedBuffer são dados deflacionados por zlib.
O recebimento de comandos começa quando o backdoor envia informações sobre a máquina para o servidor com um MessageType de 0x11000000:
- Hostname
- IP address(es)
- CPU architecture
- RAM size
A estrutura de resposta do servidor é semelhante à da solicitação:
Em um segmento separado, o backdoor pode sinalizar o servidor se a supressão de recebimento de comando estiver em vigor no momento com MessageType 0x1100000B.
Comandos
Quando o servidor envia um comando, ele é um dos valores de CommandType na tabela abaixo e o campo CompressedBuffer está vazio. O backdoor envia uma solicitação adicional para os parâmetros do comando, com um valor MessageType especificado de acordo com o comando específico.
O mesmo acontece quando o backdoor funciona no modo passivo, exceto que é limitado a manipulação de arquivos, CMD e comandos de fechamento de soquete.
Os carregadores do SoulSearcher
O SoulSearcher é um tipo de carregador de segundo estágio visto na natureza desde novembro de 2018. Todas as amostras que encontramos são DLLs com um fluxo de operação semelhante. Eles são responsáveis por executar a carga útil do módulo Soul e analisar sua configuração.
As principais diferenças entre as variantes do SoulSearcher são o tipo de configuração passado para a carga e o local onde a configuração e a carga são armazenadas.
SoulSearcher Binário
Estas são as primeiras amostras de SoulSearcher em nossa posse. Uma dessas amostras tem sua carga útil – um módulo Soul – embutido nela. Cada amostra exporta duas funções: DumpAnalyze e DumpAnalyzeEx.
Primeiro, o SoulSearcher procura o módulo e a configuração, seja em seus dados de sobreposição ou arquivos no disco. Se forem encontrados, ele salva o módulo no registro. Independentemente disso, o SoulSearcher busca a carga útil do registro, carrega-a reflexivamente e passa a configuração para ela como um argumento.
A configuração está localizada no final da sobreposição e é descriptografada usando SUB-XOR 0x13. Ele tem o mesmo formato do backdoor original do Soul, com um campo adicional que determina o tamanho do módulo Soul compactado na sobreposição. Outra parte da configuração é recuperada do valor de registro HKCU\Software\OIfkO2i1 e descriptografada com SUB-XOR 0x79. Se não existir, esse caminho também será consultado nas seções de registro de outros usuários.
Se o argumento “-h ” foi passado para a exportação do SoulSearcher, a configuração e a carga útil são extraídas de sdc-integrity.dat em vez da sobreposição. Eles são extraídos exatamente da mesma maneira que antes. O argumento fornecido é um identificador para uma DLL usada para recuperar o caminho do diretório no qual reside o arquivo .dat.
De qualquer forma, o módulo é salvo no registro em HKCU\Software\kuhO6Ba0kT.
XML SoulSearcher
Todo XML SoulSearcher começa com a obtenção da configuração anteriormente descartada por um componente desconhecido. A maioria dos exemplos o recupera do registro e alguns têm a opção de recuperá-lo de um objeto de mapeamento de arquivo ou de um arquivo no disco.
Por exemplo, uma amostra recupera a configuração de um dos seguintes, dependendo de estar sendo executado como um serviço:
- Um nome de valor no formato de um GUID nos parâmetros de serviço em HKLM\SYSTEM\CurrentControlSet\Services\\Parameters
- Um objeto de mapeamento de arquivo chamado Global\CacheDataMapping
Os dados binários recuperados têm a seguinte estrutura:
A estrutura é processada da seguinte maneira para recuperar a configuração XML:
- Verifique se o tamanho de CompressedConfig é igual a CompressedConfigSize
- Verifique se CompressedConfigSize e ConfigSize não são 0
- Verifique se ambas as somas de verificação MD5 não são 0
- Certifique-se de que Magic contém a sequência de bytes 86 AE 00 00
- Execute a validação da soma de verificação MD5 da configuração compactada
- LZMA-descompacte a configuração
- Execute a validação da soma de verificação MD5 da configuração descompactada
Em uma variante, uma etapa extra é executada no início para descriptografar os dados do registro usando o AES-256 CBC. A chave é recuperada de um dos dois caminhos codificados.
As amostras mais antigas desserializam a string resultante com a API CreateXmlReader, enquanto as amostras mais recentes usam a biblioteca de código aberto TinyXML. Os nomes de atributos XML esclarecem os módulos Soul carregados do registro.
Semicolon SoulSearcher
A partir de agosto de 2021, as variantes do SoulSearcher começaram a usar uma configuração separada por ponto e vírgula codificada em vez de uma XML do registro. A primeira variante desse tipo foi compilada pouco mais de um mês antes do lançamento do relatório da Symantec.
Essa configuração não possui os nomes de atributos XML indicativos, como aqueles nas configurações XML, resultando em uma ferramenta mais obscura. No entanto, aqui está o que podemos dizer sobre alguns desses campos:
- Acreditamos que o primeiro campo, do5Kc1diLHgq5f6, representa o tipo de configuração. Nas configurações XML, o tipo é representado pela string X6bmLMbAL29AlxB.
- Um dos valores indica se o SoulSearcher foi instalado como um serviço. Nesse caso, a configuração inclui campos para detalhes sobre o serviço, como seu nome.
- Alguns dos valores determinam quais módulos Soul devem ser carregados.
- Um campo pode conter um caminho de registro do qual carregar um módulo Soul (enquanto outros módulos são carregados de caminhos codificados).
Quando o Soul é encontrado
Variantes mais antigas do SoulSearcher carregam um único módulo Soul, enquanto alguns XML e Semicolon SoulSearchers mais recentes podem carregar até quatro, dependendo de sua configuração.
Cada módulo é buscado no registro de maneira semelhante à configuração:
- Verifique se o tamanho de CompressedModule é igual a CompressedModuleSize
- LZMA-descompacte o módulo
- Execute a validação da soma de verificação MD5 do módulo descompactado
- Certifique-se de que a arquitetura do módulo corresponda à arquitetura do SoulSearcher
Este procedimento é idêntico em todas as amostras do SoulSearcher, exceto nos SoulSearchers binários, cuja estrutura difere ligeiramente.
O SoulSearcher carrega reflexivamente o módulo na memória e chama sua exportação Construct. Algumas variantes anteriores também chamam exportações adicionais do módulo.
Soul Backdoor
Descobrimos que uma amostra do Binary SoulSearcher de novembro de 2018 tinha uma carga útil incorporada.
Este módulo Soul se assemelha ao backdoor original em termos de funcionalidade, embora seu código seja muito mais organizado. Um exame minucioso revelou que o código do backdoor original foi reorganizado como várias exportações. Por exemplo, o código responsável por enviar e receber mensagens HTTP foi dividido nas exportações SendMsg e RecvMsg.
Configuração
O SoulSearcher chama a exportação BeginConnect do módulo com a configuração como um argumento. A configuração tem o mesmo formato binário que a configuração do backdoor original, mas sem os campos relacionados ao serviço.
Comunicação
Ao contrário do backdoor original, este módulo Soul só recebe comandos como cliente.
Se a resolução do endereço do servidor por meio da API gethostbyname falhar, o backdoor também tentará consultar dois servidores DNS codificados usando um recurso não documentado da API DnsQuery:
- 193.0.14.129 (servidor raiz DNS)
- 8.8.8.8 (DNS público do Google)
Os cabeçalhos constantes da solicitação foram alterados para representar o tráfego para s-microsoft[.]com, e a exportação GetSubInfo coleta as informações da máquina.
Comandos
As estruturas de mensagem são as mesmas do backdoor original. Conforme visto na tabela abaixo, vários novos códigos de comando não estavam presentes no backdoor original. Quando um dos cinco comandos nomeados é recebido, o backdoor baixa e executa uma DLL do servidor. Os nomes de comando são divulgados no binário e são passados para as DLLs de comando como parte de seus argumentos. Como as próprias DLLs são desconhecidas para nós, só podemos especular sobre sua funcionalidade com base em seus nomes e nas implementações dos mesmos tipos de comando no código backdoor original.
Uma conexão de soquete adicional é criada para baixar uma DLL de comando do servidor. Primeiro, o backdoor envia uma mensagem do tipo 0x1100000C com um buffer que contém o valor constante 0x4096C083. Como todas as solicitações, ela é enviada via SendMsg na estrutura BackdoorRequest mencionada anteriormente. Em seguida, ele envia outra mensagem do mesmo tipo, mas desta vez o buffer está estruturado conforme mostrado abaixo. O campo Arquitetura contém um valor de 32 ou 64 dependendo da arquitetura do backdoor.
O servidor responde ao backdoor com a seguinte estrutura:
O backdoor usa a estrutura para carregar a DLL de comando da seguinte maneira:
- Valide a soma de verificação MD5 do módulo compactado
- LZMA-descompacte o módulo compactado
- Validar a soma de verificação MD5 do módulo descompactado
- Se as etapas 2 ou 3 falharem, emita novamente a solicitação para o servidor
- Carregar reflexivamente o módulo na memória
- Chame a exportação Construct do módulo com argumentos que incluem, entre outras coisas:
- O valor constante 0x4096C083 (mesmo valor enviado ao servidor anteriormente)
- O nome do comando (como “Arquivo” ou “UsbNtf”)
- A configuração de backdoor
- A estrutura CommandResponse recebida do servidor
Mais Souls do que uma
Conforme mencionado anteriormente, cada XML SoulSearcher analisa uma configuração formatada em XML que contém atributos com nomes informativos. Com base nesses artefatos, conseguimos classificar potenciais cargas úteis de várias amostras em nossa posse.
Backdoor
Essas amostras do SoulSearcher estão intimamente ligadas às suas cargas úteis, na medida em que são orquestradores intrincados em vez de carregadores simples. Além de analisar uma configuração, eles invocam várias funções exportadas do módulo Soul para criar uma lógica de backdoor completa. Os campos de configuração e os nomes das funções importadas indicam a capacidade de shell remoto e a utilização do Dropbox.
Configuration Fields
- Ip
- Dns
- CntPort
- LstPort
- Blog
- DropboxBlog
- SvcName
- SvcDisp
- SvcDesc
- SvcDll
- OlPass
- OlTime
- SelfDestroy
Exports Names
- Construct
- ConnectHost1
- ForceCloseSocket
- CopyReserveMem
- Recv
- RecvEx
- Send
- SendEx
- BindShell
- Accept
- TransmitData_htran
- KillChildenProcessTree
- ExtractIPToConnect
- ExtractIPToConnect1
- GetDeviceInfoString1
- GetPseudoSocketInfo
- Decrypt_ByteToByte
Figura 17: Campos de configuração e nomes de funções importados vistos em versões mais antigas do SoulSearcher
Advanced RAT
Um SoulSearcher analisa vários campos de configuração diferentes do backdoor:
Se o atributo EnableDropbox for definido como true, o SoulSearcher carregará um módulo do caminho especificado por ServiceRegValueName_MemMod3. Se o EnableKeylog estiver definido, um módulo é carregado a partir do caminho especificado por ServiceRegValueName_MemMod1.
Proxy
A configuração desses exemplos indica recursos de proxy em HTTP e HTTPS, bem como a capacidade de executar comandos CMD.
Componentes Adicionais
Como mencionado anteriormente, o SoulSearcher é um componente de segundo estágio. Também identificamos um carregador de primeiro estágio da variante Binary SoulSearcher.
Este carregador é uma DLL com uma única função exportada, denominada SntpService, e depende de uma DLL de utilitário denominada SntpService.dll, que se espera que já resida no disco. Esses nomes provavelmente são usados para se assemelhar a um produto de software de segurança legítimo da Sophos com o mesmo nome (como visto aqui).
O carregador verifica se seu nome de processo é MSDTC.exe ou svchost.exe antes de executar SntpService em um novo thread. Neste último caso, um mutex chamado DBWinMutex_1 é criado (também usado no módulo Soul).
O carregador executa duas operações. Primeiro, ele descriptografa dois arquivos .dat de seu diretório e salva a saída no registro:
- sdc-integrity.dat é gravado em HKCR.rat\PersistentHandler\TypeFace
- scs-integrity.dat é gravado em HKCR.rat\PersistentHandler\MagicNumber
O esquema de descriptografia é AES-256 CBC com o hash SHA256 de um valor codificado permanentemente usado como chave. Ambos os arquivos são excluídos do disco, o que implica que esse procedimento ocorre apenas na infecção inicial ou quando as atualizações são implantadas.
Em segundo lugar, os dados do valor TypeFace são usados para carregar o SoulSearcher. Consiste em uma estrutura que contém um buffer e seu tamanho. O carregador pula os últimos 0x3d0 bytes do buffer, pois esses são sua configuração, e passa o restante do buffer para a função Decrypt_ByteToByte do SntpService.dll. A saída é um PE, que o carregador carrega reflexivamente e, em seguida, invoca sua exportação DumpAnalyze. O carregador passa um identificador de si mesmo para o SoulSearcher como argumento, tanto como ponteiro quanto em formato de string: “-h ”.
Exportações adicionais de SntpService.dll também são resolvidas:
Encontramos uma variante do utilitário DLL carregado no VirusTotal com o nome AvpCon.dll do Kaspersky Antivirus. Semelhante ao caso Sophos citado anteriormente, é provável que isso pareça legítimo. Apesar de suas exportações serem chamadas de “Criptografar” e “Descriptografar”, todas as funções realmente realizam compactação ou descompactação LZMA. Isso se correlaciona com uma amostra do Binary SoulSearcher que encontramos compactada, não criptografada.
Keyloggers
Os keyloggers foram compilados entre meados de 2017 e o final de 2020. Todos compartilham códigos muito semelhantes, com poucas alterações entre eles. Além dos keyloggers relatados pela Symantec, encontramos outra amostra de setembro de 2020. Embora sua função de keylogging seja idêntica às outras amostras, o restante do código tem diferenças significativas.
Os keyloggers lêem sua configuração de um arquivo com o mesmo nome, mas com a extensão .dll cortada. Nosso exemplo, no entanto, usa uma configuração do registro, e o arquivo age como um kill switch: se existir, o keylogger é encerrado. Esta amostra também tem seqüências de pilha e ofuscações DynamiCall não presentes nas amostras anteriores.
O keylogger garante que está sendo executado no Explorer.exe e recupera sua configuração lendo seus próprios últimos 0x208 bytes e descriptografando-os. A descriptografia é feita adicionando e XORing cada byte com valores constantes. Em seguida, a configuração criptografada é definida no registro em HKCU\Software\F32xhfHX. Em execuções futuras, a configuração será buscada a partir desta chave. A configuração contém dois caminhos:
- Keylogger module file – C:\Windows\SndVolSSO.DLL
- Keylogging output file – C:\users\minh\AppData\Local\OneDrive\Cache.dat
Curiosamente, o caminho do arquivo de saída inclui um nome de usuário, sugerindo que este exemplo pode ter sido destinado a uma máquina de destino específica.
O keylogger monitora os pressionamentos de tecla usando GetRawInputData e dados da área de transferência e os registra em um arquivo de saída como texto simples. O arquivo de saída tem um timestamp para tornar seu timestamp idêntico ao svchost.exe na máquina infectada. Erros retornados de GetRawInputData são registrados em C:\ProgramData\Users.inf. O keylogger também registra códigos de chave virtual IME, que suportam alguns idiomas asiáticos.
Serviço de execução de linha de comando
Esta é uma DLL de serviço leve que executa um comando CMD da chave do Registro HKCR.c\Type\Type00. Ele executa o comando às 20:00 e se nenhum processo chamado powershell.exe estiver ativo no sistema. É compilado com ofuscação DynamiCall.
7zr.exe
Este executável 7zr compilado de forma personalizada é modificado para incluir a ofuscação do DynamiCall.
Conclusão
A estrutura do malware Soul está em uso ativo desde 2017, e os agentes de ameaças têm evoluído constantemente suas ferramentas e recursos até hoje. Deve-se enfatizar que, apesar da dependência das ferramentas anteriores em código aberto, keyloggers personalizados já estavam em uso na época, e um desenvolvimento significativo de código personalizado ocorreu desde então. Suas cargas úteis modulares, multi-estágios e executadas de forma reflexiva demonstram um ofício adversário competente e são sinais de um grupo com bons recursos. Embora a identidade dos invasores seja atualmente desconhecida, acreditamos que eles são possivelmente patrocinados pelo Estado.
Os detalhes compartilhados neste relatório derivam da análise abrangente de várias amostras. No entanto, temos a sensação de que esta é apenas a ponta do iceberg, com mais cargas e recursos no arsenal do grupo para expor no futuro.
Soluções Fortinet
O FortiEDR detecta e bloqueia essas ameaças imediatamente, sem nenhum conhecimento prévio ou configuração especial. Ele faz isso usando seu mecanismo de prevenção pós-execução para identificar atividades maliciosas:
Todos os IOCs de rede foram adicionados à lista de bloqueio do FortiGuard WebFiltering.
O mecanismo de serviço FortiGuard AntiVirus está incluído nas soluções FortiGate, FortiMail, FortiClient e FortiEDR da Fortinet. O FortiGuard AntiVirus tem cobertura da seguinte forma:
W64/SoulSearcher.B7D1!tr
W32/SoulSearcher.B7D1!tr
W64/SoulSearcherKeyLogger.B7D1!tr.spy
W32/SoulSearcher.B7D1!tr
Data/SoulSearcher.B7D1!tr
Além disso, como parte de nossa associação à Cyber Threat Alliance, os detalhes dessa ameaça foram compartilhados em tempo real com outros membros da Aliança para ajudar a criar melhores proteções para os clientes.
FONTE:
https://www.fortinet.com/blog/threat-research/unraveling-the-evolution-of-the-soul-searcher-malware