◇ OpenCyber Academy
Engenharia · Capítulo 28 · Português

Capítulo 28 — Segurança ofensiva autorizada e red team: implementação I

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

Capítulo 28 — Segurança ofensiva autorizada e red team: implementação I

Mapa da aula

Aula 28 publicada. Mapa, exemplo, EX082.

flowchart TD
  N0["28.1 Forma da implementação"]
  N1["28.2 Testes por cláusula"]
  N2["28.3 Registro em log"]
  N3["28.2b Catálogo de códigos de motivo"]
  N4["28.3b Fuzzing das entradas"]
  N5["28.1b Horários baseados em propriedades"]
  N6["Nota de prática"]
  N7["Síntese"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
  N7 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX082"]

Desenho lógico

flowchart LR
  N0["28.1 Forma da implementação"]
  N1["28.2 Testes por cláusula"]
  N2["28.3 Registro em log"]
  N3["28.2b Catálogo de códigos de motivo"]
  N4["28.3b Fuzzing das entradas"]
  N5["28.1b Horários baseados em propriedades"]
  N6["Nota de prática"]
  N7["Síntese"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX082"]
[28.1 Forma da implementação]
    |
    v
[28.2 Testes por cláusula]
    |
    v
[28.3 Registro em log]
    |
    v
[28.2b Catálogo de códigos de motivo]
    |
    v
[28.3b Fuzzing das entradas]
    |
    v
[28.1b Horários baseados em propriedades]
    |
    v
[Nota de prática]
    |
    v
[Síntese]
    |
    v
[Exemplo resolvido] --> [Exercício EX082]

Objetivos de aprendizagem

Você vai (1) implementar allowed() com códigos de motivo explícitos; (2) acrescentar testes para cada cláusula; (3) registrar logs sem guardar segredos.

28.1 Forma da implementação

Função pura: entradas host, porta, ScopeVersion → (allow: bool, reason: str). Nenhuma E/S de rede dentro do predicado; a E/S pertence a quem chama. Isso mantém os testes rápidos e livres de contato com o cliente. Manter a E/S fora do predicado torna os testes sintéticos completos. Quem chama cuida da rede e dos arquivos; a função continua sendo uma decisão pura. Sam mede o desempenho do predicado para mantê-lo abaixo de um milissegundo em rajadas de negações. Os testes de rajada enviam dez mil negações sintéticas e falham se a latência p95 ultrapassar o orçamento de um milissegundo.

28.2 Testes por cláusula

Um arquivo de teste por cláusula: cidr, lista de exclusão, porta, horário, concorrência, indicador de aprovação. Só fixtures sintéticos. Mutação: quebre uma cláusula e veja o teste falhar.

"""Check proposed engagement targets against a written scope (synthetic)."""
SCOPE_CIDRS = [("10.20.0.0", 16), ("192.168.50.0", 24)] # inclusive synthetic ranges
SCOPE_PORTS = {22, 80, 443, 8080}
DENY_HOSTS = {"10.20.0.1", "192.168.50.1"} # out-of-scope infra

def ip_int(s):
a,b,c,d = map(int, s.split("."))
return (a<<24)|(b<<16)|(c<<8)|d

def in_cidr(ip, net, bits):
mask = (0xFFFFFFFF << (32-bits)) & 0xFFFFFFFF
return (ip_int(ip) & mask) == (ip_int(net) & mask)

def check(host, port):
reasons = []
if host in DENY_HOSTS:
reasons.append("deny-list")
if not any(in_cidr(host, n, b) for n,b in SCOPE_CIDRS):
reasons.append("outside-cidr")
if port not in SCOPE_PORTS:
reasons.append("port-not-in-scope")
return ("ALLOW" if not reasons else "DENY"), reasons

tests = [
("10.20.1.10", 443),
("10.20.0.1", 443),
("192.168.50.20", 22),
("192.168.50.20", 3389),
("8.8.8.8", 80),
("192.168.1.10", 80),
]
for h,p in tests:
v, r = check(h,p)
print(f"target={h}:{p} decision={v} reasons={','.join(r) or '-'}")

Saída esperada:

target=10.20.1.10:443 decision=ALLOW reasons=-
target=10.20.0.1:443 decision=DENY reasons=deny-list
target=192.168.50.20:22 decision=ALLOW reasons=-
target=192.168.50.20:3389 decision=DENY reasons=port-not-in-scope
target=8.8.8.8:80 decision=DENY reasons=outside-cidr
target=192.168.1.10:80 decision=DENY reasons=outside-cidr

Arquivos por cláusula mostram qual regra regrediu. Fixtures sintéticos mantêm as redes dos clientes de fora. A revisão de Sam verifica se cada cláusula da ROE tem um arquivo. Cláusulas não implementadas bloqueiam os trens de release mesmo quando uma demonstração manual passou num build de notebook. Demonstrações em notebook podem ser anexadas como extras, mas nunca liberam um trem sozinhas. Cada arquivo de cláusula nomeia o id da cláusula da ROE que testa, para que um teste que falha aponte direto para o texto assinado.

28.3 Registro em log

Registre a decisão, o motivo, a versão da política e quem agiu. Não registre tokens nem senhas. Oculte os parâmetros de consulta que possam conter segredos. A versão da política e quem agiu tornam um DENY explicável meses depois. Tokens em logs são incidentes. A ocultação dos parâmetros de consulta é testada com um fixture que tenta passar uma senha às escondidas. Sam faz os builds falharem quando imprimem cabeçalhos Authorization. Os fixtures de segredo às escondidas rodam a cada commit, não só nos candidatos a release, para que regressões de ocultação falhem rápido.

Caso sintético — Riverstone Transit. A Implementação I entregou códigos de motivo que a interface de linha do tempo mostra — os operadores finalmente viram por que uma sondagem parou.

28.4 Erros e limites

Motivos tipados como strings derivam; use um enum compartilhado com o PulseMap. Códigos de motivo que inventam strings novas toda semana quebram painéis e treinamentos. Um catálogo com ids estáveis mantém os operadores fluentes. Sam rejeita pull requests que acrescentem um motivo sem uma linha no catálogo e um teste unitário, e arquiva os códigos aposentados em vez de reciclar seus ids, para que os logs antigos continuem legíveis. Os códigos arquivados permanecem numa tabela somente-leitura consultada pela interface de logs quando os analistas abrem engajamentos antigos. O acesso somente-leitura ao arquivo é concedido aos analistas apenas pelo período de retenção do engajamento.

28.2b Catálogo de códigos de motivo

Publique o enum de códigos de motivo como um documento versionado, compartilhado pelo PulseMap e pelos operadores: outside-cidr, deny-list, port-not-in-scope, outside-hours, concurrency, needs-approval, policy-unavailable. Códigos desconhecidos precisam falhar fechados nos consumidores, não aparecer como "OK". Códigos estáveis permitem que o PulseMap e os documentos de treinamento falem uma só língua. O texto para pessoas pode mudar; os ids, não. Sam versiona o catálogo e exige uma nota de migração quando os significados mudam. Os notebooks anuais de métricas fixam a versão do catálogo, para que uma mudança de significado não remodele as tendências em silêncio. Notebooks que omitem a fixação reprovam na revisão de métricas, da mesma forma que versões de ferramentas não fixadas reprovam no kickoff.

28.3b Fuzzing das entradas

Alimente o predicado com hosts, portas e UTF-8 quebrado aleatórios. Ele precisa negar (DENY) ou dar erro fechado, nunca lançar uma exceção não tratada e nunca permitir (ALLOW) numa falha de interpretação. Um harness de fuzzing de cinco minutos na CI pega falhas constrangedoras antes que um engajamento as pegue. Hosts e portas sem sentido precisam ser negados de forma fechada, não derrubar o sistema aberto. Fazer fuzzing do predicado pega pânicos que falhariam abertos num wrapper. Sam roda um job noturno de fuzzing em allowed(), com uma etapa que trata falhas como reprovação. As notas de release listam as horas de fuzzing e a contagem de falhas ao lado dos destaques de funcionalidades, para dar visibilidade.

28.1b Horários baseados em propriedades

Para a etapa de horário, gere carimbos de tempo aleatórios ao longo de uma semana e afirme DENY fora da janela da ROE e ALLOW dentro dela (com as outras cláusulas satisfeitas). Testes por exemplo deixam passar bordas de fuso horário que os testes de propriedade pegam. As cláusulas de horário falham nas fronteiras: 08:59 contra 09:00. Testes de propriedade em vários fusos horários pegam lacunas que os testes por exemplo deixam passar. Sam inclui um caso logo dentro e um logo fora da janela em todo arquivo de horário. Os tickets de mudança de horário colam a tabela de fronteiras da suíte, para que os revisores vejam o que será afirmado.

Nota de prática

As listas de verificação de revisão de código para mudanças no predicado incluem: cláusula nova? testes novos? documentação dos códigos de motivo? caso de fuzzing? falha fechada em erro de interpretação? Revisores que pulam a lista estão fazendo revisão social, não revisão de segurança. O caminho de negação do TrackScope é o lugar errado para uma cultura informal de LGTM; a lista é curta de propósito, para que de fato seja usada sob pressão de tempo.

Exercícios

Síntese

Predicado puro, testes por cláusula, logs seguros.

Continue estudando — capítulo 28 de 50

← Capítulo 27 (rascunho) · Índice de capítulos do curso · Capítulo 29 (rascunho) →