College Online
0%

Segurança do Sistema Operacional

Módulo 8 · Aula 2 ~25 min de leitura Nível: Intermediário

Vídeo da aula estará disponível em breve

Modelo de Ameaças

Antes de implementar defesas, precisamos entender o que estamos defendendo e contra quem. O modelo de ameaças (threat model) define os ativos, os atacantes e os vetores de ataque.

Modelo de ameaças de um SO
ATIVOS (o que proteger):
  ├── Dados do usuário (arquivos, senhas, chaves)
  ├── Integridade do kernel (não ser modificado)
  ├── Disponibilidade (sistema funcionando)
  └── Isolamento entre processos (um não acessa outro)

ATACANTES (contra quem):
  ├── Usuário local malicioso (escalar privilégios)
  ├── Malware/exploit remoto (buffer overflow via rede)
  ├── Processo comprometido (sandbox escape)
  └── Acesso físico (boot de USB, cold boot attack)

VETORES DE ATAQUE:
  ├── Buffer overflow (execução de código arbitrário)
  ├── Escalação de privilégios (user → root)
  ├── Race conditions em setuid programs
  ├── Engenharia social (phishing de senha root)
  └── Supply chain (dependência maliciosa)

Superfície de ataque do kernel Linux:
  ~400 system calls = 400 pontos de entrada potenciais
  Cada syscall é um vetor de ataque se tiver um bug

Autenticação

O primeiro passo da segurança é autenticação: verificar que o usuário é quem diz ser. Existem três fatores de autenticação, e combiná-los aumenta a segurança.

Fatores de autenticação
1. ALGO QUE VOCÊ SABE:
   Senha, PIN, passphrase
   → Armazenada como hash em /etc/shadow (Linux)
   → Vulnerável a: brute force, phishing, reuso de senhas

2. ALGO QUE VOCÊ TEM:
   Token físico (YubiKey), celular (TOTP), smart card
   → 2FA: senha + código do celular
   → Vulnerável a: roubo, SIM swap

3. ALGO QUE VOCÊ É:
   Biometria: impressão digital, face, íris
   → Conveniente mas irrevogável (não dá para trocar seu dedo)
   → Vulnerável a: falsificação, coerção

Linux: /etc/shadow
  marco:$6$salt$hash...:19000:0:99999:7:::
         │  │    │
         │  │    └── hash SHA-512 de salt+senha
         │  └── salt aleatório (contra rainbow tables)
         └── $6$ = SHA-512 (algoritmo de hash)

PAM (Pluggable Authentication Modules):
  Framework modular de autenticação do Linux.
  /etc/pam.d/ contém configuração por serviço.
  Permite: senha + 2FA + LDAP + biometria, etc.

Buffer Overflow

O buffer overflow é a vulnerabilidade mais importante da história de segurança de sistemas. Ocorre quando um programa escreve dados além dos limites de um buffer, sobrescrevendo dados adjacentes na memória — incluindo endereços de retorno na stack.

C — Buffer overflow na stack
#include <string.h>
#include <stdio.h>

void funcao_vulneravel(char* input) {
    char buffer[64];        // 64 bytes na stack
    strcpy(buffer, input);  // NÃO verifica tamanho!
    printf("Você digitou: %s\n", buffer);
}

int main(int argc, char** argv) {
    funcao_vulneravel(argv[1]);
    return 0;
}

/*
 Se input tem 70 bytes: sobrescreve dados após buffer
 Se input tem 80+ bytes: sobrescreve ENDEREÇO DE RETORNO

 Layout da stack durante funcao_vulneravel():
 ┌──────────────────────────────────────┐ endereço alto
 │  args de main (argc, argv)          │
 ├──────────────────────────────────────┤
 │  endereço de retorno de main        │
 ├──────────────────────────────────────┤
 │  frame pointer salvo (EBP)          │
 ├──────────────────────────────────────┤
 │  endereço de retorno de func_vuln   │ ← atacante sobrescreve isso!
 ├──────────────────────────────────────┤
 │  frame pointer salvo                │
 ├──────────────────────────────────────┤
 │  buffer[64]                         │ ← overflow começa aqui
 │  .................................   │   e cresce para CIMA
 └──────────────────────────────────────┘ endereço baixo

 Ataque clássico:
 1. Preencher buffer com shellcode (instruções de máquina)
 2. Sobrescrever endereço de retorno com endereço do buffer
 3. Quando função retorna: salta para o shellcode
 4. Shellcode executa: execve("/bin/sh") → shell com privilégios do processo
*/

Defesas contra Buffer Overflow

Stack Canaries
STACK CANARY (gcc -fstack-protector):
  Valor aleatório colocado entre buffer e endereço de retorno.
  Verificado antes do retorno da função.
  Se foi sobrescrito → overflow detectado → programa abortado.

  Stack COM canary:
  ┌────────────────────────────┐
  │ endereço de retorno        │
  ├────────────────────────────┤
  │ ★ CANARY (valor aleatório) │ ← verificado antes do ret
  ├────────────────────────────┤
  │ buffer[64]                 │ ← overflow sobrescreve canary
  └────────────────────────────┘

  Se canary mudou: *** stack smashing detected ***
  O atacante não sabe o valor do canary (aleatório por execução).

  Compilar com proteção:
    gcc -fstack-protector-strong programa.c  (default no Ubuntu)
    gcc -fstack-protector-all programa.c     (todos os funções)
ASLR e DEP/NX
ASLR (Address Space Layout Randomization):
  Randomiza posições de: stack, heap, bibliotecas, código.
  Atacante não sabe onde o buffer está na memória.
  Sem ASLR: buffer sempre em 0x7fffffffe000
  Com ASLR: buffer em endereço diferente a cada execução.

  $ cat /proc/sys/kernel/randomize_va_space
  2  (0=off, 1=parcial, 2=completo)

  $ ldd /bin/bash   # executar duas vezes: endereços diferentes
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x7f3a1b200000)
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x7f8e44c00000)

DEP / NX (No-Execute / W^X):
  Páginas de memória: ou executáveis, ou escrevíveis, NUNCA ambas.
  Stack marcada como não-executável: shellcode na stack não roda.

  Sem NX: atacante injeta código na stack e executa
  Com NX: CPU gera fault ao tentar executar código na stack

  Bypass: ROP (Return-Oriented Programming)
    Encadeia "gadgets" (fragmentos de código existente)
    Não injeta código novo, reutiliza código do programa/libc
    Mais difícil, mas possível → ASLR dificulta ainda mais

Defesa em profundidade (defense in depth):
  Stack canary + ASLR + NX + PIE + RELRO
  Todas habilitadas por default em distribuições modernas.
  Cada camada torna o ataque exponencialmente mais difícil.

Sandboxing e Isolamento

Mesmo com todas as defesas de memória, se um processo é comprometido, precisamos limitar o dano. Sandboxing confina processos em ambientes restritos.

Técnicas de sandboxing no Linux
1. chroot (change root):
   Muda o "/" visível para o processo.
   Processo vê /var/chroot como seu "/".
   Limitado: não isola PID, rede, IPC. Escape fácil como root.

2. Namespaces (base de containers Docker):
   Cada namespace isola um aspecto:
   ├── PID namespace:    processo vê só seus PIDs
   ├── Network namespace: rede isolada (IP próprio)
   ├── Mount namespace:   filesystem isolado
   ├── User namespace:    UID 0 dentro = unprivileged fora
   ├── IPC namespace:     shared memory isolada
   └── UTS namespace:     hostname próprio

3. cgroups (control groups):
   Limita RECURSOS: CPU, memória, I/O, PIDs.
   "Processo X pode usar no máximo 2 cores e 512MB de RAM"

4. seccomp (secure computing):
   Filtra SYSCALLS: lista branca de system calls permitidas.
   "Este processo só pode: read, write, exit. Nada mais."
   Usado por: Chrome (tabs), Docker, Firefox.

Container = namespaces + cgroups + seccomp + filesystem overlay
  Docker: empacota e isola aplicações usando esses mecanismos
  Não é VM: compartilha o kernel do host

SELinux e AppArmor

Permissões Unix tradicionais são DAC (Discretionary Access Control): o dono do arquivo controla as permissões. MAC (Mandatory Access Control) adiciona políticas do sistema que nem root pode violar.

DAC vs MAC
DAC (Unix padrão):
  marco pode chmod 777 seus arquivos → qualquer um acessa
  root pode fazer TUDO → se comprometido, game over
  Problema: decisões de segurança nas mãos do usuário

MAC (SELinux / AppArmor):
  Política do SISTEMA define o que cada processo pode fazer
  Mesmo root está sujeito à política
  "httpd pode ler /var/www e escutar porta 80. NADA mais."
  Root comprometido via httpd? Não pode ler /etc/shadow.

SELinux (Red Hat, Fedora, CentOS):
  - Rótulos em TUDO: arquivos, processos, portas
  - Política define: qual tipo de processo acessa qual tipo de arquivo
  - httpd_t pode ler httpd_sys_content_t
  - httpd_t NÃO pode ler shadow_t (mesmo como root!)
  - Complexo de configurar, mas muito granular

AppArmor (Ubuntu, SUSE):
  - Perfis por programa (/etc/apparmor.d/)
  - "Firefox pode ler ~/Downloads, /tmp, ~/.mozilla"
  - "Firefox NÃO pode ler ~/.ssh, /etc/shadow"
  - Mais simples que SELinux (path-based vs label-based)

  $ sudo aa-status   # ver perfis ativos
  25 profiles are in enforce mode.
     /usr/sbin/cupsd
     /usr/bin/firefox
     ...

Conceitos de Criptografia no SO

Criptografia usada pelo SO
1. HASH (integridade):
   SHA-256, SHA-512: transformam dados em fingerprint fixo
   Uso: /etc/shadow (hash de senhas), verificação de pacotes
   Propriedade: irreversível (não dá para obter a senha do hash)

2. CRIPTOGRAFIA SIMÉTRICA (confidencialidade):
   AES-256: mesma chave para cifrar e decifrar
   Uso: LUKS (disk encryption), dm-crypt
   $ sudo cryptsetup luksFormat /dev/sda2  # cifrar partição

3. CRIPTOGRAFIA ASSIMÉTRICA (autenticação):
   RSA, Ed25519: chave pública + chave privada
   Uso: SSH (login sem senha), assinatura de pacotes
   $ ssh-keygen -t ed25519  # gerar par de chaves

4. GERAÇÃO DE ALEATORIEDADE:
   /dev/random:  entropia do hardware (bloqueia se acabar)
   /dev/urandom: PRNG semeado por entropia (não bloqueia)
   Kernel coleta entropia de: timing de interrupções, disco,
   teclado, mouse, RDRAND (instrução Intel)

Disk encryption (LUKS/dm-crypt):
  Dados no disco cifrados com AES-256.
  Chave protegida por senha do usuário (na inicialização).
  Se disco for roubado: dados ilegíveis sem a senha.
  Transparente: filesystem monta normalmente após desbloqueio.

No harness.os

Os conceitos de segurança do SO aplicam-se diretamente a como o harness.os protege seus dados e operações:

Diagrama — Segurança no harness.os
Defesas do SO                    Paralelo no harness.os
══════════════                   ═══════════════════════

Autenticação (login)             Neon API key + MCP auth
  Quem é este usuário?             Quem é este agente?

Stack canary (detecção)          Validação de inputs
  Detecta overflow                 Verifica tool parameters

ASLR (imprevisibilidade)         Token rotation, session IDs
  Atacante não sabe endereços      IDs não previsíveis

Sandboxing (isolamento)          Agent scoping
  Processo vê só seu namespace     Agente vê só seu project
  seccomp filtra syscalls          MCP filtra tools disponíveis

MAC (SELinux/AppArmor)           Row-Level Security (Postgres)
  Política overrides permissões    Policy overrides queries
  Nem root viola a política        Nem admin vê dados de outro tenant

Criptografia (LUKS/SSH)          TLS + encrypted connections
  Dados cifrados em repouso        Neon: dados cifrados at rest
  SSH para acesso remoto            TLS para todas as conexões

Princípio compartilhado:
  Defense in depth — múltiplas camadas de proteção.
  Se uma falha, outras ainda protegem.

Exercícios

  1. Compile o programa vulnerável a buffer overflow (sem proteções): gcc -fno-stack-protector -z execstack -no-pie -o vuln vuln.c. Execute com entrada de 60, 70 e 80 caracteres. O que acontece? Agora compile com proteções padrão e repita. Qual é a diferença?
  2. Verifique o estado das defesas no seu sistema: cat /proc/sys/kernel/randomize_va_space (ASLR), dmesg | grep NX (NX/DEP). Qual é a configuração padrão? Explique o que cada defesa protege.
  3. Execute sudo aa-status (ou sestatus no Fedora). Quantos perfis estão em modo enforce? Escolha um perfil e analise: que arquivos e capacidades o processo está limitado a usar?

Resumo

Verifique seu entendimento

Qual técnica impede que um atacante execute shellcode injetado na stack?

  • ASLR (randomização do layout de endereços)
  • DEP/NX (bit de não-execução nas páginas de memória)
  • Stack canary (valor sentinela antes do endereço de retorno)
  • SELinux (controle de acesso mandatório)