Voltar para blog

Gestão de crise e hardening: anatomia de uma resposta a incidente em ambiente Linux

17/02/2026 · 5 min · Infraestrutura

Compartilhar

Durante uma auditoria operacional no ambiente de desenvolvimento de um projeto, identifiquei comportamento compatível com exploração RCE em dependência legada (React2Shell). O incidente não ficou na camada de aplicação: houve indícios de persistência em shell startup e tentativa de execução recorrente em /tmp, com padrão típico de dropper para escalada posterior.

Este artigo documenta exatamente o que executei, em ordem cronológica, comandos incluídos, critérios de decisão e validações de saída. O objetivo foi restaurar disponibilidade com rastreabilidade técnica e reduzir risco residual para nível aceitável de produção.

1) Detecção inicial, triagem e ativação do protocolo

O primeiro alerta não veio de dashboard bonito: veio de comportamento anômalo de processos de diagnóstico e ruído incomum em sessão shell.

Sinais observados na triagem inicial:

Comandos executados na triagem quente:

ps auxf
ps -eo pid,ppid,user,cmd --sort=-%cpu | head -n 40
top -b -n 1 | head -n 60
ss -lntup
ss -plant
journalctl -xe --no-pager | tail -n 200
last -a | head -n 30

Com base nesses indicadores, ativei resposta a incidente em modo contenção.

2) Contenção imediata (janela de impacto)

A contenção teve três objetivos: parar propagação, preservar evidência e evitar perda de disponibilidade em sistemas adjacentes.

Ações executadas:

  1. congelamento de mudanças (deploy e automações de CI/CD do projeto afetado).
  2. bloqueio de tráfego externo não essencial no host comprometido.
  3. isolamento de credenciais de operação usadas naquele host.
  4. preservação de artefatos para análise posterior.

Exemplo de bloqueio emergencial aplicado no host:

# Política restritiva temporária
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP

# Exceções mínimas para administração segura durante resposta
iptables -A INPUT -p tcp --dport 22 -s <SEU_IP_ADMIN>/32 -j ACCEPT
iptables -A OUTPUT -p tcp --sport 22 -d <SEU_IP_ADMIN>/32 -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
Nota operacional: em resposta a incidente, eu privilegio isolamento primeiro, limpeza depois. Sem isolamento, qualquer correção vira corrida contra processo ativo do atacante.

3) Coleta forense mínima viável (antes de tocar no disco)

Antes de remover qualquer arquivo, executei coleta de evidências para manter cronologia dos eventos e sustentar análise técnica posterior.

Itens coletados:

Exemplo de coleta aplicada:

mkdir -p /root/ir-evidence/{logs,proc,net,fs}
date -u > /root/ir-evidence/timestamp_utc.txt

ps auxf > /root/ir-evidence/proc/ps_auxf.txt
ss -plant > /root/ir-evidence/net/ss_plant.txt
ss -lntup > /root/ir-evidence/net/ss_lntup.txt
journalctl -b --no-pager > /root/ir-evidence/logs/journal_current_boot.log

find /tmp -maxdepth 2 -type f -printf "%TY-%Tm-%Td %TT %p\n" \
  > /root/ir-evidence/fs/tmp_file_timeline.txt
find /etc/profile* -maxdepth 1 -type f -exec sha256sum {} \; \
  > /root/ir-evidence/fs/profile_hashes.txt

Compactei evidências para retenção offline:

tar -czf /root/ir-evidence-$(date +%F-%H%M).tar.gz /root/ir-evidence
sha256sum /root/ir-evidence-*.tar.gz > /root/ir-evidence.sha256

4) Recuperação forense em Rescue Mode (sem SO comprometido em execução)

Para eliminar qualquer interferência do ambiente contaminado, migrei o host para Rescue Mode e trabalhei com montagem passiva.

Fluxo executado:

  1. reboot em modo rescue via painel do provedor.
  2. identificação de partições e montagem em leitura.
  3. extração seletiva de dados confiáveis.
  4. revisão de persistência SSH e shell startup fora do runtime comprometido.

Sequência base utilizada:

lsblk
blkid
mount /dev/vda2 /mnt/sysroot
mount -o remount,ro /mnt/sysroot

4.1) Backup cirúrgico (somente o que é confiável)

Copiei apenas o que era necessário para continuidade de negócio:

Itens explicitamente excluídos:

Exemplo de extração seletiva:

rsync -aHAX --numeric-ids \
  --exclude='node_modules' \
  --exclude='.cache' \
  --exclude='tmp' \
  /mnt/sysroot/home/domain_user/app/ /mnt/backup/app/

4.2) Auditoria de chaves e persistência

Revisei e saneei todos os pontos de acesso persistente:

cat /mnt/sysroot/root/.ssh/authorized_keys
cat /mnt/sysroot/home/*/.ssh/authorized_keys
grep -R "ssh-rsa\|ssh-ed25519" /mnt/sysroot/home -n
find /mnt/sysroot/etc -type f -name "*profile*" -o -name "*rc" | sort

Entradas não reconhecidas foram removidas, registradas e correlacionadas com o horário dos eventos no relatório de incidente.

5) Decisão técnica: rebuild completo (clean slate)

Com evidência de tentativa de persistência e vetor com potencial de escalada, optei por rebuild completo do host.

Critério usado para descartar "limpeza parcial":

  1. confiança comprometida em integridade de bibliotecas e binários.
  2. alto custo de validar 100% de um sistema possivelmente adulterado.
  3. risco residual inaceitável para retorno de carga em produção.

Resultado: formatação e reinstalação completa do SO como única opção com rastreabilidade e compliance defensável.

6) Hardening pós-reinstalação (Rocky Linux baseline)

Após reinstalar Rocky Linux, apliquei uma baseline prática de endurecimento em camadas.

6.1) Segregação de privilégios e runtime sem root

Criei usuário de sistema dedicado para execução da aplicação, sem shell de administração para tarefas comuns.

sudo adduser --system --group --home /home/domain_user domain_user
id domain_user

Ajustei ownership da aplicação para o usuário de runtime e removi permissões excessivas de escrita em diretórios sensíveis.

6.2) Endurecimento de filesystem temporário

Configurei /tmp com noexec,nosuid,nodev para reduzir vetor de execução de payload em diretório temporário.

echo "tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0" >> /etc/fstab
mount -o remount /tmp
findmnt /tmp

6.3) Hardening de SSH

No /etc/ssh/sshd_config, apliquei:

Validação e aplicação:

sshd -t
systemctl restart sshd
systemctl status sshd --no-pager

6.4) Perímetro com firewalld (deny by default)

Substituí regra aberta por política de menor privilégio:

firewall-cmd --permanent --set-default-zone=drop
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-port=80/tcp
firewall-cmd --permanent --add-port=443/tcp
firewall-cmd --reload
firewall-cmd --list-all

6.5) Isolamento lógico de dados

Mantive dados operacionais em /home e isolei caminho de aplicação do core do SO para simplificar backup, restore e governança de permissões.

mkdir -p /home/www
ln -s /home/www /www
ls -la /

6.6) Regras de kernel e hygiene básica

Apliquei parâmetros de rede conservadores para reduzir superfícies conhecidas:

cat >/etc/sysctl.d/99-hardening.conf <<'SYSCTL'
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.tcp_syncookies = 1
SYSCTL

sysctl --system

7) Restauração de aplicação e deploy controlado

Com host endurecido, executei restauração com foco em previsibilidade:

  1. restauração de código limpo e configuração auditada.
  2. reinstalação de dependências a partir de lockfile.
  3. ajuste de runtime RHEL-like (incluindo xdg-utils para tooling de dev).
  4. subida com gerência de processo e observabilidade mínima.

Stack operacional aplicada:

Validações de pós-deploy:

pm2 status
pm2 logs --lines 100
ss -lntup | grep -E ':80|:443|:3000'
systemctl status sshd firewalld --no-pager

Critério de saída do incidente:

8) Checklist operacional executado

A principal lição operacional foi objetiva: em cenário com tentativa de escalada, rapidez sem método aumenta risco. O protocolo que funcionou foi sequencial e disciplinado: detectar, conter, preservar evidência, reconstruir, endurecer e só então restaurar carga.

Esse tipo de resposta não é teoria. É execução técnica orientada a continuidade, auditoria e redução real de superfície de ataque em ambiente Linux.

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.