lacort.eesistemas * backend * segurança ofensiva

cat~/writing/pt-br/ransomhub-esxi.md2024-12-24 · 8 min

Ransomware RansomHub

2024-12-248 min1.5k palavras

Análise técnica do ransomware para ESXi do grupo RansomHub

Resumo

Este relatório apresenta minha análise detalhada das principais funções do ransomware para ESXi do grupo RansomHub.

CTI do RansomHub

O RansomHub surgiu como uma operação de ransomware-as-a-service (RaaS), especialmente após a desarticulação de outros grupos grandes como o LockBit. O grupo parece ter crescido rápido na segunda metade de 2024. Vale notar que o RansomHub é tido como ligado a ex-afiliados de outros grupos, como LockBit e BlackCat, e adotou várias táticas desses predecessores, incluindo estratégias de “living-off-the-land” e alvos multiplataforma, tanto Linux quanto Windows.

As atividades do grupo têm algumas características distintas. Consta que o RansomHub dá aos afiliados o controle direto sobre os pagamentos de resgate, o que o tornou atraente para criminosos vindos de outros grupos. Foi associado a vários ataques de repercussão, incluindo os contra empresas como Halliburton e Kawasaki Europe. Além disso, o RansomHub explora vulnerabilidades em diversos setores, inclusive saúde, e é conhecido pelas práticas de exfiltração de dados.

O RansomHub atraiu um conjunto notável de afiliados, incluindo pessoas que antes trabalhavam nas operações hoje extintas do BlackCat e do LockBit. Por isso é visto como uma nova potência no ecossistema de ransomware, usando técnicas sofisticadas de extorsão e oferecendo pagamentos significativos aos afiliados.


Análise Estática de Código

Processo Daemon

Ao iniciar a execução, a primeira coisa que o ransomware faz depois de interpretar a linha de comando é verificar a presença do argumento -verbose. Se esse argumento não for informado, o programa se configura para rodar como processo daemon, ou seja, em segundo plano.

A execução em segundo plano começa com a criação de um processo filho pela chamada de sistema fork(), que duplica o processo atual criando uma instância separada.

Se a criação do processo der certo, o processo pai (o original) espera a conclusão do filho, onde o ransomware seguirá com sua execução principal, usando waitpid().

No processo filho, onde o ransomware executa, a função setsid() é usada para criar uma nova sessão. Isso desacopla o processo de qualquer terminal de controle. Se setsid() falhar, o processo é encerrado imediatamente com exit(1).

fig.1Daemonização: tudo que o locker faz antes de encostar em um único arquivo.

Os descritores padrão do processo filho, stdin, stdout e stderr, também são fechados com close(). Isso impede que o daemon continue interagindo com o terminal. Os descritores fechados são então redirecionados para /dev/null e duplicados com dup(), forçando qualquer tentativa de acessar os canais padrão a cair em /dev/null.

child_proc_pid_var = fork();
if ( child_proc_pid_var < 0 )
  exit(1);

if ( child_proc_pid_var > 0 )
{
  puts("running...");

  waitpid(child_proc_pid_var, stat_loc, 0);
  
  puts("done");
  exit(0);
}

if ( setsid() < 0 )
  exit(1);
  
close(0);
close(1);
close(2);

open("/dev/null", 2);

dup(0);
dup(0);

Decifragem da Configuração

Antes de executar a parte principal do malware, o ransomware precisa decifrar sua configuração, e para isso exige que o operador forneça uma senha como argumento. Essa senha é usada em operações de decifragem baseadas em XOR sobre a configuração cifrada.

Com a senha informada, o programa a transforma com operações de manipulação binária e depois compara o resultado com o dado esperado dentro do malware. Se a senha estiver errada, uma mensagem de erro é exibida e o processo termina.

Se a senha estiver correta, o malware usa operações XOR para decifrar os dados. O processo resulta em um JSON no formato abaixo:

{
    "master_public_key": ,   chave pública curve25519
    "extension": ,           extensão que será adicionada aos arquivos cifrados.
    "note_file_name": ,      nome do arquivo da nota de resgate.
    "note_full_text": ,      texto completo da nota de resgate.
    "note_short_text": ,     texto personalizado na nota, adicionado pelo operador.
    "encryption_files": ,    lista de extensões de arquivo que devem ser cifradas.
    "remove_vms_snapshot": , indica se os snapshots das máquinas virtuais devem ser removidos.
    "shutdown_vms": ,        indica se as máquinas virtuais devem ser desligadas.
    "self_delete": ,         indica se o ransomware deve se apagar após a execução.
}

Preparando o ESXi para a Cifragem

Antes de iniciar o processo de cifragem, o ransomware executa uma série de passos para manipular o ESXi e garantir que o processo ocorra sem interferência.

Desabilitar o Auto Start das Máquinas Virtuais

A primeira ação do malware é desabilitar a função de auto start das máquinas virtuais no host ESXi.

Isso impede que as VMs reiniciem sozinhas ou tentem subir depois da cifragem, o que poderia corromper arquivos. O comando usado é:

/bin/sh -c vim-cmd hostsvc/autostartmanager/enable_autostart 0

Encerramento do Syslog

Para dificultar o registro e a análise, o ransomware encerra o processo responsável pelo syslog (vmsyslogd), que gerencia os logs do sistema.

Ele usa o comando abaixo para localizar e encerrar todas as instâncias em execução:

/bin/sh -c for i in $(ps -Cc | grep vmsyslogd | awk '!/grep/ {print $1}' | grep -o '[0-9]*'); do kill -9 $i;  done;

O comando usa ps para listar os processos, grep para localizar o vmsyslogd e kill -9 para forçar o encerramento de cada processo encontrado.

Removendo Snapshots

O ransomware verifica se está configurado para remover snapshots das máquinas virtuais, impedindo a recuperação após o ataque. Se essa opção estiver ativa, ele executa:

/bin/sh -c for i in $(vim-cmd vmsvc/getallvms | awk '{print $1}' | grep -o '[0-9]*'); do vim-cmd vmsvc/snapshot.removeall $i; done;

Parando as Máquinas Virtuais

O ransomware pode ser configurado para interromper a execução das máquinas virtuais. Se estiver, ele executa os seguintes passos:

  1. Verifica se o argumento -skip_vms foi especificado. Se sim, lê o arquivo com os nomes das máquinas virtuais que não devem ser desligadas e guarda a lista em uma variável.
  2. Obtém a lista de todas as máquinas virtuais em execução, com o comando:
/bin/sh -c localcli --formatter json vm process list 2>/dev/null
  1. O ransomware interpreta a saída JSON, comparando o campo Display Name de cada máquina virtual com a lista de máquinas configuradas para serem poupadas.
  2. Para todas as máquinas virtuais que não estão na lista de exclusão, o ransomware executa o comando a seguir para desligá-la à força, usando o World ID correspondente da VM:
/bin/sh -c localcli vm process kill --type=force --world-id=%d 2>/dev/null

Thread Pool

Depois da inicialização, o ransomware entra na fase principal de execução.

Ele começa inicializando um thread pool, onde o número de threads é calculado como o dobro do número de processadores disponíveis.

thread_pool_var = thread_pool_init_fn(2 * processors_count);

Depois de inicializar o thread pool, o ransomware chama uma função para procurar os arquivos que devem ser cifrados. O caminho inicial da busca é indicado pelo argumento -path.

Quando a busca termina, uma função espera que todas as tarefas pendentes no thread pool acabem antes de destruí-lo:

thread_pool_wait_fn(thread_pool_var);
thread_pool_destroy_fn(thread_pool_var);

Busca Recursiva

Para achar os arquivos que devem ser cifrados, o ransomware usa um algoritmo de busca recursiva que percorre diretórios e arquivos.

Escrita da Nota

Antes de processar o conteúdo de um diretório, o ransomware cria a nota de resgate. A nota é gerada concatenando o caminho do diretório atual com o nome do arquivo de nota definido na configuração:

strcpy(note_path_var, path_var);
strcat(note_path_var, "/");
strcat(note_path_var, *((const char **)&config + 2)); /* nome do arquivo de nota, vindo da config */

note_file_handle_var = fopen(note_path_var, "w");
if ( note_file_handle_var )
{
    fwrite(*((const void **)&config + 3), 1, strlen(*((const char **)&config + 3)), note_file_handle_var); /* conteúdo da nota, vindo da config */
    fclose(note_file_handle_var);
}

Busca de Arquivos

A busca por arquivos começa abrindo o diretório com a função opendir(). Em seguida, cada entrada do diretório é analisada em um laço while usando readdir().

Se a entrada for um diretório, ela é concatenada com o caminho atual e a função de busca recursiva é chamada de novo com o caminho novo.

Se a entrada for um arquivo, o ransomware verifica se é um arquivo de máquina virtual e se não pertence a uma lista de VMs a ignorar.

A lista de extensões usada por padrão para dizer que se trata de um arquivo de máquina virtual é:

.vmdk, .vmx, .vmsn. vswp, .vmxf, .vhd, .vhdx, .iso, .vmx.lck, .nvram, .img

Se os critérios forem atendidos, o arquivo é adicionado como nova tarefa no thread pool para ser cifrado.

Cifragem de Arquivos

Assim que um arquivo entra no thread pool, o processo de cifragem começa.

Geração de Chaves

Primeiro, o ransomware gera uma chave privada aleatória para o algoritmo curve25519. Dessa chave privada deriva-se uma chave pública, usada depois para criar uma chave compartilhada. A chave pública gerada pelo ransomware é combinada com uma chave pública mestra (guardada na configuração decifrada), e daí se deriva a chave compartilhada. Essa chave compartilhada será a chave do processo de cifragem simétrica.

Depois de gerar as chaves, o ransomware verifica se o processador da máquina onde está rodando suporta o conjunto de instruções AES-NI. Se o processador for compatível, ele usa AES-256 com otimizações SIMD. Caso contrário, usa o algoritmo ChaCha20 com um nonce aleatório.

Processamento do Arquivo

A cifragem do arquivo é feita de forma intermitente: o ransomware pula blocos de dados entre as operações de cifragem. A quantidade de dados “pulada” entre cada operação depende do tamanho do arquivo que está sendo cifrado.

A tabela abaixo mostra os valores de salto entre os blocos cifrados, definidos conforme o tamanho do arquivo:

Tamanho do ArquivoTamanho do Salto
≤ 2 MB0 B
> 2 MB ≤ 8 MB1 MB
> 8 MB ≤ 32 MB2 MB
> 32 MB ≤ 128 MB3 MB
> 128 MB ≤ 512 MB4 MB
> 512 MB ≤ 2 GB5 MB
> 2 GB ≤ 8 GB6 MB
> 8 GB ≤ 32 GB7 MB
> 32 GB ≤ 128 GB8 MB
> 128 GB ≤ 512 GB9 MB
> 512 GB10 MB

Se o parâmetro -fast for especificado, o valor do salto é multiplicado por quatro, aumentando a quantidade de dados pulada entre as partes cifradas, o que acelera o processo.

Depois de definir o tamanho do salto, o ransomware cria uma estrutura de dados que guarda informações como o tamanho do salto, a chave pública do arquivo, o nonce e outras variáveis que serão usadas para decifrar o arquivo depois. Essa estrutura é então escrita no fim do arquivo cifrado.

A fórmula usada para calcular o número de partes que precisam ser cifradas:

part_size = ((skip_size + 1MB) + file_size -1 ) / skip_size 1MB

Determinado o número de partes, o ransomware percorre o arquivo em um laço e cifra cada parte. Para cada uma, lê 1MB de dados, cifra com o algoritmo escolhido (AES-256 ou ChaCha20) e escreve os dados cifrados de volta no arquivo. O offset é atualizado a cada iteração, pulando a quantidade definida pelo tamanho do salto.

Abaixo, um trecho do código decompilado que cifra o arquivo:

skip_size = calculate_skip_size(file_size);
part_size = skip_size + 0x100000;
parts_count = (part_size + file_size - 1) / part_size;

if ( parts_count )
{
  offset = 0;
  for ( i = 0; i < parts_count; ++i )
  {
    fseek(file_handle, offset, 0);

    bytes_read = fread(file_1mb_buffer, 1, 0x100000, file_handle);
    if ( !bytes_read )
      break;

    if ( have_aes_ni )
      aes_encrypt(file_1mb_buffer,file_1mb_buffer, bytes_read, &v33, aes_context);
    else
      chacha_encrypt(chacha_context, file_1mb_buffer, bytes_read);

    fseek(file_handle, -bytes_read, 1);

    fwrite(file_1mb_buffer, 1, bytes_read, file_handle);

    offset += part_size;
  }
}

Cifrados todos os dados, o arquivo é renomeado com a extensão configurada no ransomware pela função rename().

Auto-Exclusão

Após a execução, se estiver configurado para isso, o ransomware se apaga com:

unlink(argv[0]);

Regra Yara

rule RansomHub_ESXi_Ransomware
{
    meta:
        description = "Detects RansomHub ESXi Ransomware"
        author = "@Gabriel-Lacorte"
    strings:
        $cmd_autostart = "vim-cmd hostsvc/autostartmanager/enable_autostart 0" ascii wide

        $cmd_syslog = "for i in $(ps -Cc | grep vmsyslogd | awk '{print $1}'); do kill -9 $i; done;" ascii wide

        $cmd_snapshot = "for i in $(vim-cmd vmsvc/getallvms | awk '{print $1}'); do vim-cmd vmsvc/snapshot.removeall $i; done;" ascii wide
    condition:
        any of ($cmd_*)
}

<- voltar para ~/blog

lacort.eetake a chill pillinternet privacy now!Esse site faz parte do WebCírculo BR