FlawOpen

flawopen.com/Teardowns/cve-2026-90970-gitlab-ai-gateway-prompt-template-sandbox-escape

● CVE-2026-90970 · CVSS 9.9[4] · अति गंभीर
सुरक्षा अनुसंधान · FlawOpen

CVE-2026-90970: GitLab AI Gateway प्रॉम्प्ट सैंडबॉक्स से बचाव

खास Duo Agent Platform फ़्लो AI Gateway के प्रॉम्प्ट टेम्पलेट सैंडबॉक्स से निकलकर कमांड चला सकती थी। अपना Gateway चलाने वाले ऑपरेटर संबंधित GitLab शाखा के निर्धारित पैच वाला संस्करण पर अपडेट करें।[1], [4]

CVSS वेक्टरCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H[4]
💡 आसान भाषा में (ELI5)

बातचीत के रिकॉर्ड से रिपोर्ट भरने वाले कर्मचारी की कल्पना करें। उसे स्वीकृत फ़ील्ड को साधारण पाठ के रूप में ही नकल करना चाहिए। लेकिन खास तौर पर बनाया गया टेम्पलेट उसे फ़ाइल में “पुनर्स्थापित करें” विकल्प चलाने को कह सकता था, जिससे ऐप केवल शब्द नकल करने के बजाय संग्रहीत डेटा से सक्रिय ऑब्जेक्ट फिर बना देता। इससे डेटा दिखाने और ऐप से डेटा की व्याख्या करवाने के बीच की सीमा टूट जाती है। शोधकर्ता की रिपोर्ट में parse_raw() और pickle वाला विशिष्ट रास्ता दर्ज है; GitLab की सलाह सैंडबॉक्स से बाहर निकलने और कमांड चलने के प्रभाव की पुष्टि करती है, लेकिन हर चरण नहीं बताती।[3], [6]

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

AI Gateway
GitLab की सेवा जो AI सुविधाओं को मॉडल प्रदाताओं से जोड़ती है; स्वयं-होस्टेड Gateway इमेज का अलग संस्करण होता है।[5], [1]
Prompt template
स्थिर निर्देशों को फ़्लो इनपुट और बातचीत संदर्भ जैसे रनटाइम मानों से जोड़ने वाला टेम्पलेट।[3], [6]
Sandbox escape
सीमित वातावरण में उपलब्ध व्यवहार से उसकी निर्धारित सीमा पार करना।[3], [6]
CWE-1336
टेम्पलेट इंजन के विशेष तत्वों को सही तरह निष्प्रभावी न करना; CVE रिकॉर्ड यही कमजोरी बताता है।[4], [1]

फ्रेमवर्क मैपिंग

  • इस गाइड में बताई गई कमजोरी के लिए CWE संदर्भ: CWE-1336

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

GitLab की सलाह पुष्टि करती है कि कुछ स्थितियों में Duo Agent Platform तक पहुँच वाला प्रमाणित उपयोगकर्ता खास फ़्लो कॉन्फ़िगरेशन से प्रॉम्प्ट टेम्पलेट सैंडबॉक्स पार कर AI Gateway पर मनमाने कमांड चला सकता था। यह CWE-1336, CVSS 9.9 है। सलाह सभी शर्तें या कमजोर फ़ंक्शन नहीं बताती। 2026-09-17 का सार्वजनिक कमिट PromptSandboxedEnvironment जोड़ता है, जो बाउंड ऑब्जेक्ट के मेथड एक्सेस को रोकता है, और Jinja ऑपरेशन तथा फ़िल्टर सख्त करता है। यह रनटाइम ऑब्जेक्ट पर टेम्पलेट की क्षमता सीमित करने से मेल खाता है, लेकिन कमिट CVE का नाम नहीं लेता और सार्वजनिक स्रोत इसे पैच इमेज से नहीं जोड़ते। इसे अलग सार्वजनिक सुरक्षा-सुधार कहें; कोई सार्वजनिक स्रोत इसे इस CVE या सुधारी गई images से नहीं जोड़ता। GitLab के सार्वजनिक कार्य-मद #1850 में अधिक विशिष्ट रास्ता दिया गया है: Jinja टेम्पलेट बातचीत के इतिहास में मौजूद LangChain HumanMessage पर parse_raw() बुलाता है, जिससे pickle deserialization शुरू होता है। कार्य-मद इस प्रदर्शन का श्रेय शोधकर्ता की रिपोर्ट को देता है और कहता है कि यह GitLab के staging परिवेश में हुआ; विक्रेता की सलाह सभी प्रभावित शर्तें नहीं बताती।[1], [3], [4], [2]

स्वयं-होस्ट किए गए AI Gateway के ठीक किए गए रिलीज़

डिप्लॉयमेंट / रिलीज़ लाइनAI Gateway का ठीक किया गया संस्करणऑपरेटर की कार्रवाई
स्वयं-होस्टेड GitLab 19.2 लाइन19.2.4संबंधित स्वयं-होस्टेड Gateway इमेज टैग पर अपडेट करें और चल रही इमेज सत्यापित करें।
स्वयं-होस्टेड GitLab 19.3 लाइन19.3.2संबंधित स्वयं-होस्टेड Gateway इमेज टैग पर अपडेट करें और चल रही इमेज सत्यापित करें।
स्वयं-होस्टेड GitLab 19.4 लाइन19.4.1संबंधित स्वयं-होस्टेड Gateway इमेज टैग पर अपडेट करें और चल रही इमेज सत्यापित करें।
GitLab द्वारा होस्ट किया गया AI GatewayGitLab के अनुसार इसमें पहले ही सुधार लागू था।पुष्टि करें कि डिप्लॉयमेंट GitLab-hosted Gateway उपयोग करता है; इस स्थिति में स्वयं-होस्टेड इमेज निर्देश लागू न करें।

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

चरण 1

उपयोगकर्ता कस्टम फ़्लो बना सकता है

सलाह की शर्त है कि उपयोगकर्ता प्रमाणित हो और Duo Agent Platform तक पहुँच रखता हो। अधिक विशिष्ट भूमिका या सभी ज़रूरी क्षमताएँ नहीं बताई गईं।[1], [3]

चरण 2

फ़्लो खास टेम्पलेट कॉन्फ़िगरेशन देती है

खास फ़्लो कॉन्फ़िगरेशन Gateway के प्रॉम्प्ट टेम्पलेट सैंडबॉक्स तक पहुँचती है। सार्वजनिक सलाह सुरक्षित पूरी पुनरावृत्ति या सटीक अभिव्यक्ति नहीं देती।[1], [3]

चरण 3

टेम्पलेट चलना सैंडबॉक्स सीमा पार करता है

GitLab की सार्वजनिक कार्य-मद #1850 के अनुसार, टेम्पलेट बातचीत के इतिहास के LangChain HumanMessage पर parse_raw() बुलाकर pickle deserialization शुरू कर सकता था। यह शोधकर्ता द्वारा दर्ज रास्ता है; इसे हर प्रभावित flow या विक्रेता के patch का पूरा विवरण न मानें।[3]

चरण 4

Gateway पर कमांड चल सकते हैं

पुष्ट असर AI Gateway पर मनमाने कमांड चलना है—Gateway पर सर्वर निष्पादन, केवल मॉडल का निर्देश न मानना नहीं।[1], [3]

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

GitLab कार्य-मद 1850 बताती है कि कमजोर flow ने LangChain HumanMessage ऑब्जेक्ट को Jinja के सामने रखा, जहाँ टेम्पलेट parse_raw() बुलाकर pickle deserialization शुरू कर सकता था। यह रक्षात्मक उदाहरण संदेश ऑब्जेक्ट को टेम्पलेट संदर्भ से बाहर रखता है, केवल टेक्स्ट देता है, और callable attributes तथा methods तक पहुँच रोकता है। यह बंद की जाने वाली सीमा दिखाता है; यह GitLab का जारी किया patch नहीं है। सार्वजनिक कमिट 89b72644 अलग सुरक्षा-सुधार है: उसका शीर्षक Jinja block-set लेखन पर है, और कोई सार्वजनिक स्रोत उसे CVE-2026-90970 या सुधारी गई कंटेनर इमेज से नहीं जोड़ता। ऑपरेटर विक्रेता द्वारा बताए गए सुधारे गए संस्करण अपनाएँ।[3], [2], [6]

सोर्स अंश: ai_gateway/prompts/base.py (रिपोर्ट में बताया गया कमजोर formatter; रक्षात्मक उदाहरण)

कमज़ोर कार्यान्वयन
# GitLab work item 1850 में बताए गए vulnerable path का अंश।
from jinja2 import PackageLoader
from jinja2.sandbox import SandboxedEnvironment
from langchain_core.prompts.string import DEFAULT_FORMATTER_MAPPING
from typing import Any

jinja_loader = PackageLoader("ai_gateway.prompts", "definitions")
jinja_env = SandboxedEnvironment(loader=jinja_loader)

def jinja2_formatter(template: str, /, **kwargs: Any) -> str:
    return jinja_env.from_string(template).render(**kwargs)

DEFAULT_FORMATTER_MAPPING["jinja2"] = jinja2_formatter
सुरक्षित और सुदृढ़ फ़िक्स
# यह रक्षात्मक पैटर्न का उदाहरण है, इस CVE के लिए GitLab का पैच नहीं।
from typing import Any
from jinja2 import StrictUndefined
from jinja2.sandbox import ImmutableSandboxedEnvironment

class DataOnlyPromptEnvironment(ImmutableSandboxedEnvironment):
    def is_safe_attribute(self, obj: Any, attr: str, value: Any) -> bool:
        if callable(value):
            return False
        return super().is_safe_attribute(obj, attr, value)

    def is_safe_callable(self, obj: Any) -> bool:
        return False

def render_prompt(template: str, history: list[Any]) -> str:
    safe_history = []
    for message in history:
        content = message.content
        if not isinstance(content, str):
            raise TypeError("prompt history must contain text only")
        safe_history.append({"content": content})

    env = DataOnlyPromptEnvironment(undefined=StrictUndefined)
    return env.from_string(template).render(history=safe_history)

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

स्रोत