[하루한줄] CVE-2024-22263: Spring Cloud Dataflow의 Path Traversal으로 인한 임의 파일 쓰기 취약점
URL
https://blog.securelayer7.net/spring-cloud-data-flow-exploit/
Target
- Spring Cloud Dataflow < 2.11.3
Explain
Cloud Foundry/Kubernetes에서 스트리밍 및 데이터 처리 파이프라인 구축을 위한 마이크로서비스 기반 툴킷인 Spring Cloud Dataflow 에서 임의 파일 쓰기 취약점이 발견되어 세부 정보가 공개되었습니다.
취약점은 패키지 업로드 요청을 처리하는 Skipper 서버 구성 요소인 **PackageService.java
모듈의 메서드에 존재합니다.
Skipper는 여러 클라우드 플랫폼에서 Spring Boot 애플리케이션의 라이프사이클을 관리할 수 있는 툴임.
/api/package/upload
엔드포인트의 Skipper 서버 API를 통해 임의의 경로를 대상으로 하는 업로드 요청을 보낼 수 있습니다.
@Transactional
public PackageMetadata upload(UploadRequest uploadRequest) {
validateUploadRequest(uploadRequest);
upload
메서드는 공격자의 요청에 포함된 UploadRequest
를 validateUploadRequest
을 호출해 검증합니다.
private void validateUploadRequest(UploadRequest uploadRequest) {
Assert.notNull(uploadRequest.getRepoName(), "Repo name can not be null");
Assert.notNull(uploadRequest.getName(), "Name of package can not be null");
Assert.notNull(uploadRequest.getVersion(), "Version can not be null");
// Other checks...
}
validateUploadRequest
메서드는 패키지 파일이 비어있는지 확인하는 null 검사를 수행합니다.
Path packageFile = Paths.get(packageDir.getPath() + File.separator + uploadRequest.getName() + "-" + uploadRequest.getVersion() + "." + uploadRequest.getExtension());
validation 이후 upload
메서드에서는 공격자의 입력을 사용해 packgeFile
경로를 구성해 해당 경로에 패키지파일을 구성합니다.
위 과정에서 다음과 같은 검증 부족이 존재합니다.
validateUploadRequest(uploadReqeust)
검증 과정에서,uploadRequest
의 실제 경로가 파일시스템에 생성되기 전에 검증을 진행하므로 실제 파일 경로를 검증할 수 없음validateUploadRequest
메서드는 null 패키지 검사 외 path traversal check와 같은 검증 로직이 존재하지 않음Path packageFile
구성에서 유저 인풋인uploadRequest
에 대한 검증 없이 구성함.
공격자는 name 필드에 path traversal 페이로드를 포함하는 uploadRequest 요청을 보내 임의의 경로에 파일을 쓸 수 있습니다.
uploadRequest = {
"repoName": repoName,
"name": "../../poc",
"version": version,
"extension": "zip",
"packageFileAsBytes": packageFileAsBytes
}
취약점의 패치는 다음과 같이 이루어졌습니다.
validateUploadRequest(uploadReqeust)
호출 전 업로드 프로세스 중 사용될 파일 경로를 생성함.@Transactional public PackageMetadata upload(UploadRequest uploadRequest) { Path packageDirPath = TempFileUtils.createTempDirectory("skipperUpload"); validateUploadRequest(packageDirPath, uploadRequest);
validateUploadRequest 메서드에서는 실제 파일 경로를 확인하고,
canonicalDestinationFilepath
가 디렉토리의 표준 경로인canonicalDestinationDirPath
로 시작하는지 확인하는 것으로../../
와 같은 path traversal 페이로드를 필터링함.private void validateUploadRequest(Path packageDirPath, UploadRequest uploadRequest) throws IOException { // Existing null checks... File destinationFile = new File(packageDirPath.toFile(), uploadRequest.getName().trim()); String canonicalDestinationDirPath = packageDirPath.toFile().getCanonicalPath(); String canonicalDestinationFile = destinationFile.getCanonicalPath(); if (!canonicalDestinationFile.startsWith(canonicalDestinationDirPath + File.separator)) { throw new SkipperException("Entry is outside of the target dir: " + uploadRequest.getName()); } }
요청 경로를 trim()을 통해 sanitization하는 것으로 기타 파일 경로 조작 등을 방지함.
String fullName = uploadRequest.getName().trim() + "-" + uploadRequest.getVersion().trim() + "." + uploadRequest.getExtension().trim(); Path packageFile = Paths.get(packageDir.getPath() + File.separator + fullName);
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.