Resumo
Os criminosos cibernéticos estão cada vez mais trazendo seus próprios drivers — explorando um driver legítimo vulnerável ou usando um driver personalizado para desabilitar sistemas de detecção e resposta de endpoint (EDR) e escapar de recursos de detecção ou prevenção.
O Elastic Security Labs monitorou uma campanha com motivação financeira que implantou o ransomware MEDUSA por meio do uso de um carregador compactado com HEARTCRYPT . Este carregador foi implantado junto com um driver assinado por certificado revogado de um fornecedor chinês que chamamos de ABYSSWORKER, que ele instala na máquina da vítima e então usa para atingir e silenciar diferentes fornecedores de EDR. Esse driver EDR-killer foi relatado recentemente pela ConnectWise em outra campanha, usando um certificado e códigos de controle de E/S diferentes, ocasião em que alguns de seus recursos foram discutidos. Em 2022, o Google Cloud Mandiant divulgou um driver malicioso chamado POORTRY , que acreditamos ser a primeira menção desse driver.
Neste artigo, examinaremos detalhadamente esse driver, examinando seus vários recursos e técnicas. Também fornecemos endereços virtuais relativos (RVA) em cada captura de tela de código reverso para vincular a pesquisa à amostra de referência, juntamente com um pequeno exemplo de cliente que você pode usar para experimentar mais profundamente esse malware.
Análise Técnica
Cabeçalho PE
O binário é um driver do Windows PE de 64 bits chamado smuol.sys
e imita um driver legítimo do CrowdStrike Falcon.
No momento da análise, encontramos uma dúzia de amostras no VirusTotal, datadas de 08/08/2024 a 24/02/2025. A maioria foi compactada com VMProtect, mas dois — referenciados nas tabelas observáveis abaixo — não estavam protegidos.
Todas as amostras são assinadas usando certificados provavelmente roubados e revogados de empresas chinesas. Esses certificados são amplamente conhecidos e compartilhados entre diferentes amostras e campanhas de malware, mas não são específicos para esse driver. As impressões digitais do certificado estão listadas abaixo:
fingerprint | Nome |
---|---|
51 68 1b 3c 9e 66 5d d0 b2 9e 25 71 46 d5 39 dc | Materiais de isolamento Co. de Foshan Gaoming Kedeyu, Ltd |
7f 67 15 0f bb 0d 25 4e 47 42 84 c7 f7 81 9c 4f | FEI XIAO |
72 88 1f 10 cd 24 8a 33 e6 12 43 a9 e1 50 ec 1d | Comércio Co. de Fuzhou Dingxin, Ltd. |
75 e8 e7 b9 04 3b 13 df 60 e7 64 99 66 30 21 c1 | Tecnologia da Informação Changsha Hengxiang Co., Ltd. |
03 93 47 e6 1d ec 6f 63 98 d4 d4 6b f7 32 65 6c | Tecnologia de rede Yishilian Xinjiang Co., Ltd |
4e fa 7e 7b ba 65 ec 1a b7 74 f2 b3 13 57 d5 99 | Tecnologia Shenzhen Yundian Co., Ltda. |
Ofuscação
ABYSSWORKER usa funções que sempre retornam o mesmo valor, contando com uma combinação de predicados opacos e outras funções de derivação. Por exemplo, a função de retorno zero abaixo sempre retorna um 0
com base em valores derivados codificados.
Abaixo está uma das funções de derivação:
Essas funções de retorno constante são chamadas repetidamente em todo o binário para dificultar a análise estática. No entanto, existem apenas três dessas funções, e elas não são usadas em nenhum predicado, mas são simplesmente chamadas. Podemos identificá-los facilmente, o que torna esse um esquema de ofuscação ineficiente.
Inicialização
Após a inicialização, o driver começa obtendo ponteiros para vários módulos do kernel e seu recurso de proteção ao cliente, que serão discutidos nas seções a seguir.
Em seguida, ele cria um dispositivo com o caminho \\device\\czx9umpTReqbOOKF
e um link simbólico com o caminho \\??\\fqg0Et4KlNt4s1JT
.
Ele conclui a inicialização registrando retornos de chamada para suas principais funções.
Proteção do cliente na abertura do dispositivo
Quando o dispositivo do driver é aberto, o retorno de chamada principal IRP_MJ_CREATE
é chamado. Esta função é responsável por adicionar o ID do processo à lista de processos a serem protegidos e por remover quaisquer identificadores pré-existentes para o processo de destino da lista de processos em execução.
A função recupera o ID do processo do thread do kernel atual, já que o retorno de chamada do kernel é executado no contexto do processo do cliente quando o dispositivo é aberto.
Antes de adicionar o ID do processo à lista de proteção, o ABYSSWORKER procura e remove todos os identificadores existentes para o processo cliente em outros processos em execução.
Para conseguir isso, o malware itera sobre os processos existentes forçando brutamente seus IDs de processo (PIDs) para evitar a dependência de qualquer API. Para cada processo, ele itera sobre seus identificadores, também usando força bruta, e verifica se o objeto subjacente corresponde ao processo do cliente. Se uma correspondência for encontrada, ele retira os direitos de acesso usando o valor passado como parâmetro (0x8bb
).
Por fim, ele adiciona o PID à lista global de processos protegidos.
Conforme mencionado anteriormente, o driver configura seu recurso de proteção durante a fase de inicialização. Essa proteção depende do registro de dois retornos de chamada pre-operation
usando a API ObRegisterCallback
: um para detectar a abertura de identificadores para seus processos protegidos e outro para detectar a abertura de identificadores para os threads desses processos protegidos.
Os dois retornos de chamada operam da mesma maneira: eles definem o acesso desejado para o identificador como zero, negando efetivamente a criação do identificador.
Manipuladores DeviceIoControl
Ao receber uma solicitação de controle de E/S do dispositivo, o ABYSSWORKER despacha a solicitação aos manipuladores com base no código de controle de E/S. Esses manipuladores abrangem uma ampla gama de operações, desde manipulação de arquivos até encerramento de processos e drivers, fornecendo um conjunto de ferramentas abrangente que pode ser usado para encerrar ou desabilitar permanentemente sistemas EDR.
Detalhamos os diferentes controles de IO na tabela abaixo:
Nome | Código |
---|---|
Habilitar malware | 0x222080 |
Copiar arquivo | 0x222184 |
Remover retornos de chamada e dispositivos por nome de módulo | 0x222400 |
Substituir as principais funções do driver pelo nome do módulo | 0x222404 |
Matar threads do sistema pelo nome do módulo | 0x222408 |
Desconecte os dispositivos de minifiltro | 0x222440 |
Excluir arquivo | 0x222180 |
Desabilitar malware | 0x222084 |
Carregar api | 0x2220c0 |
Diminuir o contador de referência de todos os drivers | 0x222100 |
Diminuir contador de referência de todos os dispositivos | 0x222104 |
Encerrar processo | 0x222144 |
Terminar tópico | 0x222140 |
Removendo ganchos das principais funções dos drivers Ntfs e Pnp | 0x222444 |
Reinício | 0x222664 |
Habilitando o malware (0x222080)
Conforme discutido nesta postagem do blog, o cliente deve habilitar o driver enviando uma senha (7N6bCAoECbItsUR5-h4Rp2nkQxybfKb0F-wgbJGHGh20pWUuN1-ZxfXdiOYps6HTp0X
) para o driver, no nosso caso é por meio do controle de E/S 0x222080
.
O manipulador simplesmente compara a entrada do usuário com a senha codificada. Se estiver correto, ele define um sinalizador global como verdadeiro (1). Este sinalizador é verificado em todos os outros manipuladores para permitir ou negar a execução.
Carregando a API (0x2220c0)
A maioria dos manipuladores de malware depende de APIs do kernel que devem ser carregadas usando esse manipulador. Este manipulador carrega esses globais junto com diversas estruturas, usando os ponteiros do módulo do kernel carregados anteriormente durante a inicialização. Quando o carregamento estiver concluído, um sinalizador global será definido para sinalizar a disponibilidade dessas APIs.
Este manipulador tem dois modos de operação: um modo completo e um modo parcial. No modo completo, ele carrega as APIs usando uma estrutura de mapeamento de nomes de funções e RVA fornecidos pelo usuário como entrada para o controle de E/S. No modo parcial, ele procura algumas APIs por conta própria, mas não carrega todas as APIs que são carregadas no modo completo, daí o termo modo parcial. Se o usuário optar pelo modo parcial devido à incapacidade de fornecer essa estrutura de mapeamento, alguns manipuladores não serão executados. Neste capítulo, abordamos apenas o modo completo de operação.
Detalhamos abaixo as estruturas utilizadas:
#define AM_NAME_LENGTH 256
typedef struct _struct_435
{
uint64_t rva;
char name[AM_NAME_LENGTH];
} struct_435_t;
#define AM_ARRAY_LENGTH 1024
typedef struct _struct_433
{
struct_435_t array[AM_ARRAY_LENGTH];
uint32_t length;
} struct_433_t;
Fornecemos um breve exemplo de uso abaixo:
struct_433_t api_mapping = {
.length = 25,
.array = {
[0] = {.rva = 0xcec620, .name = "PspLoadImageNotifyRoutine"},
[1] = {.rva = 0xcec220, .name = "PspCreateThreadNotifyRoutine"},
[2] = {.rva = 0xcec420, .name = "PspCreateProcessNotifyRoutine"},
// (...)
[24] = {.rva = 0x250060, .name = "NtfsFsdShutdown"},
}};
uint32_t malware_load_api(HANDLE device)
{
return send_ioctrl(device, IOCTRL_LOAD_API, &api_mapping, sizeof(struct_433_t), NULL, 0);
}
Para carregar sua API, a função começa carregando três 'listas de retorno de chamada' de diferentes tipos de objetos do kernel. Eles são usados pelo manipulador que remove retornos de chamada de notificação registrados pertencentes a um módulo específico.
Em seguida, ele carrega ponteiros para funções usando a estrutura fornecida, simplesmente pesquisando o nome da função e adicionando o RVA associado ao endereço base do módulo.
Isso é feito para as seguintes funções 25 :
PspLoadImageNotifyRoutine
PspCreateThreadNotifyRoutine
PspCreateProcessNotifyRoutine
CallbackListHead
PspSetCreateProcessNotifyRoutine
PspTerminateThreadByPointer
PsTerminateProcess
IopInvalidDeviceRequest
ClassGlobalDispatch
NtfsFsdRead
NtfsFsdWrite
NtfsFsdLockControl
NtfsFsdDirectoryControl
NtfsFsdClose
NtfsFsdCleanup
NtfsFsdCreate
NtfsFsdDispatchWait
NtfsFsdDispatchSwitch
NtfsFsdDispatch
NtfsFsdFlushBuffers
NtfsFsdDeviceControl
NtfsFsdFileSystemControl
NtfsFsdSetInformation
NtfsFsdPnp
NtfsFsdShutdown
Cópia e exclusão de arquivos (0x222184, 0x222180)
Para copiar ou excluir arquivos, o ABYSSWORKER conta com uma estratégia que, embora não seja nova, continua interessante. Em vez de usar uma API comum como NtCreateFile
, um Pacote de Solicitação de E/S (IRP) é criado do zero e enviado diretamente para o dispositivo de unidade correspondente que contém o arquivo de destino.
Criando um arquivo
A função de criação de arquivo é usada para mostrar como esse mecanismo funciona. A função começa obtendo o dispositivo de acionamento do caminho do arquivo. Em seguida, um novo objeto de arquivo é criado e vinculado ao dispositivo de unidade de destino, garantindo que o novo objeto seja vinculado corretamente à unidade.
Em seguida, ele cria um novo objeto IRP e define todos os dados necessários para executar a operação de criação de arquivo. A principal função visada por este IRP é especificada na propriedade MajorFunction
, que, neste caso, é definida como IRP_MJ_CREATE
, conforme esperado para a criação de arquivos.
Em seguida, o malware envia o IRP para o dispositivo de destino. Embora pudesse ter usado a API IoCallDriver
para fazer isso, ele envia o IRP manualmente chamando a função principal do dispositivo correspondente.
Neste ponto, o objeto de arquivo é válido para uso posterior. O manipulador finaliza seu trabalho incrementando o contador de referência do objeto de arquivo e atribuindo-o ao seu parâmetro de saída para uso posterior.
Copiando um arquivo
Para copiar um arquivo, o ABYSSWORKER abre os arquivos de origem e de destino, lê (IRP_MJ_READ
) da origem e grava (IRP_MJ_WRITE
) no destino.
Excluindo um arquivo
O manipulador de exclusão define o atributo de arquivo como ATTRIBUTE_NORMAL
para desproteger qualquer arquivo somente leitura e define a disposição do arquivo como delete (disposition_info.DeleteFile = 1
) para remover o arquivo usando o IRP IRP_MJ_SET_INFORMATION
.
Remoção de retornos de notificação por nome de módulo (0x222400)
Clientes de malware podem usar esse manipulador para ocultar produtos EDR e sua visibilidade. Ele procura e remove todos os retornos de chamada de notificação registrados. Os retornos de chamada direcionados são aqueles registrados com as seguintes APIs:
PsSetCreateProcessNotifyRoutine
PsSetLoadImageNotifyRoutine
PsSetCreateThreadNotifyRoutine
ObRegisterCallbacks
CmRegisterCallback
Além disso, ele remove retornos de chamada registrados por meio de um driver MiniFilter e, opcionalmente, remove dispositivos pertencentes a um módulo específico.
Para excluir esses retornos de chamada de notificação, o manipulador os localiza usando vários métodos, como as três listas globais de retornos de chamada carregadas anteriormente no manipulador da API de carregamento, que contêm retornos de chamada registrados com ObRegisterCallbacks
e CmRegisterCallback
. Em seguida, ele os exclui usando as APIs correspondentes, como ObUnRegisterCallbacks
e CmUnRegisterCallbacks
.
O uso desses métodos para cegar o EDR merece um post inteiro no blog. Para manter esta postagem concisa, não forneceremos mais detalhes aqui, mas convidamos o leitor a explorar esses métodos em dois projetos bem documentados que implementam essas técnicas:
Substituir as principais funções do driver pelo nome do módulo 0x222404
Outra maneira de interferir em um driver é usar esse manipulador para substituir todas as suas funções principais por uma função fictícia, desabilitando assim qualquer interação com o driver, dado um nome de módulo de destino.
Para conseguir isso, o ABYSSWORKER itera pelos objetos do driver nos diretórios de objetos Driver
e Filesystem
. Para cada objeto de driver, ele compara o nome do módulo subjacente ao do módulo de destino e, se eles corresponderem, ele substitui todas as suas principais funções por IopInvalidDeviceRequest
.
Desanexar dispositivos de mini filtro (0x222440)
Este manipulador itera sobre todos os objetos de driver encontrados nos diretórios de objetos Driver
e FileSystem
. Para cada driver, ele explora sua árvore de dispositivos e destaca todos os dispositivos associados ao driver do mini filtro: FltMgr.sys
.
A função funciona iterando sobre os dispositivos do driver por meio dos ponteiros AttachedDevice
e NextDevice
, recuperando o nome do módulo do driver associado de cada dispositivo e comparando-o com o nome do módulo de destino passado como parâmetro (”FltMgr.sys”
). Se os nomes corresponderem, ele usa a função IoDetachDevice
para desvincular o dispositivo.
Matar threads do sistema pelo nome do módulo (0x222408)
Este manipulador itera sobre threads forçando brutamente seus IDs de thread e os mata se a thread for uma thread do sistema e seu endereço inicial pertencer ao módulo de destino.
Para encerrar o thread, o malware enfileira uma APC (chamada de procedimento assíncrona) para executar código no contexto do thread alvo. Uma vez executado, este código, por sua vez, chamará PsTerminateSystemThread
.
Terminar processo e terminar thread (0x222144, 0x222140)
Com esses dois manipuladores, você pode encerrar qualquer processo ou thread pelo seu PID ou Thread ID (TID) usando PsTerminateProcess
e PsTerminateThread
.
Removendo ganchos das principais funções dos drivers Ntfs e Pnp (0x222444)
Além de registrar retornos de chamada de notificação, alguns EDRs gostam de conectar funções importantes dos drivers NTFS
e PNP
. Para remover esses ganchos, o malware pode chamar esse driver para restaurar as principais funções originais desses drivers.
O ABYSSWORKER simplesmente itera sobre cada função principal registrada, verifica se a função pertence ao módulo do driver e, caso contrário, significa que a função foi conectada, então ele a substitui pelas funções originais.
Reinício 0x222664
Para reinicializar a máquina, este manipulador usa a função não documentada HalReturnToFirmware
.
Exemplo de implementação do cliente
Nesta postagem do blog, fornecemos um pequeno exemplo de implementação de cliente. Este exemplo funciona com o exemplo de referência e foi usado para depurá-lo, mas não implementa todos os IOCTRLs para o driver e é improvável que seja atualizado no futuro.
No entanto, ele contém todas as funções para habilitá-lo e carregar sua API, então esperamos que qualquer leitor motivado, com a ajuda das informações deste artigo, consiga estendê-lo e experimentar ainda mais esse malware.
O repositório do projeto está disponível aqui.
Malware e MITRE ATT&CK
A Elastic usa a estrutura MITRE ATT&CK para documentar táticas, técnicas e procedimentos comuns que as ameaças usam contra redes corporativas.
Táticas
Técnicas
Técnicas representam como um adversário atinge um objetivo tático executando uma ação.
- Modificação de permissões de arquivos e diretórios
- Desabilitar ou modificar ferramentas
- Assinatura de código
Mitigações
YARA
A Elastic Security criou as seguintes regras YARA relacionadas a esta postagem:
Observações
Os seguintes observáveis foram discutidos nesta pesquisa:
Observável | Tipo | Referência | Data |
---|---|---|---|
6a2a0f9c56ee9bf7b62e1d4e1929d13046cd78a93d8c607fe4728cc5b1e8d050 | SHA256 | Amostra de referência ABYSSWORKER | VT visto pela primeira vez: 2025-01-22 |
b7703a59c39a0d2f7ef6422945aaeaaf061431af0533557246397551b8eed505 | SHA256 | Amostra ABYSSWORKER | VT visto pela primeira vez: 2025-01-27 |
Referências
- Google Cloud Mandiant, Mandiant Intelligence. Juro solenemente que meu motorista não está tramando nada de bom: caçando malware com assinatura de atestado. https://cloud.google.com/blog/topics/threat-intelligence/hunting-attestation-signed-malware/
- Unidade 42, Jerome Tujague, Daniel Bunce. Crypted Hearts: Expondo a operação HeartCrypt Packer-as-a-Service, dezembro 13, 2024. https://unit42.paloaltonetworks.com/packer-as-a-service-heartcrypt-malware/
- Conecte-se com sabedoria, Blake Eakin. "Atacantes aproveitam padrões do Microsoft Teams e assistência rápida para ataques de engenharia social", janeiro 31 2025. https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/
- wavestone-cdt, agosto 30, 2024. https://github.com/wavestone-cdt/EDRSandblast/tree/master
- myzxcg, maio 24, 2024. https://github.com/myzxcg/RealBlindingEDR