Originally published at webofmike.com on 2026-10-21. The demo repo was run before publishing.
에이전트가 파일 경로를 선택할 수 있고, 코드가 해당 쓰기 작업이 허용되는지 결정하는 경우, 두 줄의 순서를 살펴보십시오. '검사(Check)' 후 '해결(Resolve)'은 버그입니다. '해결' 후 '검사'는 해결책입니다.
이것이 CVE-2026-103572의 간략한 버전으로, Visual Studio와 SQL Server Management Studio의 Copilot 컴포넌트에서 발생한 원격 코드 실행(Remote Code Execution, RCE) 버그입니다. 이 취약점은 embracethered.com에 의해 공개되었으며, CVSS 8.8 등급을 받았고, Microsoft가 Patch Tuesday인 2026-10-13에 패치했습니다.
무엇이 잘못되었는가
Copilot은 MCP 서버를 등록하는 파일들을 포함하여 자체 설정 파일 목록을 보호하고 있었습니다. 이 가드(guard)는 요청된 파일 이름을 문자열로 비교했습니다. 하지만 실제 쓰기 작업은 파일 시스템이 해결한 경로로 이루어졌습니다. 이 두 가지는 항상 일치하지 않았습니다.
이 글에서는 세 가지 방식으로 취약점이 발생하는 것을 보여줍니다:
- 후행 점(Trailing dot). Windows 환경에서
settings.json.과 settings.json은 같은 파일로 끝나지만, 서로 다른 문자열입니다. 가드는 일치하는 항목을 발견하지 못하고 이를 통과시켰습니다.
- 경로 순회(Path traversal). 신뢰할 수 있는 디렉토리 내부에서 시작하는 경로라도 여전히
..를 사용하여 외부로 나갈 수 있습니다.
- 프롬프트 주입(Prompt injection): 에이전트를 구동하는 모델에 대한 공격으로, 공격자가 어떻게 악성 경로를 도구 호출(tool call)에 넣게 되는지를 보여줍니다.
쓰기 작업이 MCP 설정 파일에 도달하면 나머지는 간단합니다. 서버 항목을 추가하고, 해당 서버의 실행 명령어를 자신이 제어할 수 있는 무언가를 가리키도록 지정한 다음, IDE가 다음에 MCP 서버를 로드할 때 이를 실행하게 만듭니다. 개념 증명(proof of concept)은 calc.exe를 열었습니다. 패치에서는 보고된 바에 따르면, 다른 어떤 것이 해당 경로를 보기 전에 GetFullPath 호출을 추가하여 경로를 정규화(canonical)합니다.
Windows에 국한되지 않는 부분
후행 점 문제는 Windows에서만 작동합니다. .. 케이스는 어디서든 작동하며, 이는 동일한 실수입니다: 하나의 문자열을 검증하고 다른 경로에 쓰는 것입니다.
저는 themsquared/agent-config-guard-bypass에 이식 가능한 재현(repro) 코드를 작성했습니다. 에이전트는 workspace/ 내부를 수정할 수 있습니다. 형제 디렉터리인 agent_home/에는 어떤 MCP 서버가 실행될지 결정하는 mcp_settings.json 파일이 있습니다. 에이전트는 이 파일에 절대 도달해서는 안 됩니다.
취약한 가드(guard):
def guard_write(requested_path: str, content: str) -> Path:
candidate = WORKSPACE / requested_path
# BUG: ".."가 해결되기 전에 원본 문자열로 검사됨.
...
이 가드에 ../agent_home/mcp_settings.json을 요청해 보세요. 경로가 해결되기 전에는 해당 문자열이 실제로 작업 공간(workspace) 경로로 시작하므로, 검사를 통과합니다. 그런 다음 .resolve()를 호출하면 ..가 무너지면서 쓰기 작업이 작업 공간 외부, 즉 무엇이 실행될지를 제어하는 단일 파일에 도달하게 됩니다.
수정된 가드(fixed guard)는 호출 순서를 바꿉니다:
def guard_write(requested_path: str, content: str) -> Path:
resolved = (WORKSPACE / requested_path).resolve() # 먼저 resolve 실행
if resolved != WORKSPACE and WORKSPACE not in resolved.parents:
...
이제 검사와 쓰기 작업 모두 동일한 정규화된 경로(canonical path)를 사용합니다. 공격자가 조작할 수 있는 원본 문자열이 남아있지 않습니다.
실행하기
git clone https://github.com/themsquared/agent-config-guard-bypass.git
cd agent-config-guard-bypass
python3 exploit_demo.py
취약한 가드는 mcp_settings.json 파일에 새로운 update-check 서버를 작성합니다. 수정된 가드는 어떤 내용이 쓰여지기 전에 동일한 호출을 거부합니다.
찾아볼 곳
이 취약점은 IDE에서 발견되었지만, 이 패턴은 LLM(대규모 언어 모델)이 경로를 선택하고 코드가 그에 따라 작동하는 모든 곳에서 나타납니다. 에이전트 하네스 자체의 도구 핸들러, MCP 서버의 작업 디렉터리 샌드박스, 또는 일련의 MCP 도구 앞에 배치된 정책 검사 등 어디든 해당됩니다. 이 중 어느 곳이든 resolve() 호출 전에 단 하나의 startswith()만 있으면 동일한 구멍으로 이어질 수 있습니다. 이 수정은 새로운 종속성이나 재설계가 필요하지 않습니다.
저장소(repo)가 다루지 않은 부분은 트레일링-닷 변종(trailing-dot variant) (Windows 및 NTFS 필요)과 프롬프트 주입(prompt-injection) 전달 방식입니다. 후자는 파일 쓰기 문제가 아니라 모델 자체의 문제입니다. 이 두 가지 모두에 대한 내용은 원본 글을 참고해야 합니다.
CVE와 MCP 설정 파일이 RCE 대상인 이유에 대한 FAQ가 포함된 전체 게시물은 다음과 같습니다: webofmike.com/trailing-dot-mcp-config-bypass