Sumário
O ransomware Play, também conhecido como PlayCrypt, é um ransomware que surgiu pela primeira vez em junho de 2022. O ransomware tem como alvo setores como saúde e telecomunicações, bem como uma ampla variedade de regiões, como América Latina, Europa e América do Norte. O ransomware Play é conhecido por obter acesso às redes por meio de contas válidas comprometidas ou pela exploração de vulnerabilidades específicas. Uma vez dentro da rede, ele usa um grande conjunto de ferramentas conhecidas de pós-exploração para continuar seu ataque. Ferramentas como Bloodhound, PsExec, Mimikatz e AdFind são alguns exemplos de ferramentas usadas anteriormente em ataques envolvendo esse ransomware.
Outro aspecto do malware que o torna famoso é a quantidade de técnicas anti-análise que ele usa em suas cargas, como abusar do SEH e usar o ROP para redirecionar o fluxo de execução. Ao empregar técnicas para retardar o processo de engenharia reversa, os agentes de ameaças dificultam a detecção e a prevenção do malware.
Em 2022, outros pesquisadores publicaram uma excelente postagem no blog analisando o malware em si e algumas das técnicas anti-análise que ele usava. Nesta postagem do blog, revisaremos as técnicas de anti-análise empregadas pelas variantes recentes do ransomware Play, explicando como elas funcionam e também como podemos derrotar algumas delas usando scripts de automação.
Programação orientada ao retorno (ROP)
Ao fazer engenharia reversa de malware, garantir que o fluxo de controle não seja ofuscado é uma das primeiras coisas que precisamos fazer para entender adequadamente o malware.
Como uma tentativa de ofuscar seu fluxo de controle, o ransomware Play geralmente usa uma técnica de ROP em sua carga útil. Ele faz isso chamando mais de uma centena de funções que corrigem o valor no topo da pilha e, em seguida, redirecionam o fluxo de execução para ele. Antes de falarmos sobre como o malware faz exatamente isso, vamos dar uma olhada em como as instruções de montagem CALL e RET funcionam em geral.
Quando ocorre uma CHAMADA (neste caso, uma chamada próxima, para ser mais específica), o processador envia o valor do registrador do ponteiro de instrução (EIP neste caso) na pilha e, em seguida, ramifica para o endereço especificado pelo operando de destino da chamada, que nesse caso é um deslocamento em relação ao ponteiro de instrução. O endereço no ponteiro de instrução, mais esse deslocamento, resultará no endereço da função a ser chamada.
A instrução RET, por outro lado, indica o fim de uma chamada de função. Essa instrução é responsável por transferir o fluxo de controle do programa para o endereço no topo da pilha. E sim, esse é exatamente o endereço enviado inicialmente pela instrução de chamada!
Considerando o que foi mencionado, em um cenário ideal, o endereço destacado na imagem abaixo seria a próxima instrução a ser executada após uma chamada para a função de destino (sub_42a4b9):
O que o malware faz para abusar do funcionamento das instruções CALL e RET pode ser observado na imagem a seguir:
Depois que a função é chamada, o endereço 0x42a4b4 é colocado na pilha, então o ESP apontará para ele. A função chamada então adiciona o valor 0xA ao endereço apontado pelo ESP e retorna usando a instrução RET. Essas operações resultam no redirecionamento do fluxo de controle para 0x42a4be (0x42a4b4 + 0xa) em vez de 0x42a4b4.
Ao aplicar essa técnica, o malware torna não apenas a análise estática mais complexa, já que o fluxo do programa não será trivial, mas também pode dificultar a depuração, pois se você passar por cima desse tipo de função, muitas coisas podem acontecer antes que a “próxima instrução” regular seja executada.
Outra maneira pela qual o malware implementa essa técnica de ROP é usando a abordagem mostrada no código abaixo, que é muito comum em códigos de shell. O deslocamento especificado pelo operando de destino da instrução de chamada é zero, resultando no endereço da função a ser chamada exatamente como o endereço da próxima instrução. Esse endereço é então colocado no topo da pilha e o resto das operações são exatamente iguais às do exemplo anterior:
Para ajudar na análise do ransomware Play, o Netskope Threat Labs desenvolveu um script, com base no trabalho anterior de outros pesquisadores, para corrigir a ofuscação ROP empregada.
O script procura possíveis candidatos ao ROP, coleta o deslocamento a ser adicionado ao topo da pilha e corrige os endereços que executam as chamadas de ROP com um salto absoluto, em que o alvo é o endereço de transferência modificado calculado em tempo de execução.
Veja a seguir um exemplo de como seria a carga útil do malware antes e depois da execução do script:
Antes:
Depois de:
Anti-desmontagem
Uma técnica antidesmontagem usada para enganar analistas e desmontadores é transferir o fluxo de execução para alvos localizados no meio de outras instruções válidas.
Vamos usar a chamada de função em 0x42a4af usada na seção ROP acima como exemplo. Os opcodes para essa instrução CALL são “E8 05 00 00 00”. O byte 0xE8 é o opcode da própria instrução CALL e os outros 4 bytes representam o operando de destino (o deslocamento em relação ao EIP).
Como discutimos anteriormente, o endereço da função a ser chamada seria o valor de EIP (0x42a4b4) + o deslocamento (0x5) e isso resultaria no endereço 0x42a4b9. No entanto, esse valor cai no último byte de outra instrução válida em 0x42a4b5:
Em termos de execução, esse tipo de comportamento não muda nada porque o processador entenderá as instruções corretamente. No entanto, um desmontador pode não apresentar as instruções corretamente, dependendo da abordagem usada (por exemplo. varredura linear), tornando a análise estática um pouco complicada.
O script que fornecemos para corrigir as chamadas de ROP também lida com esse cenário para os alvos de ROP. Considerando que usamos uma instrução JMP para corrigir as chamadas, acabamos também forçando o desmontador a entender o fluxo correto a seguir.
Código inútil
Embora essa seja uma técnica muito simples, ainda vale a pena mencioná-la, pois ela pode definitivamente retardar a análise do malware. A técnica de inserção de código inútil ou lixo é exatamente o que o nome sugere: a inserção de instruções desnecessárias no binário.
Diferentemente das técnicas apresentadas até agora, a inserção de código inútil não enganaria o desmontador, mas pode fazer com que se perca tempo analisando códigos desnecessários e o ransomware Play o usa com bastante frequência, especialmente nos alvos de chamadas ROP.
Tratamento estruturado de exceções (SEH)
Outra técnica anti-análise usada pelo malware é abusar de um mecanismo do Windows chamado Tratamento Estruturado de Exceções, usado para lidar com exceções.
No sistema operacional Windows, cada thread tem uma estrutura chamada Thread Environment Block (TEB). Em um ambiente x86, o endereço do TEB está localizado no registro FS e contém informações úteis para o próprio thread e reside no espaço de endereço do processo. O primeiro campo dessa estrutura é outra estrutura chamada Thread Information Block (TIB) e o primeiro item dessa estrutura contém uma lista do que é chamado de “registros de registro de exceções”.
Cada um desses registros é composto por uma estrutura que contém dois valores. O primeiro é um ponteiro para o “próximo” registro na lista e o segundo é um ponteiro para o manipulador responsável por lidar com a exceção.
Em termos simples, quando ocorre uma exceção, o último registro adicionado à lista é chamado e decide se ele tratará a exceção acionada ou não. Se esse não for o caso, o próximo manipulador na lista é chamado e assim por diante até que o final da lista seja alcançado. Nesse caso, o manipulador padrão do Windows é chamado.
A forma como o Play abusa desse recurso é inserindo seu próprio manipulador de exceções na lista de tratamento de exceções e, em seguida, forçando uma exceção. No exemplo abaixo, o registro EBX contém o endereço do manipulador de exceções de malware e, uma vez inserido como o primeiro elemento da lista (as quatro instruções destacadas na parte superior), o malware zera o EAX e o divide por zero, causando uma exceção (as duas instruções destacadas na parte inferior):
Depois que a exceção é acionada, o último manipulador registrado é chamado. O malware usa esse primeiro manipulador para registrar e chamar um segundo, forçando uma segunda exceção, mas agora, por meio da instrução INT1, para gerar uma exceção de depuração. O segundo manipulador registrado é o responsável por avançar na execução “normal” do malware.
Essa técnica pode ser muito irritante ao depurar o malware, pois o depurador interrompe a execução quando a exceção ocorre, forçando-nos a encontrar o manipulador de exceções e garantindo que tenhamos controle sobre ele de antemão.
Ofuscação de cordas
Todas as strings relevantes usadas pelo malware são ofuscadas, dificultando a sinalização estática de qualquer string relevante. Para desofuscar suas strings em tempo de execução, o malware gera uma chave de 8 bytes e a usa como entrada para o algoritmo de desofuscação.
O algoritmo para gerar a chave é bem simples. Ele recebe um valor inicial codificado e executa algumas operações aritméticas básicas dentro de um loop. O contador de ciclos também é um valor codificado que costumava ser muito grande nas amostras analisadas (por exemplo. 0x20c87548).
Podemos simplificar as operações usadas para gerar a chave no seguinte trecho de python:
x = seed
y = 0
i = 0
enquanto i < contador:
a = (x * x) > > 32
b = (x * y) + (x * y)
if y:
y = (a + b) & 0xffffffff
else:
y = a
x = (x * x) & 0xffffffff) + i i += 1
key = struct.pack (" <
2I ", * [x, y])
O algoritmo usado para desofuscar as strings envolve mais algumas etapas e algumas outras operações, como AND, OR e NOT. Essas operações são aplicadas na própria string ofuscada e a última etapa do loop de desofuscação é aplicar uma operação XOR de vários bytes usando a chave de 8 bytes gerada anteriormente. As operações podem ser simplificadas no seguinte trecho de python:
i = 0
enquanto i < len (enc_str):
dec_str [i] = enc_str [i]
j = 0
enquanto j < 8:
v1 = (dec_str [i] > > j) & 1
v2 = (dec_str [i] > > (j + 1)) & 1 se v1!
= v2:
se v1 == 0:
dec_str [i] = (dec_str [i] & ~ (1 < < (j + 1))) & 0xFF
senão: dec_str [i] = (
dec_str [i] | (1 < < (j + 1)) & 0xFF
se v2 == 0: dec_str [i] = (dec_str [i] = (dec_str [i] r [i] & ~ (1 < <)
j)) & 0xFF
senão:
dec_str [i] = (dec_str [i] | (1 < < j)) & 0xFF
j += 2
dec_str [i] = ~dec_str [i] & 0xFF dec_str [i] = (dec_str [i] ^ chave [i% len (chave)]) & 0xFF
i += 1
Vale ressaltar que os algoritmos contêm muitas operações inúteis que não são necessárias para a desofuscação em si e que foram removidas nos trechos apresentados.
A equipe do Netskope Threat Labs criou dois scripts para ajudar no processo de desofuscação: um para gerar a chave de 8 bytes automaticamente e outro para realizar a descriptografia da string.
Hash de API
O malware usa a conhecida técnica de hash de API para resolver as funções da API do Windows que usa em tempo de execução. O algoritmo usado é o mesmo sinalizado em 2022, que é xxHash32, com a mesma semente de 1 fornecida à função de hash.
O algoritmo xxHash32 pode ser facilmente reconhecido devido à quantidade de constantes que ele usa em suas operações:
Como uma tentativa de ofuscar ainda mais as funções com hash, o malware adiciona ou subtrai constantes específicas aos resultados do hash. O valor constante é diferente para cada amostra. Veja a seguir um exemplo em que o malware adicionou o valor 0x4f5dcad4 ao resultado do hash:
Netskope detection
- Proteção Contra Ameaças Netskope
- Win32.Ransomware.Playde
- Netskope Advanced Threat Protection provides proactive coverage against this threat.
- Gen.Malware.Detect.By.Stheur e Gen:Heur.Mint.Zard.55 indicam uma amostra que foi detectada usando análise estática
- Gen.detect.by.NSCloudSandbox.tr indica uma amostra que foi detectada pela nossa sandbox na nuvem
Conclusões
Como podemos observar nesta postagem do blog, o ransomware Play emprega várias técnicas de anti-análise como uma tentativa de desacelerar sua análise. Da ofuscação de sequências ao ROP e ao uso do sequestro de SEH, os agentes de ameaças geralmente atualizam seu arsenal e expandem suas técnicas para tornar o impacto de seus ataques ainda mais destrutivo. O Netskope Threat Labs continuará a acompanhar a evolução do ransomware Play e seu TTP.
IOCs
Todos os indicadores de comprometimento (IOCs) relacionados a esta campanha, scripts e regras Yara podem ser encontrados no repositório GitHub da Netskope.