Skip to main content
Tweeted twitter.com/StackCodeReview/status/971015123296051200
edited tags
Link
200_success
  • 145.6k
  • 22
  • 191
  • 481
Source Link
JesseTG
  • 459
  • 2
  • 6

Bash script to temporarily run a shell without certain group memberships

I have here a script called rwog (run without groups) that allow a user to run a shell without particular group memberships. rwog, in more detail...

  • Is primarily for a support staffer to pretend to be an ordinary user without su-ing to anybody.
  • Must be run as root, but those allowed to run it will be given an entry in /etc/sudoers.
  • Should be executed as sudo rwog groups to drop.
  • Will have a uid of root, a gid of the_support_team, and permissions 0550 (i.e. user and group can read and execute, world can do nothing, and no setgid/setuid bits).
  • Cannot change your gid, change your uid, or add yourself to supplementary groups.
  • Revokes pretty much all capabilities, though this may be a tad zealous.
  • Does not modify /etc/group (so id will be changed, but id $USER will not).
  • Is primarily (but not exclusively) intended to run on a Centos 7 environment..

My main concern: Can this script be exploited to gain escalated privileges? I won't ignore suggestions not related to security.

The Script

#!/bin/bash

function help(){
    echo "rwog - run without groups";
    echo "Runs a shell as if you weren't in certain supplementary groups."
    echo "Good for pretending that you're not a support user."
    echo "Usage example:";
    echo -e "\trwog [(-h|--help)] [group ...]";
    echo "Options:";
    echo -e "\t-h or --help: Displays this information.";
    exit 1;
}

# Declare vars. Flags initalizing to 0.

# Execute getopt
ARGS=$(getopt -o "h" -l "help" -n "rwog" -- "$@");

#Bad arguments
if [ $? -ne 0 ];
then
    help;
fi

eval set -- "$ARGS";

while true; do
    case "$1" in
        -h|--help)
            shift;
            help;
            ;;

        --)
            shift;
            break;
            ;;
    esac
done


if [[ $(id -u) != 0 ]]; then
    # If you're not root...
    echo "You must be root to use this script. Run it with sudo."
    exit 1
fi

for group in $@; do
    # For each group we want to drop...

    if [[ "$group" == "$SUDO_USER" ]]; then
        # If you're trying to drop your own gid...
        echo "You cannot drop your own gid."
        exit 1
    elif ! getent group "$group" &>/dev/null ; then
        #If this group doesn't exist...
        echo "$group is not a valid group."
        exit 1
    elif  ! groups "$SUDO_USER" | grep -E &>/dev/null "\b$group\b"; then
        # Else if you're not actually a member of this group...
        echo "Not a member of $group, cannot drop it"
        exit 1
    fi
done

my_groups=$(id -Gn "$SUDO_USER" | xargs -n1 | sort -u)
groups_to_drop=$(echo $@ | xargs -n1 | sort -u)
# Put the groups that you want to drop on multiple lines, then sort them

if [[ -z "$groups_to_drop" ]]; then
    # If you didn't pick a group to drop...
    echo "Please specify at least one supplementary group to drop."
    help
fi

reduced_groups=$(comm -13 <(echo "$groups_to_drop") <(echo "$my_groups") | paste -s -d,)
# Subtract the groups we want to drop from the groups we're in, then merge them onto one line

export USERNAME="$SUDO_USER"
export USER="$SUDO_USER"
export LOGNAME="$SUDO_USER"
export HOME=$( getent passwd "$SUDO_USER" | cut -d: -f6 )

dropped_capabilities=$(capsh --print | grep -E "Bounding set =(.+)" | sed "s/Bounding set =//g")

capsh --secbits=0xf --drop="$dropped_capabilities" --groups="$reduced_groups" --gid="$SUDO_GID" --uid="$SUDO_UID" -- --login