flawopen.com/Teardowns/cve-2024-3177-kubernetes-serviceaccount-envfrom-secrets-bypass

● CVE-2024-3177 · CVSS 2.7 · निम्न
सुरक्षा अनुसंधान · FlawOpen

CVE-2024-3177: Kubernetes ServiceAccount एडमिशन में envFrom से Secret प्रतिबंध का बायपास

CVE-2024-3177 (CVSS 2.7, कम): kube-apiserver का ServiceAccount एडमिशन प्लगइन Secret वॉल्यूम और env valueFrom संदर्भों को सर्विस अकाउंट की अनुमत सूची से मिलाता था, लेकिन envFrom को कभी नहीं देखता था, इसलिए एक पॉड अपने नेमस्पेस का कोई भी Secret लोड कर सकता था।

💡 आसान भाषा में (ELI5)

एक होटल हर सफ़ाईकर्मी को उन कमरों की सूची देता है जिनमें वह जा सकता है। स्टाफ़ दरवाज़े पर खड़ा गार्ड चाबी के गुच्छे की हर चाबी और बेल्ट पर लटकी हर अकेली चाबी को उस सूची से मिलाता है। लेकिन ज़िप वाली वह थैली कोई नहीं खोलता जिसमें एक पूरी मंज़िल की सारी चाबियाँ एक साथ रहती हैं। जिसे सिर्फ़ लॉबी की अनुमति है, वह एग्ज़ीक्यूटिव मंज़िल की थैली लेकर अंदर चला जाता है और गार्ड उसे जाने देता है, क्योंकि उसकी जाँच-सूची में थैली का ज़िक्र ही नहीं है।

इस पेज के मुख्य शब्द

ServiceAccount admission plugin
kube-apiserver में बना एडमिशन कंट्रोलर, जो पॉड का सर्विस अकाउंट भरता है और माँगे जाने पर सीमित करता है कि पॉड किन Secrets का संदर्भ दे सकता है (plugin/pkg/admission/serviceaccount)।
kubernetes.io/enforce-mountable-secrets
ServiceAccount पर लगने वाला एनोटेशन। इसे "true" करने पर उस अकाउंट से चलने वाले पॉड केवल अकाउंट के secrets फ़ील्ड में दिए Secrets का ही संदर्भ दे सकते हैं।
envFrom / secretRef
कंटेनर का फ़ील्ड जो किसी Secret की सभी कुंजियों को एक साथ एनवायरनमेंट वेरिएबल के रूप में ले आता है; जबकि env[].valueFrom.secretKeyRef सिर्फ़ एक कुंजी लाता है।
एफ़ेमेरल कंटेनर
pods/ephemeralcontainers सबरिसोर्स से चलते हुए पॉड में जोड़ा गया डीबग कंटेनर। इसकी एडमिशन जाँच अलग से limitEphemeralContainerSecretReferences() में होती है।

मूल कारण विश्लेषण (Root Cause)

plugin/pkg/admission/serviceaccount/admission.go में limitSecretReferences() माउंट किए जा सकने वाले Secrets की अनुमत सूची लागू करने के लिए पॉड के Secret वॉल्यूम और हर कंटेनर के env[].valueFrom.secretKeyRef को जाँचता था। पूरा Secret ले आने वाले envFrom[].secretRef को वह कभी नहीं जाँचता था। यही कमी init कंटेनरों में और limitEphemeralContainerSecretReferences() में एफ़ेमेरल कंटेनरों के लिए भी थी। PR #124322 ने तीनों जगह छूटा हुआ envFrom लूप जोड़ा।

हमले का चरण-दर-चरण प्रवाह

चरण 1

एक प्रतिबंधित सर्विस अकाउंट

ServiceAccount builder पर kubernetes.io/enforce-mountable-secrets: "true" लगा है और उसके secrets में सिर्फ़ build-token है। उसी नेमस्पेस में prod-db-credentials भी रखा है।

चरण 2

envFrom वाला पॉड स्पेक

पॉड बनाने की अनुमति वाला उपयोगकर्ता builder से चलने वाला पॉड भेजता है और किसी सामान्य, init या एफ़ेमेरल कंटेनर पर envFrom: [{secretRef: {name: prod-db-credentials}}] सेट करता है।

चरण 3

एडमिशन जाँच पास हो जाती है

limitSecretReferences() को अनुमत सूची के बाहर न कोई Secret वॉल्यूम मिलता है, न कोई env[].valueFrom.secretKeyRef, इसलिए पॉड स्वीकार हो जाता है।

चरण 4

kubelet Secret डाल देता है

kubelet envFrom को हल करता है और prod-db-credentials की हर कुंजी को एनवायरनमेंट वेरिएबल बना देता है, जिसे कंटेनर पढ़ सकता है।

सोर्स कोड: कमज़ोर बनाम सुरक्षित कार्यान्वयन

कमज़ोर कार्यान्वयन
// plugin/pkg/admission/serviceaccount/admission.go (kube-apiserver v1.29.3)
func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error {
	// Only allow Secrets that the service account lists in its "secrets" field.
	mountableSecrets := sets.NewString()
	for _, ref := range serviceAccount.Secrets {
		mountableSecrets.Insert(ref.Name)
	}

	for _, volume := range pod.Spec.Volumes {
		source := volume.VolumeSource
		if source.Secret != nil && !mountableSecrets.Has(source.Secret.SecretName) {
			return fmt.Errorf("volume with secret.secretName=%q is not allowed because service account %s does not reference that secret", source.Secret.SecretName, serviceAccount.Name)
		}
	}

	for _, container := range pod.Spec.Containers {
		for _, env := range container.Env {
			if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil {
				if !mountableSecrets.Has(env.ValueFrom.SecretKeyRef.Name) {
					return fmt.Errorf("container %s with envVar %s referencing secret.secretName=%q is not allowed because service account %s does not reference that secret", container.Name, env.Name, env.ValueFrom.SecretKeyRef.Name, serviceAccount.Name)
				}
			}
		}
		// BUG: container.EnvFrom is never inspected. envFrom[].secretRef imports
		// every key of any Secret in the namespace and still passes admission.
	}
	return nil
}
सुरक्षित और सुदृढ़ फ़िक्स
// plugin/pkg/admission/serviceaccount/admission.go (fixed in v1.29.4, PR #124322)
func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, pod *api.Pod) error {
	// Only allow Secrets that the service account lists in its "secrets" field.
	mountableSecrets := sets.NewString()
	for _, ref := range serviceAccount.Secrets {
		mountableSecrets.Insert(ref.Name)
	}

	for _, volume := range pod.Spec.Volumes {
		source := volume.VolumeSource
		if source.Secret != nil && !mountableSecrets.Has(source.Secret.SecretName) {
			return fmt.Errorf("volume with secret.secretName=%q is not allowed because service account %s does not reference that secret", source.Secret.SecretName, serviceAccount.Name)
		}
	}

	for _, container := range pod.Spec.Containers {
		for _, env := range container.Env {
			if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil {
				if !mountableSecrets.Has(env.ValueFrom.SecretKeyRef.Name) {
					return fmt.Errorf("container %s with envVar %s referencing secret.secretName=%q is not allowed because service account %s does not reference that secret", container.Name, env.Name, env.ValueFrom.SecretKeyRef.Name, serviceAccount.Name)
				}
			}
		}
		// FIX: envFrom can import a whole Secret, so it gets the same allow-list check.
		// The patch adds this loop for init and ephemeral containers too.
		for _, envFrom := range container.EnvFrom {
			if envFrom.SecretRef != nil && !mountableSecrets.Has(envFrom.SecretRef.Name) {
				return fmt.Errorf("container %s with envFrom referencing secret.secretName=%q is not allowed because service account %s does not reference that secret", container.Name, envFrom.SecretRef.Name, serviceAccount.Name)
			}
		}
	}
	return nil
}

इंजीनियरिंग और सिस्टम सुरक्षा चेकलिस्ट

स्रोत