
개요
| 항목 | 내용 |
| CVE ID | CVE-2026-12944 |
| 취약점 유형 | CWE-918 (SSRF) → 인증된 사용자의 서버측 임의 코드 실행 |
| CVSS 3.1 | 9.6 (Critical) — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:N |
| 영향 범위 | IBM Langflow OSS 1.0.0 ~ 1.10.0 |
| 패치 버전 | 1.10.1 (관련 인증 취약점 CVE-2026-17628까지 포함하면 1.10.3) |
Langflow는 LLM 애플리케이션을 노드 기반으로 조립하는 비주얼 프레임워크다. 사용자가 파이썬 코드로 직접 "커스텀 컴포넌트"를 작성해 붙일 수 있는데, 당연히 이 지점은 임의 코드 실행의 후보지가 된다. Langflow도 이를 알고 있었고, 제출된 컴포넌트 코드를 실행하기 전에 코드 보안 스캐너를 한 번 거치도록 설계해 두었다. 문제는 그 스캐너가 지키는 블록리스트, DANGEROUS_IMPORTS가 딱 필요한 두 개를 빠뜨렸다는 점이다.
이 취약점이 특히 뼈아픈 이유는, 스캐너를 "통과"시키는 절차 자체가 이미 코드를 실행시켜 버린다는 데 있다. 검증은 인증된 사용자만 트리거할 수 있지만(PR:L), 일단 트리거되면 별다른 조건 없이 확정적으로 성립한다(AC:L, UI:N). 게다가 이 코드는 root(UID=0) 권한으로 실행된다.
전체 공격 체인

앞의 3단계가 "서버에서 코드를 실행시키는 것" 자체를 다루고, 뒤의 3단계는 그렇게 얻은 root 실행 권한으로 무엇을 할 수 있는지를 보여준다. 하나씩 소스 코드 수준에서 뜯어본다.
1~2단계 · 커스텀 컴포넌트 제출: 그저 회원가입만 하면 된다
Langflow는 자체 호스팅이 흔한 도구다. 배포 형태에 따라 회원가입이 열려 있거나, 최소한의 인증만으로 컴포넌트 제출 기능(custom_component 관련 엔드포인트)에 접근할 수 있는 경우가 많다. 공격자에게 필요한 건 관리자 권한이 아니라, 그냥 컴포넌트를 하나 등록할 수 있는 계정뿐이다.
공격자가 준비하는 컴포넌트 코드는 이런 모양을 하고 있다.

핵심은 악성 코드가 build() 메서드 안이 아니라 모듈 최상위(top-level)에 있다는 것이다. 이 위치가 왜 중요한지는 다음 단계에서 드러난다.
3단계 · 검증 단계에서 즉시 실행: DANGEROUS_IMPORTS가 놓친 두 단어
Langflow는 이 코드를 무작정 실행하지 않는다. 컴포넌트가 유효한 클래스 구조를 갖췄는지 확인하기 위해, 서버는 먼저 이 코드를 import(임포트)한다. 그런데 그 전에, 명백히 위험한 임포트를 걸러내기 위한 코드 보안 스캐너가 한 번 지나간다. IBM의 보안 공지는 이 스캐너의 정체를 DANGEROUS_IMPORTS 블록리스트로 명시하고 있다.


subprocess, eval, exec, compile처럼 "누가 봐도 위험한" 이름들은 정확히 차단된다. 하지만 socket과 urllib은 이 명단에 없다. 그리고 이 두 모듈만으로도 네트워크 연결을 열고, 임의의 URL에 HTTP 요청을 보내는 데는 아무 지장이 없다.
더 근본적인 문제는 따로 있다. scan_code_security()가 무엇을 차단하든, 검증을 위해 코드를 import하는 행위 자체가 이미 실행이다. 파이썬에서 import module은 그 모듈 파일의 최상위 레벨 코드를 즉시 실행시킨다 — 함수 정의나 클래스 정의를 등록하는 것과는 별개로, 최상위에 적힌 urllib.request.urlopen(...) 같은 호출문은 import되는 순간 곧바로 실행된다. 즉 스캐너가 "이 코드는 안전하다"고 결론 내리기도 전에, 검증을 위한 import 그 자체가 이미 공격자의 네트워크 요청을 서버에서 실행해 버린 뒤다.
그래서 이 API는 다음과 같은, 사실과 다른 응답을 돌려준다.
{ "validated": true }
코드는 이미 실행되었다. validated: true는 "이 코드가 안전하다"는 뜻이 아니라 "블록리스트에 있는 이름은 못 찾았다"는 뜻일 뿐인데, API 응답은 이 둘을 구분해서 알려주지 않는다.
4단계 · IMDSv1 SSRF로 AWS 자격 증명 탈취
Langflow는 클라우드에서 컨테이너로 배포되는 경우가 대부분이고, 그 컨테이너에는 필요한 AWS 리소스에 접근하기 위한 IAM 역할이 연결되어 있는 경우가 많다. 이 자격 증명은 인스턴스 메타데이터 서비스(IMDS)를 통해 얻을 수 있다.
문제는 IMDSv1이다. 최신 세대인 IMDSv2는 PUT 요청으로 토큰을 먼저 발급받아야 메타데이터에 접근할 수 있게 해서 SSRF로부터 어느 정도 방어가 되지만, IMDSv1은 별도 토큰 없이 단순 GET 요청만으로 응답을 내준다.
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/ → 역할(role) 이름 반환 GET
http://169.254.169.254/latest/meta-data/iam/security-credentials/<역할이름> → AccessKeyId, SecretAccessKey, Token을 포함한 JSON 반환
urllib.request.urlopen() 두 번이면, 컨테이너에 연결된 IAM 역할의 모든 권한을 가진 임시 자격 증명을 그대로 손에 넣는다. 이건 Langflow 자체의 취약점이 아니라 IMDSv1이라는 레거시 기능의 특성이지만, SSRF 한 방으로 도달할 수 있다는 점에서 이 체인의 위력을 몇 배로 키운다.
5단계 · 리버스 쉘로 UID=0 획득
socket 모듈만으로도 외부로 나가는 TCP 연결을 여는 데는 전혀 문제가 없다. 공격자가 미리 띄워 둔 리스너로 연결한 뒤 표준 입출력을 그 소켓에 연결하면, 인터랙티브 쉘 하나가 통째로 넘어온다.
IBM의 보안 공지는 이 코드가 root(UID=0) 권한으로 실행된다고 명시한다. 즉 별도의 권한 상승 단계가 필요 없다 — 3단계에서 얻은 코드 실행이 이미 최고 권한이다. 공격자는 이제 컨테이너 파일시스템 전체를 읽고 쓸 수 있고, .env 파일이나 애플리케이션 설정에 박혀 있는 DB 접속 정보, 다른 API 키까지 그대로 확인할 수 있다.
6단계 · 내부 도커 네트워크로 횡적 이동
Langflow 컨테이너는 보통 PostgreSQL(플로우·사용자 데이터 저장)이나 Redis(캐시·큐) 같은 서비스와 같은 도커 네트워크 안에 함께 떠 있다. 이들 서비스는 "내부 네트워크에서만 접근 가능"하다는 전제로 인증을 느슨하게 설정해 두는 경우가 드물지 않다. 4~5단계에서 확보한 자격 증명과 파일시스템 접근 권한이 있으면, 공격자는 이 내부 서비스들에도 별다른 장벽 없이 도달한다.
패치는 어떻게 이루어졌나
1.10.1에서의 수정은 근본 원인 두 가지를 동시에 겨냥한다.

단순히 블록리스트에 두 단어를 추가한 것으로 보일 수 있지만, IBM의 공지 문구를 보면 스캐너가 다루는 대상 자체가 "네트워크 기반 코드 실행 위험"으로 명확히 재정의됐다는 점을 알 수 있다. 블록리스트 방식은 본질적으로 "우리가 지금까지 생각해낸 위험한 이름들의 목록"이라서, 이번처럼 목록에서 빠진 항목이 나올 때마다 땜질할 수밖에 없는 구조다. 검증을 위해 코드를 실제로 import(=실행)하는 아키텍처 자체를 바꾸지 않는 한, "우리가 놓친 다음 모듈은 무엇인가"라는 질문은 계속 남는다.
완화 조치 (패치 적용 전 임시 대응)
- 컴포넌트 제출 기능이 필요하지 않다면, 신뢰할 수 없는 사용자의 회원가입 자체를 막아 진입 경로를 차단한다.
- 클라우드 환경이라면 IMDSv2를 강제하고 IMDSv1을 비활성화해, SSRF가 성립하더라도 토큰 없이는 자격 증명에 도달하지 못하게 한다.
- Langflow 컨테이너의 아웃바운드 네트워크 접근을 꼭 필요한 목적지로만 제한(egress 방화벽)해, 169.254.169.254나 내부 DB로의 임의 연결 자체를 차단한다.
- 컨테이너를 root가 아닌 비특권 사용자로 실행하도록 설정을 점검한다. 이 취약점의 피해가 큰 이유 중 하나가 "root로 실행된다"는 점이므로, 최소 권한 원칙을 적용해 두면 같은 종류의 코드 실행이 일어나도 피해 범위가 줄어든다.
- PostgreSQL·Redis 등 내부 서비스에도 네트워크 위치와 무관하게 인증을 요구하도록 설정한다. "내부망이니 안전하다"는 가정이 이번 체인의 마지막 단계를 가능하게 만든 전제였다.
'CVE 취약점' 카테고리의 다른 글
| CVE-2026-87902 분석: WordPress Core 인증 없는 LFI, 그리고 조건부 RCE (0) | 2026.09.30 |
|---|---|
| CVE-2026-31431 "Copy Fail" — Linux 커널 algif_aead 권한 상승 취약점 분석 (0) | 2026.09.29 |
| CVE-2026-64638 (XSS2Shell) 분석: 워드프레스 로그인 화면 사전인증 XSS → RCE 체인 분석 (0) | 2026.09.26 |
| CVE-2026-8932: curl/libcurl mTLS 커넥션 재사용 취약점 분석 (0) | 2026.09.23 |
| CVE-2026-17633 분석 — Langflow OSS 인증된 원격 코드 실행(RCE) (0) | 2026.09.22 |