Skip to content

Instantly share code, notes, and snippets.

@nonamed01
Last active April 27, 2024 00:45
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save nonamed01/0961d8a79955206ebdc00abcaa56aefe to your computer and use it in GitHub Desktop.
Save nonamed01/0961d8a79955206ebdc00abcaa56aefe to your computer and use it in GitHub Desktop.
fuckForticlient, a command-line client to connect to SAML fortivpn servers by using openfortivpn and the --cookie-in-stdin parameter
#!/bin/bash
# Uncomment the following line to debug the script:
#set -x
#####################################################################################
# fuckForticlient.sh
#
# Script to authenticate against Fortinet SAML servers using Firefox and
# openfortivpn. This replaces Forticlient for GNU/Linux completely.
# Because openfortivpn does not support SAML login (yet), this script uses Firefox
# to authenticate, grabs SVPNCOOKIE and then calls openfortivpn to setup
# the VPN service.
#
# See:
# https://github.com/adrienverge/openfortivpn/pull/1042#issuecomment-1344211491
#
# 2022-2023 by Toni Castillo Girona (@Disbauxes)
#
# INSTALLATION
# > First, install the following packages:
# sudo apt-get install liblz4-dev lz4json jq firefox-esr git inotify-tools \
# libssl-dev autoconf make gcc pkg-config
# > Next, install all the dependencies to build openfortivpn:
# sudo apt-get build-dep openfortivpn
#
# Please notice that if your OS provides an openfortivpn package with
# support for the "--cookie-on-stdin" parameter, you do not need to
# install these dependencies or build openfortivpn from scratch.
#
# > Now clone and build openfortivpn:
# git clone https://github.com/adrienverge/openfortivpn.git
# cd openfortivpn
# ./autogen.sh
# ./configure && make
# > And finally, install it system-wide:
# sudo make install
#
# The user running this script must belong to the sudo group. In case they
# don't, run:
# sudo usermod -aG sudo USER
#
# HOW IT WORKS
# 1. Opens firefox and navigates to https://${SERVER}/remote/login
# 2. After a succesful authentication, SVPNCOOKIE
# is saved to sessionstore-backups/recovery.jsonlz4 on Firefox's profile.
# 2. The script will use openfortivpn to start the tunnel providing it
# with SVPNCOOKIE (--cookie-on-stdin < cookie_file) because
# it does not support SAML (yet!).
#
# NOTES
# > Firefox will store cookies in sessionstore-backups/recovery.jsonlz4 only when
# the following options within "Privacy&Security/History" are not enabled:
# "Always use private(...)" and "Clear history when Firefox closes".
# > SVPNCOOKIE is saved into ~/.${USER}.svpncookie with 0600 perms and removed
# when exiting the script (thanks to trap).
# > Remember, this script is a HACK. So if something does not work for you,
# change whatever you think needs to be changed and please, READ these notes
# BEFORE assuming that it won't work at all!
# > This script has been tested on some OS but not all. And remember that, for
# some systems, custom configurations of Firefox may affect this script in
# different ways. So please UNDERSTAND how this script works and then adjust
# whatever you think you need to adjust to make it work.
# > Tested on: Raspbian 11; Debian 10, 11; Ubuntu/Kubuntu 20.04,22.04.
#
# USAGE
# Run the script like this to start a new authentication process against
# a SAML server and get the VPN up and running automatically:
#
# ./fuckForticlient.sh -S vpnserver -c
#
# In case you already have a valid non-expired SVPNCOOKIE, you can re-use
# it like this:
#
# ./fuckForticlient.sh -S myvpnserver -s
#
# Once the vpn is up and running, you can close the terminal and the
# connection will hold; you can disconnect by pressing CTRL+c at any
# time. If you close the terminal and the connection is still alive,
# you can kill it with:
#
# sudo kill `pidof openfortivpn`
#
# Run the script with "-h" to get a list of valid options and some
# running examples.
#
#####################################################################################
VERSION="1.1"
AUTHOR="Toni Castillo Girona"
EMAIL="toni.castillo@upc.edu"
TWITTER="@Disbauxes"
# Fortinet VPN SAML server, replace with your own or use -S SERVER
# E.g: /fuckForticlient.sh -S myvpnserver.domain.org -c
SERVER=""
# Default timeout in seconds to wait for the SVPNCOOKIE to appear:
TIMEOUT=120
# Options to pass to the firefox browser (it only applies when there's no
# previous Firefox instance already running):
OPTIONS="--window-size 150,150"
# By default, we don't show the SVPNCOOKIE on screen:
SHOWCOOKIE=0
# We get the current distro:
DISTRO=`lsb_release -i|cut -d":" -f2|tr -d '\t'`
DISTROV=`lsb_release -r|awk '{print $2}'`
# No debug by default (use -D to change it)
DEBUG=0
#####################################################################################
# Colors
#####################################################################################
clRed='\033[1;31m'
clYellow='\033[1;33m'
clGreen='\033[1;32m'
clNone='\033[0m'
#####################################################################################
# cleanup()
#####################################################################################
cleanup(){
# Removes SVPNCOOKIE:
test -r $HOME/.${USER}.svpncookie && rm $HOME/.${USER}.svpncookie
test -d /tmp/openfortivpn && rm -rf /tmp/openfortivpn
}
#####################################################################################
# banner()
# Shows the banner ;-)
#####################################################################################
banner (){
echo " ___ __ ____ __ _ ___ __ "
echo " / _/_ ______/ /__ / __/__ ____/ /_(_)___/ (_)__ ___ / /_"
echo " / _/ // / __/ '_// _// _ \/ __/ __/ / __/ / / -_) _ \/ __/"
echo -e "/_/ \_,_/\__/_/\_\/_/ \___/_/ \__/_/\__/_/_/\__/_//_/\__/ ${clRed}v${VERSION}"
echo ""
echo -e "${clYellow}2022-2023 by: $AUTHOR ($TWITTER)"
echo -e "${clNone}"
}
#####################################################################################
# getProfilePath()
# Gets the Firefox profile path or returns an error. Depending on the distro,
# sometimes the profile path is not on the usual path (like with Ubuntu because
# it uses SNAPd)
#####################################################################################
getProfilePath(){
profilepath=""
case $DISTRO in
Debian|Raspbian|Parrot)
if [ ! -d ${HOME}/.mozilla/firefox ]; then
return -1
fi
profilepath="${HOME}/.mozilla/firefox"
;;
Ubuntu)
if [ "$DISTROV" != "20.04" ]; then
if [ ! -d ${HOME}/snap/firefox/common/.mozilla/firefox ]; then
return -1
fi
profilepath="${HOME}/snap/firefox/common/.mozilla/firefox"
else
if [ ! -d ${HOME}/.mozilla/firefox ]; then
return -1
fi
profilepath="${HOME}/.mozilla/firefox"
fi
;;
*)
# We return the usual path, but who knows if it even works at all!
profilepath="${HOME}/.mozilla/firefox"
;;
esac
echo "${profilepath}"
return 0
}
#####################################################################################
# enumerateProfiles()
# Enumerates all profiles available by reading profiles.ini. This can be used
# to determine which profile to use to override the automatic detection of the
# default profile with -p.
#####################################################################################
enumerateProfiles(){
profilepath=`getProfilePath`
if [ $? -eq 0 ]; then
profs=`cat ${profilepath}/profiles.ini|grep Path|cut -d"=" -f2|xargs`
for p in ${profs}; do
pName=`echo ${p}|cut -d"." -f2`
echo -e " [>] Name : ${clGreen}${pName}"
echo -en "${clNone}"
echo -e " [>] Path (-p): ${clGreen}${profilepath}/${p}"
echo -en "${clNone}"
echo ""
done
else
echo "[!] Unable to determine Firefox profile PATH!!!"
return -1
fi
}
#####################################################################################
# getFirefoxProfile()
# Returns the path for the default Firefox profile or "" if it cannot determine
# where it is. Using -p overrrides this function.
# If the user has decided to write the profile-path to use right in the file
# ~/.${USER}.fuckforticlient-profile, this functions simply returns the contents
# of ~/.${USER}.fuckforticlient-profile.
#####################################################################################
getFirefoxProfile(){
# Do we have a saved profile to use?
if [ -r ~/.${USER}.fuckforticlient-profile ]; then
profilep=`cat -v ~/.${USER}.fuckforticlient-profile`
# If it's not empty and the directory is valid:
if [ ! -z "${profilep}" -a -d "${profilep}" ]; then
echo "${profilep}"
return 0
fi
fi
# First, we get the profile path depending on the Distro:
profilepath=`getProfilePath`
if [ $? -eq 0 ]; then
prof=`cat ${profilepath}/profiles.ini |grep Path=|grep -i default|tail -1|cut -d"=" -f2`
if [ ! -z "$prof" ]; then
echo "${profilepath}/${prof}/sessionstore-backups"
return 0
else
echo ""
return -2
fi
else
echo ""
return -2
fi
}
#####################################################################################
# getCookie(profile,waitForIt)
# Waits up to TIMEOUT seconds for the SVPNCOOKIE to appear. Stores the cookie in
# $HOME/${USER].svpncookie and returns 0; returns 1 otherwise.
#####################################################################################
getCookie(){
# Storage file where the cookie is stored in firefox:
storage="$1"
waitForIt=$2
# We save the current umask value first:
curUmask=`umask`
# We change it to 0077:
umask 077
# We try to grab the cookie right away:
c=`lz4jsoncat ${storage}/recovery.jsonlz4 2>/dev/null|jq '.cookies[]|select(.name!=null)|select(.name|contains("SVPNCOOKIE"))|.value'`
if [ ! -z "$c" ]; then
echo "SVPNCOOKIE=${c}" > $HOME/.${USER}.svpncookie
sed -i 's/\"//g' $HOME/.${USER}.svpncookie
# We restore umask:
umask $curUmask
return 0
fi
# We only wait if waitForIt == 1
test $waitForIt -eq 0 && return 1
# We will wait until the cookie is already there...
while inotifywait -e modify -t $TIMEOUT --format '%w%f' ${storage} -q >/dev/null 2>&1;
do
if [ -r ${storage}/recovery.jsonlz4 ]; then
# Make sure we check for the cookie once the file changes:
c=`lz4jsoncat ${storage}/recovery.jsonlz4 2>/dev/null|jq '.cookies[]|select(.name!=null)|select(.name|contains("SVPNCOOKIE"))|.value'`
if [ ! -z "$c" ]; then
#echo ${c}
echo "SVPNCOOKIE=${c}" > $HOME/.${USER}.svpncookie
sed -i 's/\"//g' $HOME/.${USER}.svpncookie
# We restore umask
umask $curUmask
return 0
fi
fi
done
# If we reach this, we exit with error (timeout!):
# We restore umask:
umask $curUmask
return 1
}
#####################################################################################
# usage()
# Shows the help screen and some examples and exits
#####################################################################################
usage(){
echo ""
echo -ne "Usage: `basename $0` -L|-u|-d|[-p][-P][-t][-v][-S][-c][-s] \n" \
"\t-h Shows this help and exits.\n" \
"\t-c Opens firefox to perform SAML Authentication.\n" \
"\t-s Tries to re-use a previous SVPNCOOKIE.\n" \
"\t-p PATH Overrides the detection of the Firefox Profile to use.\n" \
"\t-P Saves chosen Firefox Profile (-p) as the default one.\n" \
"\t-t SECONDS Sets the timeout to wait for the SVPNCOOKIE cookie to SECONDS.\n" \
"\t-v Shows the SVPNCOOKIE cookie on screen.\n" \
"\t-S SERVER Authenticates against VPN server SERVER .\n" \
"\t-L Lists all Firefox profiles detected and exits.\n" \
"\t-d Removes Forticlient from the system and exits.\n" \
"\t-u Updates openfortivpn and exits.\n" \
"\t-i Shows current assigned VPN Ip address and exits.\n" \
"\t-D Runs the script in debug mode.\n" \
"Examples:\n" \
"\t`basename $0` -L \n" \
"\t`basename $0` -S myserver.org.edu -c\n" \
"\t`basename $0` -i\n" \
"\t`basename $0` -t 200 -S myvpnserver.com -c \n" \
"\t`basename $0` -p /home/u1/.mozilla/firefox/myprofile -S vpnsrv -c\n" \
"\t`basename $0` -p /home/u1/.mozilla/firefox/myprofile -P -S vpnsrv -c\n" \
"\t`basename $0` -p /home/u1/.mozilla/firefox/myprofile -S vpnsrv -s\n" \
"\t`basename $0` -t 200 -p /home/u1/.mozilla/firefox/myprofile -S vpnsrv -c\n" \
"\n" \
"Extra options for openfortivpn \n" \
"\t FUCKFORTICLIENT_OPTS=\"--op1 --op2 ...\" `basename $0` options ... \n" \
"Example:\n" \
"\tFUCKFORTICLIENT_OPTS=\"--no-dns\" `basename $0` -S myserver.org.edu -c\n"
exit 0
}
#####################################################################################
# sanityCheck()
# Performs some trivial sanity checks before attempting to run the script.
#####################################################################################
sanityCheck(){
# Test for jq presence:
type jq >/dev/null 2>&1|| return 1
# Test for lz4jsoncat
type lz4jsoncat >/dev/null 2>&1 || return 1
# Test for openfortivpn:
type openfortivpn >/dev/null 2>&1|| return 1
# Make sure openfortivpn supports "--cookie-on-stdin":
/usr/local/bin/openfortivpn --help|grep cookie >/dev/null|| return 1
# Make sure we have Firefox installed:
type firefox >/dev/null 2>&1 || return 1
# Make sure the user running this script belongs to the sudo group:
id -Gn |grep sudo >/dev/null || return 1
return 0
}
#####################################################################################
# checkFirefoxSettings()
# Makes sure the following two options ARE not enabled on the chosen
# firefox profile to ensure the SVPNCOOKIE cookie will be stored in the
# sessionstore-backups/recovery.jsonlz4 file:
#
# browser.privatebrowsing.autostart
# privacy.sanitize.pending
#####################################################################################
checkFirefoxSettings(){
# Now, if the sessionstore-backups directory DOES NOT EXIST at all,
# it is obvious these options are ALREADY ENABLED!
if [ ! -d "${fProfile}" ]; then
return 1
else
# Otherwise, make sure everything else fits:
grep -q "browser.privatebrowsing.autostart" "${fProfile}/../prefs.js"
# Not found, maybe the next option?
if [ $? -eq 1 ]; then
# sanitize pending should'nt have anything between "[]":
sa=`cat "${fProfile}/../prefs.js"|grep "privacy.sanitize.pending"|awk '{print $2}'|cut -d"\"" -f2`
if [ "${sa}" != "[]" ]; then
if [ "${sa}" != "[{\\" ]; then
return 1
else
return 0
fi
else
return 0
fi
fi
return 1
fi
}
#####################################################################################
# getVPNIp()
# Returns the current assigned VPN IP Address or error
# It assumes the VPN device used by openfortivpn is always "ppp0", quite naive!!
#####################################################################################
getVPNIp(){
data=`ip a list ppp0 2>/dev/null|grep "inet"`
if [ $? -eq 0 ]; then
echo ${data}|awk '{print $2}'
return 0
else
echo ""
return 1
fi
}
#####################################################################################
# checkAnotherInstance()
# Checks whether there's a running instance of openfortivpn. If there is,
# exits with error. Otherwise, returns 0.
#####################################################################################
checkAnotherInstance(){
pidof -q openfortivpn
if [ $? -eq 0 ]; then
echo -e "${clRed}[!] Another openfortivpn instance detected!${clNone}"
# If there is another instance running, it is probably because there is
# an active VPN connection running already:
ipvpn=`getVPNIp`
if [ $? -eq 0 ]; then
echo -e "[+] Current VPN IP: ${clGreen}${ipvpn}${clNone}"
echo -e "${clRed}[!] If you kill openfortivpn instance, you will be disconnected!${clNone}"
fi
echo -e "[!] Run: ${clGreen} sudo kill `pidof openfortivpn`${clNone} or just press CTRL+c on the terminal window..."
exit 1
fi
return 0
}
#####################################################################################
# checkOpenfortivpn()
# Returns 0 if the installed version of Openfortivpn is from the repo or 1
# otherwise. For some distros, the included openfortivpn from the official repos
# does not support "--cookie-on-stdin"
#####################################################################################
checkOpenfortivpn(){
dpkg -l |grep openfortivpn|grep -E "^ii" >/dev/null 2>&1
return $?
}
# Tiddy things up if we cancel or finish the script:
trap "cleanup" EXIT
# We show the banner:
banner
# We show the detected distro:
echo -e "[*] Detected distro: ${clRed}$DISTRO${clNone}, version: ${clRed}$DISTROV"
echo -ne "${clNone}"
# First of all, we perform a trivial sanity check:
sanityCheck
if [ ! $? -eq 0 ]; then
echo "[!] Sanity check reported an error."
echo "[!] Make sure you have installed all the pre-requisites first."
echo "[!] Make sure you belong to the sudo group also."
exit 0
fi
# We get Firefox default profile first thing:
fProfile=`getFirefoxProfile`
if [ ! $? -eq 0 ]; then
# Has the user provided us with a custom profile path?
args="$*"
if [[ "$args" != *"-p"* ]]; then
echo -e "${clRed}[!] Unable to determine the default firefox profile!...${clNone}"
echo "[+] Enumerating all profiles now...:"
# So we show all the detected profiles just in case:
enumerateProfiles
echo "[!] Please, re-run this script with the -p option! Aborting..."
exit 1
fi
else
echo -e "[*] Auto-detected firefox profile: ${clRed}$fProfile"
echo -ne "${clNone}"
fi
# If we have a valid Firefox profile (either by auto-detecting it or because the
# user has specified the -p and/or -P parameters, we make sure Firefox settings
# will save the cookie to the restore file before going further...
checkFirefoxSettings
if [ $? -eq 1 ]; then
echo -e "${clRed}[!] Error; make sure the following two options are disabled on Firefox"
echo -e "\t>Never Remember History"
echo -e "\t>Always use private browsing mode"
echo -e "\t>Clear history when Firefox closes"
echo -e "${clNone}[!] Go to ${clGreen}Preferences/Privacy&Security/History ${clNone}to fix it!"
echo "[!] Aborting now ... "
exit 1
fi
# We get openfortivpn version and show it:
opv=`openfortivpn --version`
echo -e "[*] Openfortivpn version: ${clRed}$opv"
echo -en "${clNone}"
# Is it installed from a repo or from github?
checkOpenfortivpn
if [ $? -eq 0 ]; then
echo -e "[*] Openfortivpn installed from: ${clRed}REPO"
else
echo -e "[*] Openfortivpn installed from: ${clRed}GITHUB"
fi
echo -en "${clNone}"
# Show any extra openfortivpn parameter:
if [ ! -z "$FUCKFORTICLIENT_OPTS" ]; then
echo -e "[*] Openfortivpn extra args: $clGreen$FUCKFORTICLIENT_OPTS "
echo -en "${clNone}"
fi
# Process arguments:
while getopts "Licshut:p:PvdDS:" opt; do
case "$opt" in
# Shows usage message and exits:
h)
usage
exit 0
;;
D)
echo -e "[*]${clYellow} Debug mode enabled!${clNone}"
DEBUG=1
set -x
;;
u)
# Clones the repository first:
echo "[*] Updating openfortivpn ... "
echo -e "\t[>] Cloning ..."
git clone https://github.com/adrienverge/openfortivpn.git /tmp/openfortivpn>/dev/null 2>&1
if [ $? -eq 0 ]; then
cd /tmp/openfortivpn >/dev/null 2>&1
echo -e "\t[>] Running autogen.sh ... "
./autogen.sh >/dev/null 2>&1
echo -e "\t[>] Running configure & make ..."
( ./configure && make )> /dev/null 2>&1
echo -e "\t[>] Running make install ... "
sudo make install >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo -e "[*] ${clGreen}openfortivpn updated sucessfully!"
echo -ne "${clNone}"
else
echo -e "[!] ${clRed}error updating openfortivpn."
echo -ne "${clNone}"
fi
# We clean the directory:
cd .. && rm -rf /tmp/openfortivpn >/dev/null 2>&1
else
echo -e "[!] ${clRed}Unable to clone openfortivpn!"
exit -1
fi
;;
# Shows current assigned VPN Ip address (if any) and exits:
i)
ipvpn=`getVPNIp`
if [ $? -eq 0 ]; then
echo -e "[+] Current VPN Ip: ${clGreen}${ipvpn}${clNone}"
else
echo -e "[!] ${clRed}You are not connected to the VPN!${clNone}"
fi
exit 0
;;
# It will show the SVPNCOOKIE on screen:
v)
SHOWCOOKIE=1
;;
# Shows all detected profiles and quits:
L)
echo "[*] Enumerating Firefox profiles ... "
enumerateProfiles
exit 0
;;
# Firefox default profile override:
p)
fProfile="$OPTARG/sessionstore-backups"
echo "[*] Overriding detected Firefox profile"
# Make sure the directory is valid:
if [ ! -d "$fProfile" ]; then
echo "[!] Error, ${fProfile} does not exist! Aborting..."
exit 1
fi
;;
P)
# We do not really care if the fProfile variable has been filled
# by autodetecting the Firefox profile or because the user has
# used the "-p" parameter; we save it to ~/.${USER}-fuckforticlient-profile
# anyways...:
echo -e "[+] Saving profile to: ${clGreen}~/.${USER}.fuckforticlient-profile"
echo -ne "${clNone}"
echo -n "${fProfile}" > ~/.${USER}.fuckforticlient-profile
;;
# Timeout for the SVPNCOOKIE override:
t)
TIMEOUT=$OPTARG
;;
# Overrides SERVER and tries to authenticate against -S SERVER:
S)
SERVER="$OPTARG"
;;
# Removes Forticlient:
d)
echo "[*] Removing Forticlient as requested ... "
dpkg -l forticlient >/dev/null 2>&1
if [ $? -eq 0 ]; then
sudo dpkg --purge forticlient
else
echo "[!] Forticlient is not installed!"
fi
exit 0
;;
# Tries to get the SVPNCOOKIE without re-opening firefox and
# then uses the cookie to start the VPN:
s)
# First of all, if there is a running openfortivpn instance,
# we exit and we do not try to re-connect:
checkAnotherInstance
# We do nothing if we do not specify a valid VPN-SSL server:
if [ -z "$SERVER" ]; then
echo "[!] Please, re-run the script with -S VPNSERVER"
exit 0
fi
echo -e "[*] Firefox profile: ${clRed}$fProfile"
echo -ne "${clNone}"
echo "[*] Trying to re-use a previous SVPNCOOKIE..."
getCookie "$fProfile" "0"
if [ ! $? -eq 0 ]; then
echo "[!] Unable to get SVPNCOOKIE; aborting..."
exit 0
else
test $SHOWCOOKIE -eq 1 && echo "[*] `cat $HOME/.${USER}.svpncookie`"
echo -e "[*] ${clGreen}SVPNCOOKIE sucessfully retrieved!"
echo -ne "${clNone}"
# We save the cookie file to a variable first:
cookie=$HOME/.${USER}.svpncookie
# We connect to the vpn now:
test $DEBUG -eq 1 && dbg="-vvv"
sudo /usr/local/bin/openfortivpn $SERVER:443 --cookie-on-stdin < ${cookie} ${dbg} ${FUCKFORTICLIENT_OPTS}
if [ ! $? -eq 0 ]; then
echo "${clRed}[!] Error, expired cookie probably...${clNone}"
echo "[!] Close Firefox and re-lanch the script using -c"
exit 1
fi
fi
;;
# Establishes a new connection by opening Firefox first. The user
# needs to authenticate against the Fortinet server first:
c)
# First of all, if there is a running openfortivpn instance,
# we exit and we do not try to re-connect:
checkAnotherInstance
# We do nothing if we do not specify a valid VPN-SSL server:
if [ -z "$SERVER" ]; then
echo "[!] Please, re-run the script with -S VPNSERVER"
exit 0
fi
# FIX: make sure to use the right profile!!!!
profName=`dirname ${fProfile}|rev|cut -d"." -f1|rev|cut -d"/" -f1`
echo -e "[*] Opening Firefox for SAML login with: ${clGreen}-P ${profName}...${clNone}"
firefox -P ${profName} ${OPTIONS} https://${SERVER}/remote/login >/dev/null 2>&1 &
echo -e "[*] Firefox profile: ${clRed}$fProfile"
echo -ne "${clNone}"
echo -e "[*] Authenticating against ${clRed}https://$SERVER ..."
echo -ne "${clNone}"
# There's some delay before firefox stores the cookie unless it is closed,
# in which case it's inmediately there.
echo -e "[*] Waiting up to ${clRed}$TIMEOUT seconds${clNone} until the cookie appears..."
# Gets the cookie:
getCookie "$fProfile" "1"
if [ ! $? -eq 0 ]; then
echo "[!] Unable to get SVPNCOOKIE; aborting..."
exit 0
else
test $SHOWCOOKIE -eq 1 && echo "[*] `cat $HOME/.${USER}.svpncookie`"
echo -e "[*] ${clGreen}SVPNCOOKIE sucessfully retrieved!"
echo -ne "${clNone}"
# We save the cookie file to a variable first:
cookie=$HOME/.${USER}.svpncookie
# We connect to the vpn now:
test $DEBUG -eq 1 && dbg="-vvv"
sudo /usr/local/bin/openfortivpn ${SERVER}:443 --cookie-on-stdin < ${cookie} ${dbg} ${FUCKFORTICLIENT_OPTS}
if [ ! $? -eq 0 ]; then
echo -e "${clRed}[!] Error, expired cookie probably...${clNone}"
echo "[!] Close Firefox and re-lanch the script using -c"
exit 1
fi
fi
;;
esac
done
@AHZR199
Copy link

AHZR199 commented Sep 11, 2023

Hey, would you be able to adapt this script to run on MacOS?

Thanks.

@NCatalani
Copy link

Good stuff!

I've done a similar thing using a main shell and a python snippet (Selenium) helper to fetch the token, but this looks cleaner and more well-rounded with the benefit of less dependencies.

(fuck FortiClient btw)

@nonamed01
Copy link
Author

nonamed01 commented Sep 20, 2023

Hey, would you be able to adapt this script to run on MacOS?

Thanks.

Sorry mate, I don't have access to an OS/X installation. Maybe someone else can help instead....how about you?

@nonamed01
Copy link
Author

Good stuff!

I've done a similar thing using a main shell and a python snippet (Selenium) helper to fetch the token, but this looks cleaner and more well-rounded with the benefit of less dependencies.

(fuck FortiClient btw)

Thanks! XD

@NCatalani
Copy link

Hey, would you be able to adapt this script to run on MacOS?

Thanks.

Well, you can try my workaround. I have a work m8 that uses it with OSX daily.

Create the files below and do the following:

  1. Replace VPN_HOST inside get_cookie.py with your real VPN host
  2. Replace both host and trusted cert inside vpn.config
  3. Run vpn_connect.sh

vpn_connect.sh

#!/usr/bin/env bash

REQUIREMENTS_FILE="requirements.txt"
CONFIG_FILE="vpn.config"
ENV_DIR=".env"

# Creates vevn if it doesn't exist
[[ ! -d .env ]] && python3 -m venv ${ENV_DIR}
source ${ENV_DIR}/bin/activate && pip install -r ${REQUIREMENTS_FILE}

# Retrieves the token
python3 get_cookie.py > .vpn_cookie || exit 1

# Connects to VPN
COOKIE=$(cat .vpn_cookie)
sudo openfortivpn -v -c $CONFIG_FILE --cookie="SVPNCOOKIE=${COOKIE}"

get_cookie.py

#!/usr/bin/env python3
from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait

VPN_HOST="https://foo.bar.com/"

#driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager(version='114.0.5735.90').install()))
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))
driver.get(VPN_HOST)
cookie = WebDriverWait(driver, timeout=120).until(lambda d: d.get_cookie('SVPNCOOKIE'))['value']
driver.quit()
print(cookie)

requirements.txt

selenium==4.10.0
webdriver-manager>=3.8.6

vpn.config

host = foo.bar.com
port = 443
trusted-cert = 015d40adce838fd3dbcdaf7d8d7438aabb6c57e5e9ab020dbb536fff7eba2e1d

@Vampirebruno
Copy link

Vampirebruno commented Sep 26, 2023

I´m really interested on this script and seems to work but, after login, shows this message on Firefox:

"The SSL-VPN portal has been enabled for tunnel mode use only. Forticlient is required to connect".

I´m not a VPN expert. Could anyone help us?

Could be related to this bug?

https://docs.fortinet.com/document/forticlient/7.2.1/linux-release-notes/254811/known-issues

Thanks and best regards

@nonamed01
Copy link
Author

Just in case, from now on the development of this project will continue as a proper git repo:

https://github.com/nonamed01/fuckForticlient

So clone the repo and feel free to create pull requests, issues, or just be a part of its development.... When I wrote this script, the main idea was to solve a problem that I though temporary .... and , well, one year in and the official Forticlient GNU/Linux client is total crap ... So, well, it seems we'll be using this for a long time.....

@ciherrera
Copy link

not working on fedora 39

[*] Detected distro: Fedora, version: 39
[!] Sanity check reported an error.
[!] Make sure you have installed all the pre-requisites first.
[!] Make sure you belong to the sudo group also.

@ngsoftwaredev
Copy link

not working on fedora 39

[*] Detected distro: Fedora, version: 39 [!] Sanity check reported an error. [!] Make sure you have installed all the pre-requisites first. [!] Make sure you belong to the sudo group also.

lz4json package does not exist, not sure how to build it, so yeah, no solution for Fedora unfortunately... And official Forticlient for Linux is garbage.

@filippor
Copy link

If someone interested
For external browser I implemented a script to retrieve token on repository https://github.com/filippor/XdgOpenSaml
the process is
1 start a server to listen on localhost:8020/?id=
2 open in external browser url + "/remote/saml/start?redirect=1"
3 server receive a call and with retieved id call url + "/remote/saml/auth_id?id=" + id to retrieve cookie

you can see a sample implementation in this repo https://github.com/filippor/XdgOpenSaml/blob/main/XdgOpenSaml.java that write the cookie to standard out like openfortivpn-webview

XdgOpenSaml url:port | sudo openfortivpn url:port --cookie-on-stdin --pppd-use-peerdns=

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment