← 모든 글

루프백은 신뢰 경계가 아니에요: CVE-2026-2611

From the guide: Claude Code Comprehensive Guide

2026년 5월 19일, National Vulnerability Database는 CVE-2026-2611을 공개했어요. MLflow 3.9.0의 Assistant 기능에는 로컬 /ajax-api 엔드포인트에 대한 출처 검증이 부적절한 문제가 있었어요. 어떤 탭에서든 악성 웹페이지가 열리면 Assistant의 로컬 서버로 교차 출처 요청을 보내고, 설정을 바꿔 전체 접근 모드를 켠 뒤, 함께 제공되는 Claude Code CLI 연동을 통해 임의 명령을 실행할 수 있었어요. Huntr는 이 취약점에 CVSS 3.0 기준 9.6점(CRITICAL)을 매겼고, 벡터는 AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H였어요. 네트워크에서 접근 가능하고, 공격 난도가 낮고, 권한이 필요 없으며, 범위가 바뀌고, 기밀성/무결성/가용성에 모두 큰 영향을 준다는 뜻이에요.1

근본 원인은 새롭지 않고 수정도 특별히 복잡하지 않지만, 위협 모델은 새로워졌어요. 루프백에 바인딩되어 있고 브라우저 탭에 상태 변경 엔드포인트를 노출하는 모든 로컬 서비스는 신뢰 경계에 해당해요. 그 서비스가 파일을 읽고, 셸 명령을 실행하고, 코드를 푸시할 수 있는 AI 에이전트를 구동한다면, “루프백 전용”은 더 이상 보안 통제로 작동하지 않아요. CVE-2026-2611은 더 많은 개발자 도구가 LLM 에이전트를 로컬 웹 UI 안에 감싸 넣을수록 반복해서 등장할 취약점 부류의 교과서 같은 사례예요.

요약

CVE-2026-2611은 MLflow 3.9.0의 불완전한 CORS 경로 열거 버그를 다뤄요. Assistant 기능은 MLflow의 is_api_endpoint() 검사가 포함하지 못한 /ajax-api/ 엔드포인트를 노출했고, 그래서 CORS 차단 미들웨어가 이 경로들을 건너뛰었어요. 여기에 기존 CORSMiddlewareallow_origins=["*"]까지 허용했기 때문에, evil.com에서 온 교차 출처 요청이 /ajax-api/3.0/mlflow/assistant/config를 호출해 Assistant를 전체 접근 모드로 바꾸고, 함께 제공되는 Claude Code CLI 연동을 통해 셸 명령을 보낼 수 있었어요.1 업스트림 수정(PR #20832, “Block CORS for ajax paths”)은 MLflow 3.10.0에서 보호 대상 경로 열거에 /ajax-api/를 추가하고, allow_origins를 설정된 허용 목록과 localhost 정규식으로 좁혔어요.2 아래 방어책은 MLflow만이 아니라 로컬 AI 에이전트 전반에 해당해요.

핵심 정리

역할 조치
MLflow를 실행하는 AI/ML 엔지니어 MLflow 3.10.0으로 업그레이드하세요. 루프백에서 수신하는 다른 로컬 AI 에이전트 UI도 점검하세요
보안 팀 위협 모델 템플릿에 “로컬 LLM 에이전트 루프백 서비스”를 추가하세요. 이 부류에서는 CORS 구멍 하나가 RCE로 이어져요
로컬 에이전트 UI를 만드는 도구 개발자 모든 API 경로 접두사를 한곳에서 열거하세요. localhost에만 바인딩된 서버라도 allow_origins=["*"]를 금지하세요. 파괴적 엔드포인트는 출처만이 아니라 새로운 사용자 동작도 요구하도록 보호하세요
엔지니어링 리더 팀의 AI 코딩 에이전트를 격리하고, 에이전트의 명령 실행 권한을 실제 피해 범위로 보세요

공격 흐름은 어떤 모습인가요

5단계로 충분해요. 각 단계는 허용된 기능을 사용하지만, 조합이 무너져요.

단계 동작 공격자가 제어하는 것
1 사용자가 로컬에서 mlflow ui를 실행하고, Assistant가 루프백(기본값 127.0.0.1)에서 수신해요 아직 없음(로컬 서비스)
2 MLflow가 실행되는 동안 사용자가 아무 탭에서나 evil.com을 열어요 페이지 콘텐츠
3 evil.comfetch('http://127.0.0.1:5000/ajax-api/3.0/mlflow/assistant/config', { method: 'PUT', credentials: 'include', body: ... })를 보내요 교차 출처 요청
4 /ajax-api/가 CORS 차단 경로 열거에 없었기 때문에 MLflow가 요청을 받아들이고, Assistant 설정이 전체 접근으로 바뀌어요 Assistant의 실행 중 설정
5 evil.com이 Assistant의 채팅 엔드포인트로 명령 페이로드를 올려요. MLflow는 권한 검사를 우회한 상태로 로컬 Claude Code CLI를 호출하고, 명령은 사용자 권한으로 실행돼요 임의 코드 실행

공격자는 피해자 컴퓨터에 아무것도 설치할 필요가 없어요. 피해자가 mlflow ui를 실행한 상태에서 페이로드가 담긴 탭 하나만 열면 돼요. 기존 CORSMiddlewareallow_origins=["*"]가 있었고, 경로 단위 CORS 차단 미들웨어가 /ajax-api/ 접두사를 포함하지 않았기 때문에 이 공격은 어떤 출처에서도 작동했어요.2

Claude Code 연동은 권한 증폭 장치예요. MLflow의 ClaudeCodeProvidershutil.which("claude")로 로컬 claude CLI를 찾고, 없을 때는 npm install -g @anthropic-ai/claude-code로 설치하라고 안내해요. Assistant의 permissions.full_access 설정 플래그가 켜져 있으면 프로바이더는 CLI 호출에 --permission-mode bypassPermissions를 추가해요. 이것은 대화형 권한 확인을 억제하고 사용자 확인 없이 도구 호출을 실행하게 하는 Claude Code 모드예요.3 에이전트가 허가받은 기능(셸, 파일 쓰기, HTTP)은 자연어 UX가 붙은 원격 셸이 되고, 공격자에게는 바로 그것이 필요해요.


근본 원인은 불완전한 경로 열거예요

수정 커밋을 읽으면 버그가 분명해져요. MLflow의 security_utils.py에는 이런 상수가 있었어요.

API_PATH_PREFIX = "/api/"

그리고 이런 검사가 있었어요.

def is_api_endpoint(path: str) -> bool:
    return path.startswith(API_PATH_PREFIX) and path not in TEST_ENDPOINTS

CORS 차단 미들웨어는 출처 검증을 적용할지 결정하려고 is_api_endpoint()를 호출했어요. /api/로 시작하지 않는 모든 경로는 API 경로가 아닌 것으로 처리됐어요. Assistant는 브라우저용 엔드포인트로 /ajax-api/를 도입했지만, 아무도 새 접두사를 is_api_endpoint()에 추가하지 않았어요.

수정은 security_utils.py에서 동작을 한 줄 바꾸는 정도예요.2

API_PATH_PREFIX = "/api/"
AJAX_API_PATH_PREFIX = "/ajax-api/"

def is_api_endpoint(path: str) -> bool:
    return (
        path.startswith(API_PATH_PREFIX) or path.startswith(AJAX_API_PATH_PREFIX)
    ) and path not in TEST_ENDPOINTS

여기에 fastapi_security.py의 병행 수정이 더해져요. 기존 CORSMiddlewareallow_origins=["*"]를 설정된 허용 목록과 localhost 정규식으로 바꿨어요. 합쳐서 security_utils.py에는 5줄이 추가되고 2줄이 삭제됐고, fastapi_security.py에는 5줄이 추가되고 1줄이 삭제됐어요.2

취약점 전체가 이것이에요. CVE-2026-2611은 새 엔드포인트 접두사가 배포됐지만, 어떤 경로에 CORS 보호를 적용할지 열거하는 단일 기능이 갱신되지 않아 생겼어요. 공격 표면은 잊힌 설정 상수 하나였어요.

왜 이 패턴은 반복될까요

이 버그는 MLflow보다도, AI 에이전트보다도 오래된 문제예요. 웹 프레임워크에는 경로 열거가 존재한 만큼 오래전부터 경로 열거 실수가 있었어요. 달라진 것은 결과예요. 엔드포인트 하나를 열거하지 못한 로컬 웹 UI는 예전에는 설정 데이터 일부를 흘리거나 서비스 거부를 일으키는 정도였어요. 하지만 셸 접근 권한을 가진 AI 코딩 에이전트를 실행하는 로컬 웹 UI가 엔드포인트 하나를 빠뜨리면 개발자 컴퓨터 전체가 노출돼요.

로컬 AI 에이전트 UI는 커지고 있는 범주예요. 127.0.0.1에서 제공되는 브라우저 UI 안에 LLM 에이전트를 감싸 넣는 도구라면 모두 범위 안에 들어와요. 채팅 패널이 붙은 실험 추적기(MLflow의 형태), localhost API를 통해 에이전트를 구동하는 IDE 플러그인, 확장이 HTTP로 통신하는 개발 모드 서버, 로컬 모델 실행기를 위한 웹 대시보드가 여기에 해당해요. 각각은 루프백에 바인딩돼요. 각각은 브라우저 탭에 상태 변경 엔드포인트를 노출해요. 각각은 셸 실행, 파일 쓰기, git 푸시 권한을 가진 에이전트를 실행해요. 이들 모두는 경로 열거 버그 하나만으로 CVE-2026-2611과 같은 상태가 될 수 있어요.

이 부류 밑에 깔린 공통 가정은 이거예요. “정상 요청도 127.0.0.1에서 오니, 브라우저의 동일 출처 정책이 루프백 서버를 보호해 줄 것이다.” 이 가정은 교차 출처 관문(CORS, Host 헤더 검증, Origin 헤더 검증, Fetch Metadata 헤더) 중 하나라도 엔드포인트 접두사를 놓치는 순간 깨져요. 서버가 요청을 명시적으로 받아들이면 브라우저는 서버를 보호하지 않아요.

제가 3월에 다룬 조용한 외부 전송 공격은 같은 신뢰 가정이 반대 방향으로 깨진 사례예요. 조용한 외부 전송은 에이전트가 허가받은 도구를 통해 나가는 길을 숨겨요. 루프백 오분류는 브라우저가 허가받은 네트워크 호출을 통해 들어오는 길을 숨겨요. 둘 다 결국 같은 결론으로 줄어들어요. 경계 검사 하나가 깨질 때마다 LLM 에이전트가 허가받은 기능은 공격 수단이 돼요.

이 취약점 부류는 실제로 어떤 모습인가요

이 취약점 부류에는 4가지 재료가 있어요. 네 가지가 모두 맞으면 서비스는 악용될 수 있어요.

  1. 루프백 또는 0.0.0.0에 바인딩된 로컬 웹 서버. 루프백 전용이 더 흔한 형태예요. localhost에 바인딩된 서비스를 교차 출처 탭으로부터 보호하는 것은 네트워크 바인딩이 아니라 브라우저의 CORS, Host, Origin 검사예요.
  2. 사용자 동작 없이 도달 가능한 상태 변경 엔드포인트. 설정 변경, 명령 제출, 파일 쓰기가 여기에 들어가요. 임의 출처의 fetch()가 상태를 바꿀 수 있다면 그 서비스는 도달 가능해요.
  3. 로컬 사용자는 갖고 있지만 웹페이지가 가져서는 안 되는 권한. 홈 디렉터리 읽기, 셸 명령 실행, 저장된 자격 증명으로 클라우드 API 호출, 소스 파일 수정 같은 권한이에요.
  4. 빠진 경계 검사 하나. 잊힌 경로 접두사, 와일드카드 allow_origins, 빠진 Host 헤더 검증, 빠진 Origin 검증, 빠진 Sec-Fetch-Site 강제가 여기에 해당해요.

CVE-2026-2611은 네 가지 모두에 걸렸어요. 수정은 MLflow의 /ajax-api/ 접두사에 대해 4번째 재료를 닫아요. 하지만 1번부터 3번까지는 다른 모든 로컬 AI 에이전트 도구에도 여전히 남아 있어요. 이 부류의 다음 CVE는 다른 패키지 이름으로 거의 같은 모습일 가능성이 높아요.


로컬 AI 에이전트 루프백 부류에 대한 방어책

일반적인 CORS 조언, 즉 allow_origins를 구체적인 목록으로 설정하고 필요한 경우에만 자격 증명을 켜라는 말은 필요하지만 충분하지 않아요. 이 부류에는 더 깊은 통제가 필요해요.

경로 접두사를 한곳에서 열거하고, 새 접두사가 보호되지 않은 상태로 나타나면 실패하는 테스트를 작성하세요. MLflow의 버그는 팀이 /api/를 배포할 때는 is_api_endpoint()를 갱신했지만 /ajax-api/를 배포할 때는 잊어서 생겼어요. FastAPI 앱에 등록된 모든 라우트를 순회하고, 각 라우트가 보호되는지 is_api_endpoint()에 물어본 뒤, 인식되지 않은 경로 접두사가 있으면 실패하는 테스트가 있었다면 PR 단계에서 잡을 수 있었어요. 이 검사는 테스트 코드 약 20줄이면 충분해요.

CORS만이 아니라 Origin과 Host 헤더도 검증하세요. CORS는 서버 응답으로부터 브라우저를 보호해요. 서버가 요청을 받아들이는 것 자체를 막지는 않아요. 로컬 AI 에이전트 서비스는 Origin 헤더가 명시적 허용 목록에 없거나 Host 헤더가 127.0.0.1 또는 localhost가 아니면 요청을 거부해야 해요. 후자는 DNS 리바인딩 공격을 막기 위해서예요. 두 검사는 모두 기본적으로 닫힌 방식이어야 해요.

파괴적 엔드포인트에는 Sec-Fetch-Site: same-origin 기준을 적용하세요. 브라우저는 evil.com에서 127.0.0.1로 가는 요청을 포함해 모든 교차 출처 fetch()Sec-Fetch-Site: cross-site를 보내요. Fetch Metadata Request Headers 명세는 이 헤더 값을 정의하고, Chrome, Firefox, Safari, Edge가 모두 이 헤더를 보내요.4 상태를 바꾸는 엔드포인트에 대해 Sec-Fetch-Site: same-origin이 아니면 거부하는 미들웨어는 모든 경로 접두사를 기억하는 데 의존하지 않는 추가 방어층을 제공해요.

AI 에이전트의 실행 권한을 격리하세요. Claude Code, Cursor, Aider, Continue는 모두 기본적으로 사용자 전체 권한으로 실행돼요. 그게 가장 쉬운 UX이기 때문이에요. 작업공간별 권한 격리(Linux에서는 명시적 마운트 규칙을 둔 bwrap, .app 번들로 제공되는 도구는 macOS 앱 권한 격리, 크로스 플랫폼에서는 작업공간별 컨테이너)를 적용하면 핵심 작업 흐름을 포기하지 않고 피해 범위를 줄일 수 있어요. 이 완화책은 마찰을 더해요. 그 마찰이 목적이에요.

에이전트의 커밋은 검토되기 전까지 신뢰하지 마세요. 브랜치 보호, 필수 리뷰, 서명된 커밋이 필요해요. “커밋 권한을 가진 AI 에이전트” 모델은 공격자가 에이전트의 로컬 환경을 장악하는 순간 무너져요. AI 코드 리뷰에는 반대 의견이 필요합니다에서 다룬 리뷰어의 이견은 여기에도 적용돼요. AI 에이전트와 인간 리뷰어가 항상 동의한다면, 인간 리뷰어는 장식일 뿐이에요.


이 부류의 다음 CVE는 어떤 모습일까요

흥미로운 질문은 CVE-2026-2611과 같은 모양의 다음 취약점이 나올지 여부가 아니에요. 어느 패키지가 먼저 배포하느냐가 흥미로운 질문이에요. 후보는 로컬 AI 도구 생태계 전반에 걸쳐 있어요. UI 안에 에이전트를 감싸 넣은 실험 추적기, HTTP로 로컬 에이전트 백엔드와 통신하는 IDE 플러그인, 어떤 도구든 작업 중에 여는 개발 모드 서버, 에이전트 UI를 구동하기 위해 확장이 사용하는 localhost API가 여기에 포함돼요. 아직 이 형태로 CVE를 공개한 도구는 없어요. 하지만 여러 도구가 루프백에 바인딩하고 브라우저를 신뢰 관문으로 삼아 교차 출처 요청을 받아들이는 코드 경로를 이미 갖고 있어요.

이 부류는 CVE-2026-2611보다 더 커요. 그리고 위협 모델은 바뀌었어요. 2026년의 개발자 컴퓨터에는 셸 실행, 파일 쓰기, 커밋 푸시, 클라우드 API 호출을 할 수 있는 AI 에이전트가 있어요. 이 에이전트를 구동하는 로컬 웹 UI는 UI의 HTTP 서버가 강제하는 신뢰 경계를 통해 에이전트 권한을 물려받아요. 2020년 웹 프레임워크의 CORS 오설정은 설정 데이터 일부를 흘렸어요. 2026년 로컬 AI 에이전트 UI의 CORS 오설정은 개발자 컴퓨터 전체와 프로덕션 배포 권한까지 흘려요.

수정은 단일 패치가 아니에요. 수정은 통제의 묶음이에요. 테스트가 붙은 명시적 경로 열거, 다층 헤더 검증, 권한 격리, 에이전트가 작성한 커밋에 대한 리뷰 규율이 필요해요. 이 중 하나만 제공하고 나머지를 빠뜨리는 도구는 다른 접두사를 단 CVE-2026-2611의 후속편을 배포하는 셈이에요.


FAQ

CVE-2026-2611이 무엇인가요?

CVE-2026-2611은 MLflow 3.9.0의 Assistant 기능에 있던 부적절한 출처 검증 결함에 대한 National Vulnerability Database 식별자예요. 이 결함을 이용하면 어떤 웹페이지든 Assistant의 로컬 /ajax-api 엔드포인트로 교차 출처 요청을 보내고, 설정을 바꿔 전체 접근 모드를 켠 뒤, 함께 제공되는 Claude Code CLI 연동을 통해 임의 명령을 실행할 수 있어요. CVSS 3.0 기본 점수는 9.6(CRITICAL)이에요. MLflow 3.10.0에서 PR #20832, “Block CORS for ajax paths”로 수정됐어요.12

근본 원인은 무엇이었나요?

MLflow의 is_api_endpoint() 검사는 /api/ 경로 접두사만 열거했어요. Assistant 기능이 /ajax-api/ 엔드포인트를 도입했을 때 아무도 이 열거를 갱신하지 않았어요. 그 결과 CORS 차단 미들웨어는 /ajax-api/로 들어오는 교차 출처 요청을 출처 검사 없이 통과시켰어요. 두 번째 약점도 버그를 키웠어요. 기존 CORSMiddlewareallow_origins=["*"]를 허용했기 때문에, 차단되지 않은 경로는 어떤 출처에도 관대한 CORS 헤더를 되돌려줬어요. 수정은 경로 검사에 AJAX_API_PATH_PREFIX = "/ajax-api/"를 추가하고, allow_origins=["*"]를 설정된 허용 목록과 localhost 정규식으로 바꿔요.2

내 MLflow 설치가 영향을 받는지 어떻게 알 수 있나요?

pip show mlflow | grep Version을 실행하세요. NVD의 CPE 범위에 따르면 MLflow 버전 >=3.9.0,<3.10.0이 영향을 받아요. MLflow 3.10.0(2026년 2월 20일 출시)에는 수정이 포함되어 있어요. pip install --upgrade mlflow로 업그레이드하고, 실행 중인 mlflow ui 프로세스를 모두 다시 시작하세요.

Claude Code CLI가 설치되어 있지 않아도 공격이 되나요?

설정 수정 단계는 그대로 작동해요. 임의 코드 실행 단계에는 피해자의 PATH에 Claude Code CLI가 있어야 해요. MLflow의 Assistant가 shutil.which("claude")로 이를 호출하기 때문이에요. CLI가 없으면 최악의 경우는 RCE가 아니라 정보 노출과 임의 설정 변조가 돼요. 하지만 문서화된 Assistant 설정 경로대로 Claude Code 백엔드를 구성한 사용자에게는 실제 노출이 RCE예요.

다른 로컬 AI 에이전트 서비스는 어떻게 보호하나요?

4가지 재료를 점검하세요. 루프백 또는 와일드카드 바인딩, 상태 변경 엔드포인트, 에이전트 수준 권한, 그리고 빈틈이 있을 수 있는 단일 경계 검사예요. 명시적 Origin 허용 목록, 127.0.0.1/localhost에 대한 Host 헤더 검증(DNS 리바인딩 방지), 파괴적 엔드포인트의 Sec-Fetch-Site: same-origin 기준을 추가하세요. bwrap, macOS 앱 권한 격리, 작업공간별 컨테이너로 에이전트의 실행 권한을 격리하세요. 에이전트의 git 커밋은 검토되기 전까지 신뢰하지 마세요.



  1. National Vulnerability Database. “CVE-2026-2611: MLflow Assistant improper origin validation.” 2026년 5월 19일 공개. CVSS 3.0 기본 점수 9.6(CRITICAL), 벡터 CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H, Huntr 발행. 수정은 MLflow 3.10.0에 포함됨. https://nvd.nist.gov/vuln/detail/CVE-2026-2611. 원 보고서: https://huntr.com/bounties/8462addd-b464-4a84-b6a2-5529604e6e5a

  2. MLflow. “Block CORS for ajax paths (#20832).” Tomu Hirata가 2026년 2월 16일 병합. 변경 파일: mlflow/server/fastapi_security.py(+5/-1), mlflow/server/security_utils.py(+5/-2), 테스트 업데이트 포함. 이 diff는 is_api_endpoint()의 단일 접두사 검사를 두 접두사 검사로 바꾸고, 기존 CORSMiddlewareallow_origins["*"]에서 설정된 허용 목록과 localhost 정규식으로 좁혀요. https://github.com/mlflow/mlflow/commit/8f9c8a53af90842944101eb8b7d60706822c81bc

  3. MLflow 3.10.0 소스. mlflow/assistant/providers/claude_code.py: shutil.which("claude") 조회(268, 280, 355행), 설치 안내 "Install it with: npm install -g @anthropic-ai/claude-code"(286행), config.permissions.full_access에 의해 제어되는 권한 모드 상승 cmd.extend(["--permission-mode", "bypassPermissions"])(379-381행). mlflow/assistant/config.py: PermissionsConfig.full_access: bool = False 기본값(15행). mlflow/server/assistant/api.py: 라우터 접두사 /ajax-api/3.0/mlflow/assistant(63행), GET /config 읽기 엔드포인트(261행), PUT /config 변경 엔드포인트(276행). 교차 출처 공격이 full_access를 켜기 위해 노리는 것은 이 PUT 엔드포인트예요. PyPI의 MLflow 3.10.0 sdist(mlflow-3.10.0.tar.gz) 기준으로 확인됨. 

  4. W3C. “Fetch Metadata Request Headers.” Sec-Fetch-Site와 그 값 cross-site, same-origin, same-site, none을 정의해요. Chrome 76+, Firefox 90+, Safari 16.4+, Edge 79+에서 구현돼요. https://www.w3.org/TR/fetch-metadata/ 

관련 게시물

유지관리자가 공격자가 될 때: jqwik 1.10.0

jqwik 1.10.0은 Maven 출력에 파괴적인 프롬프트 인젝션 문자열을 내보냅니다. ANSI 이스케이프는 이 문자열을 사람 눈에는 숨깁니다. 유지관리자는 이를 의도적으로 추가했어요.

12 분 소요

AI 에이전트 설정 보안은 공급망 보안이에요

AI 에이전트 설정 보안은 공급망 검토에 포함되어야 해요. 후크, 에디터 작업, 설치 스크립트, MCP 파일, 플러그인은 사용자가 알아차리기 전에 코드를 실행할 수 있어요.

11 분 소요

Ralph 루프: 자율 AI 에이전트를 밤새 운영하는 방법

중지 훅, 스폰 예산, 파일 시스템 메모리를 활용한 자율 에이전트 시스템을 구축했습니다. 실패 사례와 실제로 코드를 출시하게 된 과정을 공유합니다.

8 분 소요