
CVE-2025-68116에 대한 문서
작성자: @x0root
취약점: 브라우저 렌더링 가능 업로드를 통한 저장형 XSS(Stored Cross-Site Scripting, SVG / HTML)
영향받는 소프트웨어: FileRise (< 2.7.1)
패치 버전: 2.7.1
공식 CVE(GHSA를 통해 요청됨): CVE-2025-68116(추적/권고: GHSA-35pp-ggh6-c59c)
이전 관련 권고(우회된 기존 완화 조치): GHSA-qrcv-vjvf-fr29
CVSS 평가:
보고자의 평가는 취약점을 *설치(planting)*하는 시점이 아니라 *악용(exploitation)*하는 시점을 기준으로 필요 권한(Privileges Required, PR)을 산정합니다.
악용은 피해자가 생성된 공개 공유 링크에 접근할 때 발생하며, 이때는 인증이나 권한이 필요하지 않습니다(PR:N).
CNA 평가는 악성 파일을 업로드할 수 있는 능력을 기준으로 PR을 산정합니다. 그러나 CVSS v3.1은 필요 권한(PR)을 공격자가 취약점을 악용하는 시점에 보유해야 하는 권한으로 정의하며, 취약한 상태를 만들거나 준비하는 데 필요한 권한으로 정의하지 않습니다.
따라서 PR:N이 실제 악용 조건을 더 정확하게 반영하며, 그 결과 심각도 등급은 Critical(9.6)입니다.
참고: GHSA-qrcv-vjvf-fr29는 FileRise 웹 UI(미리보기 창) 내에서 SVG가 렌더링되지 않도록 하는 완화 조치를 도입했습니다. 이 보고서는 해당 완화 조치의 우회—구체적으로 백엔드 공유/다운로드 엔드포인트—를 문서화한 것이며, GHSA-35pp-ggh6-c59c / CVE-2025-68116으로 추적됩니다.
이 문서는 CVE-2025-68116에 대한 완전한 기술 기록입니다: 이전 완화 조치 이후에도 지속되었고最终 v2.7.1에서 수정된 FileRise의 저장형 XSS입니다. 여기에는 발견 과정, 악용 PoC(Proof-of-Concept), 반복된 실패한 수정, 정밀한 근본 원인 제어 흐름 분석(증거 포함), 패치의 최종 검증, 그리고 CVSS 평가와 관련된 악용 가능성 특성 분석이 포함됩니다. 아래의 모든 내용은 재현된 테스트, 컨트롤러 검사, 공개 권고 스레드를 기반으로 합니다.
이전 권고인 GHSA-qrcv-vjvf-fr29는 FileRise 웹 UI에서의 인라인 렌더링을 차단하여 SVG 업로드를 통한 저장형 XSS를 해결했습니다. 그러나 해당 완화 조치는 다음과 같은 백엔드 엔드포인트가 SVG 파일을 제공하는 방식은 다루지 않았습니다:
/api/file/download.php/api/file/share.phpCVE-2025-68116(GHSA-35pp-ggh6-c59c로 추적)은 GHSA-qrcv-vjvf-fr29 완화 조치의 우회를 문서화합니다: 공격자는 조작된 SVG를 저장하고 공개 공유 링크 또는 특정 다운로드 동작을 통해 피해자에게 전달하여 FileRise 오리진에서 스크립트가 실행되도록 할 수 있습니다.
백엔드가 여전히 렌더링 가능한 방식으로 SVG를 노출하는지 확인하기 위해 간단한 PoC SVG를 업로드했습니다:
다음 경로를 통해 파일에 접근했습니다:
/api/file/download.php?…/api/file/share.php?token=…그 결과 alert()가 실행되었습니다. 기존 GHSA-qrcv-vjvf-fr29 완화 조치(UI 미리보기 차단)는 이러한 엔드포인트에 직접 접근함으로써 우회되었습니다.
alert()는 PoC일 뿐이므로, 페이로드가 내부 API와 상호 작용하도록 하여 실제 영향을 테스트했습니다.
사용된 테스트 페이로드:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">
fetch('/api/upload/upload.php')
.then(response => response.text())
.then(data => alert('API Response: ' + data));
</script>
</svg>
로그인한 관리자가 이 SVG가 포함된 공유 링크를 열면 스크립트가 실행되어 인증된 API 요청을 만들었습니다. 관찰된 영향은 다음과 같습니다:
{"csrf_expired":true,"csrf_token":"..."})테스트 중 입증된 영향 분류:
이 문제를 비공개로 보고했습니다. 관리자는 여러 차례의 점진적 수정을 배포했습니다:
v2.6.0 → v2.7.0 동안 공유 링크 엔드포인트는 계속해서 인라인 렌더링과 스크립트 실행을 허용하는 방식으로 SVG를 제공했습니다. 아래의 근본 원인 분석은 이전 수정 사항들이 해당 벡터를 완전히 차단하지 못한 이유를 설명합니다.
근본 원인은 단일 누락 헤더가 아니라 shareFile()(컨트롤러) 내부의 제어 흐름 및 출력 순서로 인해 많은 실행 경로에서 보안 헤더가 적용되지 못한 것이었습니다. 두 가지 유형의 문제가 존재했습니다:
exit; 지점.awk 스캔을 사용하여 readfile() 호출까지 shareFile() 내의 header() 및 exit; 발생 지점을 나열했습니다.
명령: awk '/function shareFile(/ {flag=1} /readfile(/ {flag=0} flag && /(header|exit;)/ {printf "%4d | %s\n", NR, $0}' src/controllers/FileController.php
관찰된 출력(내 실행 결과에서 발췌):
1649 | header('Content-Type: application/json; charset=utf-8'); 1651 | exit; 1657 | header('Content-Type: application/json; charset=utf-8'); 1659 | exit; 1664 | header('Content-Type: application/json; charset=utf-8'); 1666 | exit; 1670 | header("Content-Type: text/html; charset=utf-8"); 1693 | exit; 1699 | header('Content-Type: application/json; charset=utf-8'); 1701 | exit; 1719 | header('Content-Type: application/json; charset=utf-8'); 1721 | exit; 1725 | header('Content-Type: application/json; charset=utf-8'); 1727 | exit;
보안 헤더(강화 로직)는 ~1743행에서 시작됩니다:
1743 | header('X-Content-Type-Options: nosniff'); ... 1770 | header("Content-Disposition: attachment; ...");
함수는 많은 경로에서 더 일찍 헤더를 내보내고 exit;를 수행하므로, 해당 요청들은 Content-Disposition, nosniff, 또는 제한적인 타입을 설정하는 강화 코드에 도달하지 못했습니다.
비밀번호로 보호된 공유 흐름에서 함수는 비밀번호 프롬프트 HTML을 조기에 내보냈습니다:
if (!empty($record['password']) && empty($providedPass)) { header("Content-Type: text/html; charset=utf-8"); ... exit; }
이 경로는 Content-Type: text/html을 보내고 SVG 강화 로직 전에 종료되므로, 비밀번호가 제공되지 않은 비밀번호 보호 공유의 경우 브라우저에서 인라인 렌더링이 발생합니다.
취약점은 비밀번호 보호 흐름에만 국한되지 않았습니다. 내 테스트에서 비밀번호 없는 공유 요청도 text/html을 반환했습니다:
명령: curl -svI "http://127.0.0.1:8080/api/file/share.php?token=437d7913884ace4b94fab8ce745a686a" 2>&1 | grep -iE "content-type"
관찰 결과: < Content-Type: text/html; charset=UTF-8
이는 일반적인(비밀번호 없음) 경우에도 응답이 text/html이며 SVG가 인라인으로 렌더링된다는 것을 확인합니다.
헤더 강화 전에 발생한 PHP 경고를 포함하는 공유 엔드포인트의 원시 fetch를 캡처했습니다. 스냅샷(발췌):
명령: curl -s "http://127.0.0.1:8080/api/file/share.php?token=437d7913884ace4b94fab8ce745a686a" | head -n 30
관찰된 원시 출력(발췌):
Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in /data/data/com.termux/files/home/FileRise/src/controllers/FileController.php on line 1644
이러한 경고는 헤더 강화 전에 출력(더 이상 사용되지 않는 알림)이 생성되어, 해당 실행에서 이후의 header() 호출이 적용될 수 없음을 보여줍니다.
nosniff를 설정하는 강화 코드는 존재했지만, 조기 종료와 출력으로 인해 많은 코드 경로에서 도달되지 않았습니다.근본 원인 보고 후 관리자는 제어 흐름과 출력 순서를 해결하는 변경 사항을 적용했습니다. v2.7.1에서:
exit; 지점이 수정/처리되었습니다.최종 검증(v2.7.1에서 내 테스트):
명령: curl -svI "http://127.0.0.1:8080/api/file/share.php?token=fc911e48b0a30e9417a9020ef959784d" 2>&1 | grep -iE "content-type|content-disposition"
관찰 결과: < Content-Type: application/octet-stream < Content-Disposition: attachment; filename="xss-image.svg"; filename*=UTF-8''xss-image.svg
결과: 브라우저가 다운로드를 강제했습니다. SVG는 인라인으로 렌더링되지 않았고 XSS 페이로드도 실행되지 않았습니다. 내 환경에서 v2.7.1이 문제를 해결한 것으로 간주합니다.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:L — 점수: 9.6(Critical)
이 근거를 권고 스레드에 문서화하고 PR:N 사용을 요청했습니다.
CNA의 CVSS 벡터는 제한된 위협 모델을 반영합니다.
실증 테스트는 더 심각하고 재현 가능한 악용 경로를 보여주며, 이는 표준 점수 산정 규칙에 따라 더 높은 CVSS 3.1 기본 점수와 일치합니다.
독립적인 평가자는 이 문서에 설명된 관찰된 악용 조건을 사용하여 심각도를 평가하고, 공개된 심각도가 좁게 범위가 지정된 기준선이 아닌 실제 영향을 반영하도록 하는 것이 좋습니다.
1649 | header('Content-Type: application/json; charset=utf-8'); 1651 | exit; 1657 | header('Content-Type: application/json; charset=utf-8'); 1659 | exit; 1664 | header('Content-Type: application/json; charset=utf-8'); 1666 | exit; 1670 | header("Content-Type: text/html; charset=utf-8"); 1693 | exit; 1699 | header('Content-Type: application/json; charset=utf-8'); 1701 | exit; 1719 | header('Content-Type: application/json; charset=utf-8'); 1721 | exit; 1725 | header('Content-Type: application/json; charset=utf-8'); 1727 | exit;
보안 헤더는 ~1743행부터 시작합니다: 1743 | header('X-Content-Type-Options: nosniff'); ... 1770 | header("Content-Disposition: attachment; ...");
~/FileRise $ curl -svI "http://127.0.0.1:8080/api/file/share.php?token=437d7913884ace4b94fab8ce745a686a" 2>&1 | grep -iE "content-type" < Content-Type: text/html; charset=UTF-8
Deprecated: Constant FILTER_SANITIZE_STRING is deprecated in /data/data/com.termux/files/home/FileRise/src/controllers/FileController.php on line 1644
...그 다음 SVG 페이로드가 출력되고 인라인으로 렌더링됩니다.
~/FileRise $ curl -svI "http://127.0.0.1:8080/api/file/share.php?token=fc911e48b0a30e9417a9020ef959784d" 2>&1 | grep -iE "content-type|content-disposition" < Content-Type: application/octet-stream < Content-Disposition: attachment; filename="xss-image.svg"; filename*=UTF-8''xss-image.svg