Scanners de IA Estão Cobrando Nossa Dívida Técnica
Scanners de segurança com IA transformam bugs antigos em exploits críticos. Lições para priorizar o backlog de vulnerabilidades sem se afogar.

Um relatório de bug chegou ao rastreador do projeto PHP em 2024. Ele descrevia uma condição de falta de memória — do tipo que faz um processo cair, o que é irritante mas, na hierarquia informal dos bugs, basicamente um dar de ombros. Ficou parado ali. Depois, durante uma auditoria independente do PHP patrocinada pela OpenAI, um pesquisador da empresa de segurança Calif — usando scanners de segurança com IA e sua própria expertise profunda — passou cerca de duas semanas transformando aquele crash incômodo em execução remota de código. Qualquer servidor rodando uma build vulnerável do PHP poderia ser coagido a executar código do atacante ou a vazar dados sensíveis. Os sites no raio de impacto incluíam NASA, Wikipedia e a Casa Branca, porque WordPress e Drupal rodam em PHP, e o PHP roda em uma fração surpreendente da web.
O patch foi lançado antes que alguém mal-intencionado chegasse lá, então a história terminou bem. Mas os pesquisadores envolvidos continuam repetindo a mesma coisa: isto é o começo, não o fim. Quatro décadas de código escrito por humanos contêm um oceano de bugs que coletivamente classificamos como 'crashes, não corrigir', e as ferramentas de IA derrubaram o custo de descobrir quais desses crashes são, na verdade, armas carregadas. Passei as últimas semanas conversando com pessoas que cuidam da segurança de sistemas reais sobre o que isso muda na prática. O que segue não é uma pesquisa — é um conjunto de erros que já vi times cometerem e o que as pessoas que acertaram fizeram no lugar.
Erro Um: Classificar Bugs de Crash como 'Chato, mas Inofensivo'
O bug do PHP no centro desta história não estava escondido. Foi reportado, reconhecido e despriorizado — porque 'só' causava um erro de falta de memória. Esta é a falha de triagem mais comum em organizações de software maduras, e o desenvolvimento de exploits assistido por IA acabou de torná-la cara. A professora da Columbia Suman Jana, que trabalha exatamente nessa classe de pesquisa, descreveu a dinâmica sem rodeios em entrevistas sobre a divulgação: a qualquer momento, todo projeto grande tem uma pilha de relatórios de crash que ninguém leva a sério, porque transformar um crash em exploit exige longas cadeias de raciocínio tedioso e hiperespecífico. Humanos evitam esse trabalho. Modelos não se entediam.
Aqui está a versão mecânica de por que a distinção importa. Um bug de memória que corrompe o heap com dados não controlados normalmente só mata o processo — o alocador percebe algo inconsistente e aborta, ou o processo desreferencia lixo e sofre um segfault. Mas o mesmo bug, alimentado com uma entrada cuidadosamente dimensionada e moldada, se torna uma primitiva de escrita:
void handle_request(const char *input, size_t len) {
char *buf = malloc(64);
memcpy(buf, input, len); /* len never validated */
/* len = 65: heap metadata trashed -> likely abort, filed as 'crash' */
/* len = attacker-tuned: adjacent object's function pointer overwritten */
process(buf); /* calls through the corrupted pointer -> their code runs */
free(buf);
}
A linha entre esses dois resultados não está no bug — está na entrada. Descobrir quais bits sobrescrever, em que ordem, para levar a execução a um endereço escolhido é trabalhoso, mas mecânico, e 'trabalhoso mas mecânico' é exatamente o centro daquilo em que modelos de linguagem são bons em ajudar. A correção para este erro é conceitual antes de ser técnica: pare de usar 'só dá crash' como sinal de severidade para código com segurança de memória. Um crash em C ou C++ (ou no núcleo C de um interpretador como o PHP) é um exploit que ainda não foi desenvolvido. A nova pergunta de triagem não é 'isso dá crash?', mas 'a corrupção é influenciada pelo atacante?'. Se uma entrada não confiável controla o tamanho, o offset ou o conteúdo de uma escrita ruim, presuma que é explorável e priorize de acordo — mesmo quando ninguém demonstrou um exploit funcional. Especialmente nesses casos, porque exploits demonstrados hoje levam semanas, não anos.
Erro Dois: Tratar a Saída do Scanner Como um Backlog
O segundo modo de falha aparece no momento em que um time realmente roda scanners modernos com IA em um conjunto de sistemas que inclui código com vinte anos. A ferramenta devolve centenas de achados, o painel fica vermelho, e o time faz o que os backlogs ensinam: ordena pelo score de severidade e começa do topo. Seis meses depois, corrigiram quarenta coisas, o número subiu de novo e o moral está destruído. Os engenheiros com quem conversei que lidam bem com isso disseram, de uma forma ou de outra, a mesma coisa: a saída de um scanner de IA não é um backlog, é inteligência bruta, e precisa de uma passagem de triagem por um analista antes que um único ticket seja aberto.
Uma triagem eficaz nesse cenário faz perguntas sobre o caminho de exploração, não perguntas sobre score de severidade:
- O código vulnerável é alcançável a partir de entrada não confiável? Um heap overflow em uma função que só processa um arquivo de configuração local é um bicho completamente diferente do mesmo overflow no tratamento de requisições.
- Ele está exposto à internet hoje? Não 'deveria estar' — está. Verifique as configurações do load balancer e o DNS, não o diagrama de arquitetura.
- A entrada controla a corrupção? Tamanho, offset ou conteúdo do acesso ruim. Esta é a linha entre crash e exploit da seção anterior.
- O que um exploit conquista? Um crash em um worker isolado que é reiniciado é diferente de um crash no processo que guarda as credenciais do banco de dados.
- Existe exploit circulando na natureza ou um write-up público? Isso leva qualquer item para o topo da fila, independentemente do score.
Esse último ponto merece ênfase. Scores como o CVSS foram criados para um mundo em que humanos encontravam bugs em ritmo humano, e eles sistematicamente superestimam a severidade teórica em relação à alcançabilidade. Quando um scanner consegue gerar severidade teórica em escala industrial, você se afoga. As equipes que estão lidando melhor inverteram o pipeline: alcançabilidade e exposição primeiro, severidade depois, e tudo que for inalcançável por construção é documentado e fechado em vez de corrigido — porque um fix que você entrega carrega seu próprio risco de regressão, e você vai precisar desse orçamento de risco para os achados que importam. É também aqui que as verificações de compreensão de código com IA se pagam no lado defensivo: a mesma ferramenta que encontra o bug pode muitas vezes responder 'mostre todos os caminhos de chamada desde a fronteira de rede até esta função' em minutos, uma pergunta que antes levava um engenheiro sênior um dia por achado.

Erro Três: Presumir que o Patch Pode Andar na Velocidade da Descoberta
Aqui está uma assimetria que deveria estar impressa em um pôster em cada sala de time de segurança. Encontrar o caminho de exploração do bug do PHP levou um pesquisador habilidoso com ajuda de IA cerca de duas semanas. Desenvolver o patch levou quatro engenheiros dois meses. Encontrar é um problema de busca: você precisa de um único caminho. Corrigir é um problema de forma de prova: você precisa fechar todos os caminhos, e precisa que os bilhões de instalações que não estão sendo atacadas continuem funcionando exatamente como antes. O trabalho do defensor é estritamente mais difícil que o do atacante, e a IA comprime o lado do atacante dessa lacuna muito mais rápido do que o lado do defensor.
Times que ignoram essa assimetria fazem uma de duas apostas ruins. Ou correm para lançar patches no ritmo da pressão da divulgação e enviam regressões — o patch do PHP tocou em partes internas do interpretador usadas por cerca de três quartos dos sites que publicam sua stack, então o raio de impacto de uma correção ruim é civilizacional — ou desanimam e não fazem nada enquanto esperam uma correção perfeita. Os profissionais que lidaram bem com essa janela fizeram algo menos heroico e mais eficaz: separaram 'mitigar agora' de 'corrigir direito'. Mitigações são baratas, rápidas e reversíveis — restringir o endpoint afetado no reverse proxy, criar uma regra de WAF que bloqueie o formato de entrada do exploit, desativar o caminho de código vulnerável atrás de uma feature flag, reduzir quem consegue alcançar o serviço enquanto o patch definitivo amadurece. Já escrevi antes sobre como a decisão entre patch e mitigação é um trade-off genuíno, e não uma falha moral, e este incidente é essa lição em escala planetária.
O corolário que ninguém quer ouvir: seu tempo médio para mitigar é a métrica que importa agora, porque a janela entre 'bug encontrado por white hats' e 'bug encontrado por alguém pior' está encolhendo. No caso do PHP, a janela ficou aberta apenas porque os pesquisadores foram responsáveis e a fundação agiu rápido. A biblioteca de compressão Brotli contou uma história menos lisonjeira no mesmo período — um bug comparável ficou no código por oito anos, foi corrigido no git mas não lançado por oito meses, e foi redescoberto independentemente por pelo menos quatro pesquisadores. Quando a descoberta é barata e paralela, 'alguém vai encontrar eventualmente' deixa de ser um pensamento confortável e se torna uma garantia de agendamento.
Erro Quatro: Confiar na Correção Porque um LLM a Escreveu
O contra-argumento otimista para tudo isso diz: tudo bem, a IA encontra bugs mais rápido do que conseguimos corrigi-los hoje, mas a IA também vai corrigi-los. A Anthropic lançou em outubro ferramentas exatamente nessa linha — um LLM gera um patch, depois um segundo modelo gera uma prova verificável por máquina de que o patch preserva o comportamento pretendido, e um verificador de provas pequeno, burro e confiável valida a prova. O verificador é o único componente que precisa estar correto, e pode estar, porque faz quase nada. Jana está genuinamente entusiasmada com essa direção, e entendo por quê: é o primeiro caminho crível para tornar a correção quase tão automatizável quanto a descoberta.
Mas já vi dois times adotarem fluxos de 'patch de segurança gerado por IA' e ambos cometeram o mesmo erro: trataram o patch do modelo como uma solução em vez de um candidato. Em um caso, o patch fechou o heap overflow reportado adicionando uma verificação de tamanho — e empurrou o overflow para um segundo caminho de alocação seis linhas adiante, que o modelo não havia modelado. O fix passou na revisão porque parecia obviamente correto, e essa é exatamente a falha: patches de LLM são fluentes de um jeito que patches humanos não são, e a fluência suprime o escrutínio. Os times que tiraram valor de patches gerados acrescentaram três portões, nesta ordem:
- Uma suíte de regressão que inclua a entrada do exploit. A prova de conceito que disparou o achado vira um teste permanente. Se o scanner deu uma entrada que causa crash, esse é seu primeiro novo caso de teste, sem custo extra.
- Uma caça por variantes. Antes do merge, alguém (ou alguma coisa) responde: onde mais esse mesmo padrão ocorre? Corrigir uma instância de um padrão sistêmico é como surgem as sequelas de CVE.
- Verificação independente do invariante, não do diff. Declare o que precisa ser verdade agora — 'nenhum caminho a partir do tratamento de requisições alcança memcpy com um tamanho não validado' — e verifique isso, com testes, análise estática ou ferramentas formais. Revisar só o diff é apenas reler a lição lançada pelo modelo.
A verificação formal vai ajudar aqui conforme amadurece, mas repare na divisão de trabalho que a torna confiável: as partes criativas podem alucinar, porque o árbitro final é mecânico e pequeno. Esse é o princípio de design a copiar, independentemente das suas ferramentas. Tudo o que um LLM produz — achado, patch ou prova — deve terminar em algo que não compartilhe seus modos de falha. Se sua etapa de verificação for outro prompt, você não verificou nada.

Erro Cinco: Presumir que Outra Pessoa Auditou o Código
Por baixo de cada lição técnica aqui existe uma lição econômica. O PHP é infraestrutura na escala de um serviço público — alimenta WordPress, Drupal e uma longa cauda da web que inclui agências espaciais e sites de governos — e sua segurança depende da PHP Foundation, um pequeno órgão financiado, mais voluntários. O mito confortável do open source sempre foi 'com olhos suficientes, todos os bugs são rasos'. O que de fato aconteceu durante trinta anos é que todo mundo presumiu que os olhos pertenciam a outra pessoa. A IA agora fornece os olhos, indiscriminadamente, para atacantes e defensores — o que significa que o mito finalmente está se tornando verdade, só que de forma adversarial e com prazo.
A divulgação do PHP2Shell funcionou por causa de um arranjo incomum: a OpenAI patrocinou uma auditoria externa, uma empresa bem financiada fez o trabalho, e a Wiz ajudou a confirmar e expandir os achados. Isso é caridade, não um sistema, e não escala para os milhares de projetos críticos e sem financiamento na sua árvore de dependências. Se a produção da sua organização depende de um interpretador, framework ou biblioteca open source — e depende — as correções que importam são as chatas: financie as fundações, patrocine auditorias, contribua com horas de engenharia e, quando rodar seus próprios scanners e encontrar algo em uma dependência, reporte upstream em vez de remendar seu fork e seguir em frente. A alternativa é o mundo previsto por Matthew Green, da Johns Hopkins: nada muda estruturalmente até que um incidente grande o bastante para incomodar pessoas comuns force a mudança. O conselho dele enquanto isso é pouco glamouroso e correto — quando o aviso de atualização aparecer, instale agora, não na semana que vem. Aprendemos a mesma lição da forma difícil no último grande ciclo de divulgações do PHP, quando desligar a tomada venceu as correções engenhosas para os times que não conseguiram aplicar patches rápido o bastante.
Os olhos chegaram. Só não são exigentes quanto ao lado em que estão.
O Playbook de Segunda-feira para Vulnerabilidades Reveladas por IA
Tudo o que foi dito acima se resume a uma lista curta das coisas que eu realmente faria neste trimestre se comandasse uma organização de engenharia com algum código legado em produção — e se você está lendo isto, tem código legado em produção:
- Aplique patches agressivamente e meça quanto tempo leva. Sua latência de atualização agora é um controle de segurança com um limiar seguro cada vez menor. Conheça seu número antes de precisar dele.
- Refaça a triagem do seu backlog atual de bugs de crash com a pergunta de explorabilidade, não o score de severidade: a entrada não confiável controla a corrupção? Tudo que for inseguro para memória e alcançável sobe de prioridade.
- Rode você mesmo a varredura assistida por IA, começando pelo seu código mais antigo. O custo marginal de redescobrir agora é quase zero, o que significa que você deve presumir que qualquer coisa encontrável na sua superfície de ataque será encontrada. Encontre primeiro.
- Separe mitigação de correção no seu processo de incidentes. Monte o playbook com antecedência: regras de proxy, feature flags, restrição de acesso. Ganhar tempo é uma estratégia legítima; fingir que você não precisa de tempo não é.
- Condicione patches gerados a testes com a entrada do exploit e a caças por variantes, nunca à limpeza visual do diff.
- Invista dinheiro ou horas nos projetos upstream dos quais você depende. Auditorias patrocinadas encontraram este. Isso não é coincidência; é o modelo funcionando, e precisa deixar de ser excepcional.
Há um debate justo sobre se esta era é 'pagar dívida técnica' ou nos colocar numa esteira sem fim de trabalho de correção que não entrega funcionalidade para sempre. Provavelmente as duas coisas: cada patch não trivial carrega o risco do próximo bug sutil, e nenhum sistema fica pronto de vez. Mas prefiro viver no mundo em que a dívida está sendo cobrada por auditores e white hats do que naquele em que fingimos que a areia movediça é rocha firme só porque nada nos engoliu ainda. O PHP2Shell terminou com um patch e um suspiro de alívio. O próximo já está na fila de crashes de alguém, catalogado como inofensivo. Vá olhar o seu.


