
CVE-2026-5076에 대한 개념 증명(PoC) 익스플로잇으로, ARMember Premium에서 SQL 인젝션과 평문 비밀번호 재설정 키 저장을 악용해 인증 없이 관리자 계정을 탈취하는 방법을 시연합니다.
데이터베이스에 저장된 평문 비밀번호 재설정 키 + SQL 인젝션 = 완전한 관리자 계정 탈취
| 항목 | 세부 정보 |
|---|---|
| CVE ID | CVE-2026-5076 |
| 플러그인 | ARMember – Membership Plugin & Content Restriction |
| 영향받는 버전 | Premium <= 7.3.1 |
| 패치된 버전 | 7.3.2 |
| CVSS 점수 | 9.8 Critical |
| CWE | CWE-640: Weak Password Recovery |
| 유형 | 안전하지 않은 비밀번호 재설정 메커니즘 → 평문 키 저장 |
| 공격 벡터 | 네트워크 / 원격 / 비인증 (SQLi 체인 활용) |
| 활성 설치 수 | 30,000+ (Premium) |
| 발견자 | Wordfence Threat Intelligence |
| 공개 날짜 | 2026년 6월 3일 |
| CVE | 유형 | 심각도 |
|---|---|---|
| CVE-2026-5076 | 안전하지 않은 비밀번호 재설정 — 평문 키 저장 | 9.8 Critical |
| CVE-2026-5073 | 비인증 SQL 인젝션 (ORDER BY) | 9.8 Critical |
| CVE-2026-5074 | 비인증 SQL 인젝션 (WHERE) | 9.8 Critical |
WordPress 플러그인 ARMember Premium <= 7.3.1에는 세 가지 치명적인 취약점이 있으며, 이를 함께 연결하면 인증 없이 관리자 계정을 완전히 탈취할 수 있습니다:
wp_usermeta (arm_reset_password_key)에 저장됨arm_directory_paging_action()의 order 파라미터에서 발생하는 SQL 인젝션arm_directory_paging_action()의 filter 파라미터에서 발생하는 SQL 인젝션CVE-2026-5076의 직접적인 영향: SQLi, 백업 노출 등을 통해 데이터베이스에 읽기 접근 권한을 가진 누구나 비밀번호 재설정 키를 평문으로 읽은 뒤, 크래킹 없이 즉시 임의 계정의 비밀번호를 재설정하는 데 사용할 수 있습니다.
표준 WordPress는 비밀번호 재설정 키를 user_activation_key 열에 wp_hash()를 사용해 해시된 형태로 저장합니다. ARMember는 동일한 키의 사본을 wp_usermeta에 meta_key arm_reset_password_key로 저장하지만 — 평문으로 저장합니다:```php
// FILE: armember-membership/core/class.arm_member_forms.php
// Fungsi: arm_lost_password_action()
// WordPress menyimpan HASHED key (aman) $key = wp_generate_password(20, false); $wp_key = $wpdb->get_var( $wpdb->prepare("SELECT user_activation_key FROM $wpdb->users WHERE user_login=%s", $user_login) );
// ARMember menyimpan PLAINTEXT key (VULNERABLE!) update_user_meta($user_id, 'arm_reset_password_key', $wp_key); // ^^^^^^ // Ini adalah key ASLI yang bisa langsung dipakai
**심각한 문제**: 여기서 `$wp_key`는 `wp_generate_password(20, false)`에 의해 생성된 키 — 20자 영숫자(alphanumeric)입니다. WordPress는 이 키를 `user_activation_key`에 저장하기 전에 해시하지만, ARMember는 이를 **해시하기 전에** 저장하거나 **해시되지 않은** 별도의 복사본을 저장합니다.
### 근본 원인 #2: 키 영속성 버그
`get_password_reset_key()`가 호출되면(WordPress 코어), 새 키가 생성되고 해시됩니다. 그러나 이 함수는 `arm_reset_password_key`를 **업데이트하지 않습니다**:```php
// WordPress core: get_password_reset_key($user)
// - Generate key baru
// - Hash key → simpan di user_activation_key
// - Return key plaintext
// - TAPI: arm_reset_password_key TIDAK diupdate!
결과적으로, 이전 평문 키는 비밀번호 재설정 후에도 영원히 arm_reset_password_key에 저장된 채 남아 있습니다. 이 키는 메타 키가 명시적으로 삭제될 때까지 반복해서 사용될 수 있습니다.
AJAX 핸들러 arm_directory_paging_action()는 arm_check_user_cap()을 통한 nonce 검사를 수행하지만, order 및 filter 매개변수는 검증 없이 SQL 쿼리로 직접 전달됩니다:```php
// FILE: armember-membership/core/class.arm_member_forms.php
// Fungsi: arm_directory_paging_action()
// Nonce check (dibutuhkan nonce valid) $nonce_check = $this->arm_check_user_cap();
// ORDER BY injection — langsung ke SQL tanpa sanitasi! $orderby = "u.{$arm_member} {$order_dir}"; // $order_dir dari $_POST['order'] → LANGSUNG ke ORDER BY
// WHERE injection via filter if (!empty($filter)) { $where .= " AND " . $filter; // ← LANGSUNG concatenation! }
**ORDER BY 익스플로잇**: `order` 매개변수가 SQL의 `ORDER BY` 절에 삽입됩니다. 입력값 검증이 없으므로 공격자는 하위 쿼리를 주입할 수 있습니다:```sql
-- Payload SQLi via parameter order
ORDER BY u.ID ASC, IF(COND, 1, EXP(710))
-- COND = TRUE → IF returns 1 → ORDER BY 1 → response normal (besar)
-- COND = FALSE → IF returns EXP(710) → MySQL overflow ERROR → response 90B
이 Oracle은 네트워크 레이턴시(latency)에 면역입니다. 왜냐하면 응답 시간이 아니라 오류(error)와 성공(success)에 따라 TRUE/FALSE를 구분하기 때문입니다.
╔══════════════════════════════════════════════════════════════════════════════════╗ ║ CVE-2026-5076 FULL CHAIN ATTACK ROADMAP ║ ║ ARMember Premium <= 7.3.1 → Unauthenticated Admin Takeover ║ ╚══════════════════════════════════════════════════════════════════════════════════╝