An exploitable vulnerability exists in the generation of authentication token functionality of Circle with Disney. Specially crafted network packets can cause a valid authentication token to be returned to the attacker resulting in authentication bypass. An attacker can send a series of packets to trigger this vulnerability.
Circle with Disney 2.0.1
8.1 - CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-307: Improper Restriction of Excessive Authentication Attempts
Circle with Disney is a network device used to monitor internet use of children on a given network.
When making any requests to the Circle, an authenticated token must be provided. To request a token, a client specifies an
appid, a unique string used to identify the client, as well as a
SHA1 hash to verify the client should have access to the device. One secret piece of information is a 4 digit pin. The
hash is calculated by the following:
hash = SHA1(appid + pin)
The client provides both the
hash. Because the key space for the
pin is only
10000, an attacker can brute force this pin to retrieve an authentication token. With the authentication token in hand, an attacker can make available API calls.
Circle implements a lockout mechanism that allows to test only 3 pins every 15 minutes. This is implemented by the
apid binary, which writes to the file
/tmp/failed_token_info the number of authentication attempts and the latest timestamp:
This resource is saved in
/tmp, which is a "tmpfs" directory shared by many components of the system and has a size of 30MB.
If no failed authentication attempts are made since system boot,
failed_token_info won't exist in
/tmp. This means that if an attacker is able to keep
/tmp completely filled, the
apid binary won't ever be able to create
failed_token_info, thus making the pin bruteforce possible in practice.
Moreover note that if a DoS attack is available for rebooting the device, it can be used to clear the whole
/tmp directory. Since
failed_token_info is never saved in non-volatile storage, a slow bruteforce can be performed by testing 3 pins per reboot.
Without using a DoS, an attacker needs to fill
/tmp before starting the bruteforce.
To achieve this, a combination of 4 techniques can be used.
Once every hour,
/mnt/shares/usr/bin/firmware_updater.sh is invoked to check for updates. This script downloads a file containing the latest version of the firmware via HTTP and stores it in
... /tmp/wget -q -O /tmp/versions "http://download.meetcircle.co/dev/firmware/check_version.php?DEVID=$MAC&FVER=$my_firmware_ver&UVER=$my_updater_ver&DBVER=$my_database_verÐ=$eth_connected&IP=$IP$EXTRA" ...
By exploiting this behavior, an attacker can send a 31MB versions file and fill
Unfortunately, since many components are using
failed_token_info might still be written after some time: the main problematic file is
/tmp/iplist, which can shrink depending on the network activity.
This is why the techniques described in the following paragraphs are needed as well.
apid provides a way for restoring a previously-saved configuration and for upgrading the firmware. This can be done using the api command
/api/CONFIG/restore, which is handled in function
sub_417528. At high level it works as follows:
if query == "/api/CONFIG/restore" or (query == "/api/UPLOAD_FIRMWARE" and substr(srcip, 0, 10) == "10.123.234") save_postfilebin() if check_token() ...do update/restore...
save_postfilebin() will save the uploaded firmware (or configuration file) to
/tmp/postfile.bin. As we can see, this function is called before checking if the user supplied a valid token, allowing any unauthenticated user to upload such file. Note that even if the token turns out to be wrong, the file won't be deleted.
The size limit of the upload is almost 5MB, so it cannot be used alone to fill
arp2 binary keeps a list of recently-seen hosts on the network by monitoring ARP requests and saves them in
When operating on the file,
fopen with mode "w+". This means that as soon as
fopen returns the
/tmp/iplist file will be truncated.
If, at the same time,
apid tries to create
failed_token_info, it will succeed since in that instant there will be some free space in
To avoid space to be freed by
iplist, it's important that the bruteforce attack only starts when
iplist has a size of 0, so it won't ever be able to grow.
To do this, an attacker could upload a
/tmp/postfile.bin file (to try to fill the little space left in
/tmp) in the same moment that
iplist gets truncated by
In order to maximize the likelihood of this condition, a series of spoofed ARP requests can be continuously sent to the device, forcing it to update
iplist, while in parallel uploading a rather small configuration file.
It's useful, during bruteforce, to know whether pin attempts are discarded because of the lockout mechanism. Indeed, it's possible to get this kind of feedback. At high level this is a simplification of how the token request procedure works:
if (failed_token >= 3 and time() - failed_token_time < 15 * 60) return "token request failure" else appid = get_param(query, "appid") if not appid: return "token request failure - no app id specified" if not good_pin(pin): return "token request failure" else return new_token()
As we can see, if a token request is sent without an
appid and requests are not locked out, the error returned is "token request failure - no app id specified". Note however that in this case pins are not tested.
This feedback can be used every once in a while, just to ensure that pins are effectively verified during bruteforce.
A complete attack sequence could go like this:
1- impersonate the `download.meetcircle.co` server, e.g. via MITM. 2- listen on port 80 for a GET request of "/dev/firmware/check_version.php" and return a big file (> 30MB). 3- in parallel, for about 500 POST requests: A- continuously send spoofed ARP packets with different IP source, to trigger `iplist` updates. B- continuously send POST requests for writing `postfile.bin`. 4- `/tmp` should be completely full by now. 5- bruteforce each possible PIN and every 10 attempts check whether requests have been locked.
2017-07-13- Vendor Disclosure
2017-10-31 - Public Release
Discovered by Cory Duplantis, Yves Younan, Marcin 'Icewall' Noga, Claudio Bozzato, Lilith Wyatt <(^_^)>, Aleksandar Nikolic, and Richard Johnson of Cisco Talos.