Talos Vulnerability Report


Milesight MilesightVPN requestHandlers.js detail_device cross-site scripting (XSS) vulnerabilities

July 6, 2023
CVE Number



Cross-site scripting (xss) vulnerabilities exist in the requestHandlers.js detail_device functionality of Milesight VPN v2.0.2. A specially-crafted HTTP request can lead to arbitrary Javascript code injection. An attacker can send an HTTP request to trigger these vulnerabilities.


The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Milesight VPN v2.0.2


MilesightVPN - https://www.milesight-iot.com/milesightvpn/


4.7 - CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N
6.1 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N - chain: TALOS-2023-1702


CWE-80 - Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)


The MilesightVPN is a software that make easier the setup of VPN tunnel for the Milesight products and allow to monitor the connection status with a web server interface.

The MilesightVPN exposes the /Device_Auth API used to authenticate to the server and to get the an OpenVPN configuration file. This API is for the various Milesight devices, the API requires as data the serial number, the authentication code, the device name and the subnet of the device. Essentially the API expects four entries in the POST payload:

  • authcode: is a secret generated by the MilesightVPN
  • subnet: the subnet mask of the main network used by the device
  • device_name: the identifier of the device that is connecting to the MilesightVPN server
  • sn: is the serial number of the device that is connecting to the MilesightVPN server

After been registered, the device will appear in the Device table showing the provided information. This Device table is in the landing page after the login.

Following the relevant portion of the Embedded JavaScript template related to the device table:

<table class="table table-no-bordered" data-striped="true" data-height="100%" id="tbl_content" data-toggle="table" data-url="/detail_device" data-target="/detail_device" data-id-field="sn" data-pagination="true" data-sort-stable="true" data-sort-name="connect_time" data-sort-order="asc" data-toolbar="#deviceToolbar" data-search="true" data-search-on-enter-key="true" data-cache="false">
            <th data-field="name" data-width="15%" data-sortable="true" data-formatter="name_formatter"><%=lang.detail.device.name%></th>
            <th data-field="status" data-width="10%" data-sortable="true" data-formatter="status_formatter"><%=lang.detail.device.status%></th>
            <th data-field="sn" data-width="10%" data-sortable="true"><%=lang.detail.device.sn%></th>
            <th data-field="virtual_ip" data-width="15%" data-sortable="true"><%=lang.detail.device.virtualip%></th>
            <th data-field="real_ip" data-width="15%" data-sortable="true"><%=lang.detail.device.realip%></th>
            <th data-field="remote_subnet" data-width="15%" data-sortable="true" data-formatter="subnet_formatter"><%=lang.detail.device.subnet%></th>
            <th data-field="connect_time" data-width="15%" data-sortable="true" data-formatter="time_formatter"><%=lang.detail.device.time%></th>
            <th data-field="history" data-width="10%" data-formatter="history_formatter"><%=lang.detail.device.history%></th>

The data of this table is filled using the requestHandlers.js’s detail_device function:

function detail_device(res,postdata,connection){
    var $sql="select * from device";
    $sql+=' left join ';
    $sql+=' (select count(*) as total,remote_subnet from device group by remote_subnet) repeatsubnet on repeatsubnet.remote_subnet=device.remote_subnet';
    var result={};
                res.writeHead(200,{'Content-Type':'application/json','Content-length':Buffer.byteLength(result, 'utf8')});

The device table has the following schema:

| Field         | Type         | Null | Key | Default | Extra |
| name          | varchar(255) | YES  |     | NULL    |       |
| sn            | varchar(12)  | NO   | PRI | NULL    |       |
| virtual_ip    | varchar(15)  | YES  |     | NULL    |       |
| real_ip       | varchar(32)  | YES  |     | NULL    |       |
| remote_subnet | varchar(32)  | YES  |     | NULL    |       |
| connect_time  | int          | YES  |     | NULL    |       |
| status        | int          | YES  |     | NULL    |       |

The device table is populated through the registered devices, so through the /Device_Auth API.

From when a Milesight device is registered, using the /Device_Auth API, until showing its data in the web interface, no checks about the data are performed, this can lead an XSS vulnerability. An attacker can upload malicious Javascript code through the /Device_Auth API, registering a device. An admin of the server would execute this malicious javascript whenever they viewed the details page due to the stored XSS. An attacker would need to know the Authorization Code of the server to actually use the /Device_Auth API. But because TALOS-2023-1702 this information can be easily retrieved by an attacker.

CVE-2023-24496 - XSS in the name field

An attacker can upload malicious Javascript code through the /Device_Auth’s device_name parameter. The device_name value will be stored in the device database as the name field.

CVE-2023-24497 - XSS in the remote_subnet field

An attacker can upload malicious Javascript code through the /Device_Auth’s subnet parameter. The subnet value will be stored in the device database as the remote_subnet field.


Since the maintainer of this software did not release a patch during the 90 day window specified in our policy, we have now decided to release the information regarding this vulnerability, to make users of the software aware of this problem. See Cisco’s Coordinated Vulnerability Disclosure Policy for more information: https://tools.cisco.com/security/center/resources/vendor_vulnerability_policy.html


2023-02-14 - Initial Vendor Contact
2023-02-21 - Vendor Disclosure
2023-07-06 - Public Release


Discovered by Francesco Benvenuto of Cisco Talos.