Duas RCE críticas no Next.js e o Gitea sob exploração

Neri Filho

Semana passada eu escrevi que o Next.js tinha marcado 26 de agosto para publicar uma falha crítica, e que a parte boa da história era o aviso prévio. A data chegou um dia antes e com o dobro do problema. Foram duas falhas críticas, não uma, e a equipe antecipou a publicação porque achou a segunda no meio do caminho. Serviu exatamente para o que o aviso existe: quem gastou os dias levantando em que versão cada aplicação estava de verdade atualizou na terça. Quem não gastou passou a terça descobrindo que tem um painel interno rodando 15.4 desde o ano passado.

O segundo fio é o mesmo de sete dias atrás, com outro nome no lugar. Era o GitLab, agora é o Gitea: servidor de repositório que time pequeno sobe uma vez e esquece, com uma falha de 9,8 que roda comando com a conta de serviço. A diferença é que aqui não é mais hipótese. Tem relato de instância comprometida, prazo federal já vencido e a correção disponível desde julho.

E teve um amontoado de release entre segunda e quarta: Chrome 152, Node 26.8, Kubernetes 1.37 e o próprio Next.js, tudo em quarenta e oito horas. Oito itens. Três desenvolvidos.

Destaques

Next.js: duas RCE críticas, e a faixa afetada começa na 10.0.0

Em 25 de agosto saíram a 16.3.3 e a 15.5.24, um dia antes do combinado. A antecipação foi anunciada na manhã do mesmo dia: apareceu uma segunda falha crítica numa dependência de terceiro e eles preferiram juntar as duas numa correção só, para ninguém precisar atualizar duas vezes.

A primeira é a GHSA-2xp9-vwfh-vxw4, CVSS v4 de 9,5, e é onde mora o problema de verdade. A falha é do libheif, que o sharp usa, e o sharp é quem o Next.js usa para otimizar imagem. Otimizar um AVIF controlado pelo atacante dá execução remota de código sem autenticação nenhuma. A faixa afetada é da 10.0.0 até a 15.5.23, mais tudo antes da 16.3.3, ou seja: praticamente qualquer aplicação Next.js que faça otimização de imagem nos últimos cinco anos. A correção desliga a otimização de AVIF enquanto o upstream não sai. Vale o incômodo: você troca alguns por cento de compressão por não executar decodificador de imagem em cima de arquivo que veio de fora.

A segunda é a CVE-2026-75604, também crítica, também execução remota sem autenticação, e essa é mais estreita: só pega aplicação que usa Pages Router e App Router ao mesmo tempo, sem Cache Components, com o servidor em sistema de arquivos Windows. Linux e macOS não são afetados. Não existe contorno, só atualizar.

O que muda na prática depende de onde a aplicação roda, e essa parte a Vercel deixou explícita no changelog: quem está hospedado lá já está protegido, sem upgrade, sem redeploy, sem mexer em configuração. Eles desligaram AVIF no serviço de otimização de imagem deles e a infra é Linux. Quem faz self-host carrega as duas. E aqui vai a parte que eu acho mais fácil de errar: a faixa da falha de AVIF é tão larga que "a gente está atualizado" não responde nada. Vá ver a versão instalada, uma por uma, incluindo o container que ninguém reconstrói. Se a aplicação usa next/image apontando para domínio remoto que o usuário influencia, ela está na pior configuração possível para essa falha.

Gitea: 9,8 sendo explorado, com correção disponível desde julho

Em 25 de agosto a CISA colocou a CVE-2026-60004 no catálogo de exploração ativa, com prazo federal para o dia 28. A descrição no registro do CVE é curta e desagradável: Gitea antes da 1.27.1 permite execução remota de código pela API de diffpatch, através da instalação de git hook. CVSS 3.1 de 9,8, vetor de rede, complexidade baixa, sem privilégio e sem interação. A faixa afetada começa na 1.17.

O detalhe que importa é o caminho. A falha pede acesso de escrita a um repositório, o que soa como uma barreira até você lembrar que existe registro aberto. Onde ele está ligado, o atacante cria a conta, cria o repositório dele e ganha a escrita de graça. O relato que a Help Net Security recolheu é de uma instância self-hosted comprometida por um scanner automatizado, que registrou conta, criou repositório e subiu minerador. O comando roda com a conta de serviço do Gitea, que é justamente a conta que enxerga todos os repositórios da máquina.

O que muda na prática é a conta de tempo, e ela é constrangedora. A 1.27.1 saiu em 27 de julho, o advisory no dia seguinte, e a exploração automatizada apareceu um mês depois. Um mês é bastante para um projeto que se atualiza com uma troca de tag de imagem. Se você tem Gitea em algum lugar, a versão de linha hoje é a 1.27.3, de 29 de agosto, e a atualização é o menor dos trabalhos. Os outros dois são: confira se o registro aberto está ligado e se ele precisa estar, e, se a instância ficou exposta, olhe os git hooks dos repositórios antes de dar por encerrado. Correção fecha a porta, não desfaz o que entrou por ela.

Kubernetes 1.37: autoscaler chegando a zero, e um detalhe de SELinux que quebra pod

O Kubernetes v1.37, apelidado de Garhwal, saiu em 26 de agosto com 67 melhorias: 16 promovidas a estável, 23 a beta, 27 entrando em alfa e uma remoção. Três coisas dali mudam decisão.

A primeira é o HorizontalPodAutoscaler escalando até zero, que virou beta e vem ligado por padrão. Com spec.minReplicas: 0, workload que usa métrica de objeto ou externa some quando não tem trabalho e volta quando a métrica sobe. Não vale para CPU e memória, porque essas métricas dependem de pod vivo, e é justamente por isso que a mira do recurso é consumidor de fila, job em lote e carga com GPU. Enquanto segura em zero, o HPA registra a condição ScaledToZero, que é o que distingue "eu escalei para zero" de "alguém desligou isso na mão". Para quem paga GPU ociosa, esse é o item mais caro da lista, no bom sentido.

A segunda é a que eu leria antes de subir a versão. SELinuxMount e SELinuxChangePolicy viraram estáveis e ligados por padrão: volume passa a ser montado com -o context=<label> em vez de ser rotulado recursivamente, desde que o driver CSI opte por isso. A consequência está no próprio anúncio, com todas as letras: pods com rótulos SELinux diferentes que compartilham um volume no mesmo nó, e que conviviam sob a rotulagem recursiva, agora podem falhar ao iniciar. Dá para voltar ao comportamento antigo pondo .spec.seLinuxChangePolicy em Recursive no pod, e dá para desligar no cluster inteiro por mais uma versão, porque o travamento fica para a 1.38. Cluster sem SELinux não sente nada.

A terceira é chata e vale para quem escreve controller. A inicialização resiliente do watch cache fechou em estável, e o efeito é que o kube-apiserver agora rejeita requisição cara com HTTP 429 em vez de deixar a fila crescer contra o etcd durante a partida e a recuperação. Isso protege o control plane em cluster grande, e transfere trabalho para o cliente: seu operador precisa tratar 429 respeitando o Retry-After, com backoff exponencial. Código que trata 429 como erro genérico e sai repetindo vai ter comportamento pior depois do upgrade, não melhor. Na mesma leva, o KYAML e a API metrics.k8s.io chegaram a estável, essa última depois de quase nove anos em beta.

Em nota

  • Node 26.8.0 trouxe zip na biblioteca padrão. A versão saiu em 26 de agosto na linha Current e entraram as classes ZipEntry, ZipFile e ZipBuffer no node:zlib, mais StatementSync.prototype.close() e o Symbol.dispose do SQLite embutido, modos SIV e GCM-SIV no Cipher, e um MIMEType.parse que não lança. Zip nativo tira uma dependência de árvore de projeto que só descompactava arquivo, e o SQLite com Symbol.dispose fecha statement por using. É linha Current, então trate como material de experimento, não de produção. No mesmo dia saiu a 26.8.1, fora de banda, só para consertar o node --version que estava reportando versão alfa.
  • O Chrome 152 ganhou uma allowlist de conexão declarativa. A versão começou a sair em 25 de agosto com o cabeçalho Connection-Allowlist, que restringe a quais endpoints externos a página pode se conectar. É CSP com outro nome e outro escopo, e é o tipo de coisa que ajuda contra script de terceiro que resolve conversar com domínio novo. Vieram junto uma API de performance de CPU, para a página se adaptar à capacidade do aparelho, e CSSPseudoElement para ::backdrop, ::scroll-marker e ::view-transition.
  • A Assistants API da OpenAI foi desligada. O changelog registra o desligamento em 26 de agosto, com migração para Responses e Conversations. Na mesma entrada entrou o fim de vida de whisper-1, gpt-4o-transcribe, gpt-4o-mini-transcribe e gpt-4o-transcribe-diarize, com desligamento marcado para 26 de fevereiro de 2027. O primeiro já quebrou quem não migrou. O segundo é o que vale anotar hoje, porque pipeline de transcrição é o tipo de coisa que roda sozinha e ninguém olha até parar.
  • Foi uma semana cheia no catálogo de exploração ativa da CISA. Além do Gitea, entraram dez CVEs entre 26 e 27 de agosto, incluindo NetScaler, SQL Server e uma da JFrog Artifactory. Essa última merece nota porque é ferramenta de quem constrói: a CVE-2026-66384 é travessia de caminho no cache de artefato Docker de repositório remoto, permite usuário autenticado escrever fora do diretório previsto, e afeta versões antes da 7.146.35 e da 7.161.0 até a 7.161.16. O CVSS dela é 5,3, média. Está sendo explorada mesmo assim, o que é o lembrete de sempre: nota de severidade não prevê exploração, contexto prevê.
  • Nvidia comprando a Hugging Face, segundo a imprensa e ninguém mais. A história saiu em 26 de agosto pelo The Information e foi repercutida por TechCrunch, CNBC e Forbes, falando em 12,9 bilhões de dólares. Registro com a ressalva que ela merece: até aqui nem a Nvidia nem a Hugging Face confirmaram, e a sala de imprensa da Nvidia não tem nada sobre isso. Se confirmar, muda quem controla o lugar de onde metade do mundo baixa peso de modelo. Mas a pergunta prática independe do desfecho: se o seu build faz download de hf.co em tempo de deploy, você já tem um ponto único de falha que não é seu, e isso valia consertar na semana passada.

O que eu faria com isso esta semana

  1. Levantar a versão real de Next.js de cada aplicação, não a do repositório principal. A falha de AVIF pega da 10.0.0 em diante, então o critério não é "estamos atualizados", é qual número aparece no node_modules da imagem que está no ar. Self-host atualiza para 16.3.3 ou 15.5.24. Quem está na Vercel não precisa fazer nada, mas confirme que é mesmo o caso antes de assumir.
  2. Atualizar Gitea hoje e olhar o registro aberto. A correção é de julho, a exploração automatizada é desta semana e o alvo é o servidor que guarda seu código. Depois de atualizar, decida se o registro aberto precisa estar ligado naquela instância, e confira os git hooks se ela esteve exposta.
  3. Antes de subir para o Kubernetes 1.37, procurar volume compartilhado entre pods com rótulo SELinux diferente. É a única mudança da lista que falha na hora de iniciar, e ela tem escape documentado por mais uma versão. Depois disso, vale olhar se algum controller seu trata HTTP 429 como erro qualquer.
  4. Anotar 26 de fevereiro de 2027 no calendário de quem tem transcrição em produção. Os quatro modelos de áudio da OpenAI saem nessa data. Migração de modelo de transcrição não é troca de string: muda formato de saída, muda qualidade em português e muda o que o seu pós-processamento espera receber.

Radar é a newsletter semanal deste blog: o que aconteceu na semana em tecnologia, com comentário sobre o que muda para quem constrói e opera software.

Tem um desses rodando — ou pra construir?

Me conte em duas linhas. Respondo em até 24h dizendo se é o tipo de coisa que eu faço bem — e se não for, digo isso também.

Outros textos