Permissions Practice


Overview

The goal here is for you to gain some hands-on practice. This is a companion guide to our lecture on this topic, and a required myCourses quiz. You’ll have written questions on the exam that resemble these questions.

We’re going to give you a script that emulates a basic permissions system. Importantly, each time we run our script, we’re starting from a completely clean container. This is intentional. Playing around with permissions is infamous for putting the system into a different state than you expect, and can get very confusing. So, for this practice, we’ll always start from a generic Linux operating system, add our users, groups, files, etc. so you can know exactly what state the system is in.

Secondarily, we’re giving you some practice with Docker.

Docker

Messing with permissions is not something you want to do to a regular machine. So for this project, we’re going to be using Docker. Docker is a containerization system that allows you to fake an entire operating system for a single program. We’re going to use a generic Docker container in Linux, and we’ll run a script to get the information we need. We’re going to use docker run to run a shell script, and that will output to our console.

To use Docker, you have a few options:

  • Install locally on your own machine. These days, Docker is becoming a standard software development tool - so you may already have Docker Desktop already installed.

  • Use our hydra servers. These are special servers that are isolated from other shared resources so that we can run docker containers from the command line. Here’s how you use it:

    • There are six servers, named hydra00.cs.rit.edu through hydra05.cs.rit.edu. For basic load balancing, use the following servers based on the month you were born:
    Birth Month Server
    Jan-Feb hydra00.cs.rit.edu
    Mar-Apr hydra01.cs.rit.edu
    May-Jun hydra02.cs.rit.edu
    Jul-Aug hydra03.cs.rit.edu
    Sep-Oct hydra04.cs.rit.edu
    Nov-Dec hydra05.cs.rit.edu
    • SSH into your hydra server using your CS (yes, Computer Science) password. If you don’t know it, you can reset it at: knockknock.cs.rit.edu.
    • Important Hydra itself runs Linux, however, you don’t have permissions to do what we’ll do in this project. That’s why we’re running our scripts inside a Docker container.
    • When you SSH in, you’ll be able to do your docker run commands as you wish.
    • You’ll be editing a script on Hydra. You can use vim or nano to get this done. Or, you can set up your local VS Code to connect to hydra using its Remote Explorer (your instructor may demo this).

Note: we will not be able to run Docker from the SE lab machines.

Script Copypasta

Copy and paste the following script into a file called perms.sh.

Note about line endings. If you copy this from a Windows machine directly into an editor like Notepad, you might get errors about non-ASCII characters and other cryptic errors. Coding editors like (e.g. VS Code) will auto-detect the line endings and fix this for you. Alternatively, you can run dos2unix perms.sh on Hydra convert the line endings.

echo "\n--- CREATE USERS ---"
adduser paul  --shell /bin/bash --disabled-password --gecos ""
adduser leto  --shell /bin/bash --disabled-password --gecos ""
adduser vlad  --shell /bin/bash --disabled-password --gecos ""

echo "\n--- CREATE GROUPS ---"
addgroup arrakis
adduser paul arrakis
adduser leto arrakis
# NOTE that vlad is NOT in arrakis

echo "\n--- UMASK ---"
umask

echo "\n--- SET UP DIRECTORY ---"
mkdir /dune
chmod 777 /dune # BAD SECURITY. This is for educational purposes.
ls -la /dune

echo "\n--- SET UP FILES ---"
echo "the spice must flow" >> /dune/melange.txt
chown paul    /dune/melange.txt
chgrp arrakis /dune/melange.txt
chmod g+w     /dune/melange.txt
chmod o-rwx   /dune/melange.txt

mkdir /dune/vault
chmod 771 /dune/vault
chown paul /dune/vault
chgrp arrakis /dune/vault
echo "secrets!" >>    /dune/vault/secret1234.txt
chmod o=rw /dune/vault/secret1234.txt

mkdir /dune/workspace
chgrp arrakis /dune/workspace
chmod g+s /dune/workspace # setgid
touch arrakis /dune/workspace/shared.txt

cp /usr/bin/whoami /dune
mv /dune/whoami /dune/whoamireally
chown paul /dune/whoamireally
chmod u+s  /dune/whoamireally # setuid

echo "\n--- ACT AS PAUL ---"
echo -n "Who am I? "
su paul -c 'whoami'
su paul -c 'umask'
su paul -c 'cat /dune/melange.txt'
su paul -c 'touch         /dune/paul_only.txt'
su paul -c 'chgrp arrakis /dune/paul_only.txt'
su paul -c 'chmod g-rwx   /dune/paul_only.txt'
su paul -c 'echo -n "Running whoamireally: " ; /dune/whoamireally'


su paul -c 'umask 007 ; touch /dune/arrakis_only.txt'
su paul -c 'ls -l /dune/arrakis*'

echo "\n--- ACT AS LETO ---"
echo -n "Who am I? "
su leto -c 'whoami'
su leto -c 'umask'
su leto -c 'touch /dune/leto.txt'
su leto -c 'cat /dune/melange.txt'

su leto -c 'ls /dune/vault'
su leto -c 'cat /dune/vault/secret1234.txt'

su leto -c 'echo -n "Running whoamireally as leto: " ; /dune/whoamireally'

su leto -c 'cat /dune/arrakis_only.txt'

echo "\n--- ACT AS VLAD ---"
echo -n "Who am I? "
su vlad -c 'whoami'
su vlad -c 'umask'
su vlad -c 'touch /dune/vlad.txt'
su vlad -c 'cat /dune/melange.txt'

su vlad -c 'ls /dune/vault'
su vlad -c 'cat /dune/vault/secret1234.txt'

su vlad -c 'echo -n "Running whoamireally as vlad: " ; /dune/whoamireally'

su vlad -c 'cat /dune/arrakis_only.txt'


echo "\n--- PERMISSIONS ---"
ls -l /dune
ls -la /dune/vault
ls -la /dune/workspace
echo "" # newline at the end of the output

# bash

Running the Script

Your Docker command to run this will look like this. Note that it is all one line.

Compatibility note:

  • This will work in Mac and Linux (Hydra).
  • This will also work in most Windows Powershell (not CMD), prompts. If it doesn’t, try replacing $(pwd) with your current directory path (see below).

docker run -it --rm --mount type=bind,source="$(pwd)",target=/root ubuntu:22.04 sh /root/perms.sh

Let’s break down each piece of this, using the documentation

  • docker run is our command
  • -it essentially means “interactive”, or “show everything to the console stdout and use the shell when reading from stdin”.
  • --rm means “delete everything when this run is done”, which makes our run clean and doesn’t fill up our hard drive with past runs.
  • --mount type=bind,source="$(pwd)",target=/root means “my current folder should be mapped to the /root folder in the container. This mount command allows us to run our script without having to create a whole new container. The pwd means “print working directory”, so you could also replace this with the full filepath of your current directory (this part is what might vary from OS to OS)
  • ubuntu:22.04 is the image we want to run. If your machine doesn’t have it installed, Docker will call out to DockerHub and download the image. You may see downloading feedback the first time your run this. The Linux version we chose is pretty arbitrary - you can use lots of different Linux images for this project.
  • sh /root/perms.sh - means “run the shell script”, which, since we mounted our local folder with --mount it’ll find our file to run.

Commands

Here’s a reference to all of the commands we are using:

  • ls lists file permissions. The -a will show files that start with ., including the pseudo-directories of . (“this” directory) and .. (parent directory)
  • mkdir creates a new directory
  • umask shows the current default permissions mask if no number is given. Invoking with a number (e.g. umask 077) sets the current user’s umask.
  • chmod changes the permissions on a file or directory. See our lecture on how it works.
  • chgrp changes the group of a file or directory
  • chown changes who owns a file or directory
  • cp copies the file
  • mv moves the file, in this case effectively renaming the file
  • su allows you to act as another user, i.e. spoof them. (Note: this is why we need to be in Docker - you need root privileges to use su). Ordinarily we might use sudo, but sudo is no longer installed by default in Ubuntu, but the -c option to su will suffice for our purposes.
  • adduser does two things:
    • Creates a new user. Our invocation is pretty non-standard and intended to make scripting easier
    • Adds a user to a group
  • addgroup creates a new group
  • echo is a standard shell scripting command that prints stuff to the console. Also:
    • The -n option omits the newline
    • We are using the “pipe append” (>>) to create files and put some contents in them
  • cat means “concatenate to stdout”, effectively just “print the contents of the file to the console”
  • ; allows us to put two commands into one, simplifying our statement
  • bash (commented out by default). If you want to try these commands interactively in this environment, you can uncomment this. You can also place bash between any other lines in this scrtipt and execution will halt there. Type exit to finish running the script when you’re done.

Our Setup

We have the following users set up:

  • Everything in our scenario takes place in the /dune folder.
  • paul and leto are members of the group arrakis
  • vlad is not a member of the group arrakis

Note that, by default, all users have a group that is also named after them. So paul is in a group called paul, and so on. This is the default group that is set when you make a new file or folder.

From there we are doing the following:

  • The file /dune/melange.txt is a file that is group editable and in the arrakis group
  • The directory /dune/vault is in the arrakis group. It is set up so that anyone can open a file if they know the name, but people outside the arrakis group cannot use ls to find the name. We have a file called secret1234.txt in it.
  • The OS-standard binary whoami is copied and renamed to whoamireally and the setuid permission is set. Ordinarily whoami prints what the current user’s name is, but in this case we’re tampering with a copy of the binary and changing the permissions to take advantage of setuid. Thus, when, say, vlad runs whoamireally, it says paul.

Default Output

From the copypasta above, the output should look something like this, e.g. your timestamps will look different. The Permission denied errors, however, are expected.


--- CREATE USERS ---
Adding user `paul' ...
Adding new group `paul' (1000) ...
Adding new user `paul' (1000) with group `paul' ...
Creating home directory `/home/paul' ...
Copying files from `/etc/skel' ...
Adding user `leto' ...
Adding new group `leto' (1001) ...
Adding new user `leto' (1001) with group `leto' ...
Creating home directory `/home/leto' ...
Copying files from `/etc/skel' ...
Adding user `vlad' ...
Adding new group `vlad' (1002) ...
Adding new user `vlad' (1002) with group `vlad' ...
Creating home directory `/home/vlad' ...
Copying files from `/etc/skel' ...

--- CREATE GROUPS ---
Adding group `arrakis' (GID 1003) ...
Done.
Adding user `paul' to group `arrakis' ...
Adding user paul to group arrakis
Done.
Adding user `leto' to group `arrakis' ...
Adding user leto to group arrakis
Done.

--- UMASK ---
0022

--- SET UP DIRECTORY ---
total 8
drwxrwxrwx 2 root root 4096 Aug 14 19:44 .
drwxr-xr-x 1 root root 4096 Aug 14 19:44 ..

--- SET UP FILES ---

--- ACT AS PAUL ---
Who am I? paul
0002
the spice must flow
Running whoamireally: paul
-rw-rw---- 1 paul paul 0 Aug 14 19:44 /dune/arrakis_only.txt

--- ACT AS LETO ---
Who am I? leto
0002
the spice must flow
secret1234.txt
secrets!
Running whoamireally as leto: paul
cat: /dune/arrakis_only.txt: Permission denied

--- ACT AS VLAD ---
Who am I? vlad
0002
cat: /dune/melange.txt: Permission denied
ls: cannot open directory '/dune/vault': Permission denied
secrets!
Running whoamireally as vlad: paul
cat: /dune/arrakis_only.txt: Permission denied

--- PERMISSIONS ---
total 44
-rw-rw---- 1 paul paul        0 Aug 14 19:44 arrakis_only.txt
-rw-rw-r-- 1 leto leto        0 Aug 14 19:44 leto.txt
-rw-rw---- 1 paul arrakis    20 Aug 14 19:44 melange.txt
-rw----r-- 1 paul arrakis     0 Aug 14 19:44 paul_only.txt
drwxrwx--x 2 root arrakis  4096 Aug 14 19:44 vault
-rw-rw-r-- 1 vlad vlad        0 Aug 14 19:44 vlad.txt
-rwsr-xr-x 1 paul root    31232 Aug 14 19:44 whoamireally
drwxr-sr-x 2 root arrakis  4096 Aug 14 19:44 workspace
total 12
drwxrwx--x 2 root arrakis 4096 Aug 14 19:44 .
drwxrwxrwx 4 root root    4096 Aug 14 19:44 ..
-rw-r--rw- 1 root root       9 Aug 14 19:44 secret1234.txt
total 8
drwxr-sr-x 2 root arrakis 4096 Aug 14 19:44 .
drwxrwxrwx 4 root root    4096 Aug 14 19:44 ..
-rw-r--r-- 1 root arrakis    0 Aug 14 19:44 shared.txt

This is now YOUR script

At this point, we’re giving you a toolbox and we’re asking you to explore and answer questions. We’ve demonstrated much of what we lectured on, and now it’s your turn to mess around in the sandbox.

You will undoubtedly have to edit this script and experiment on your own to answer these questions. You likely won’t be able to answer all of the questions by just studying the output.

Questions

The following questions are on myCourses, but are repeated here for convenience. All of these assume the system is in the state left by the above script.

Making vault personal

What chmod command should be used to make the vault readable only to paul and not anyone else?.

  • chmod 000
  • chmod 777
  • chmod 711
  • chmod 700
  • chmod 600

Why can Vlad see the secret?

Why can vlad read the contents of the file in /dune/vault/secret1234.txt?

Choose one:

  • Because the /dune directory is 777
  • Because Docker runs as root
  • Because /dune has executable permissions on non-arrakis users
  • Because /dune has read permissions for all users

Hide the secret

Suppose paul wants to change the permissions on only the file secret1234.txt so that anyone outside of arrakis could not open it - even if they knew the name of the file (but paul and leto could still access it). What chmod command should be used?

  • chmod ug=rw /dune/vault/secret1234.txt
  • chmod o-r /dune/vault/secret1234.txt
  • chmod a-r /dune/vault/secret1234.txt

For these permissions, what is the octal notation?

-rw--w----

Why the group?

Check the group ownership of /dune/vault/secret1234.txt and /dune/workspace/shared.txt. Why is shared.txt in the arrakis group and secret1234.txt not?

Choose one.

  • workspace uses the sticky bit
  • workspace uses setgid and is owned by the arrakis group.
  • shared.txt uses setuid
  • We used touch and not echo to create the file
  • workspace is owned by arrakis and vault is not.
  • The umask is set to have permissive group permissions

Why no execute?

What would happen if we tried to execute melange.txt with su paul -c './melange.txt', and why?

  • The code runs, because umask is 0022, but it’s not valid shell syntax so it fails
  • The code runs and nothing happens because umask is 0002 for paul
  • The code does not run because files are never created with execute permissions from umask, and we never gave execute permissions
  • The code does not run because the directory also has execute permissions
  • The code does run because chmod 777 /dune was run at the beginning.

Copying whoamireally?

Suppose vlad makes a copy of whoamireally into /dune and gives it read and execute permissions for non-group members. Then, paul attempts to execute it. What happens?

  • The output is paul
  • The output is leto
  • The output is vlad
  • The execution fails

Permission for standard tools

Changing passwords in Linux requires you to run passwd, which is located in /bin/passwd. You can only change your own password, however. Passwords are stored in the root-only file /etc/shadow, which means you would need to temporarily act as root to change your own password. But… you don’t need root access to change your own password. How does Linux accomplish this?

  • The /bin/passwd has setuid and is owned by root
  • The /bin/passwd has setgid and is in the root group
  • The /etc/shadow and /bin/password are both owned by root
  • The /bin/passwd is owned by root, and when you execute a program, the owner becomes the acting user
  • Nobody knows, only Linus Torvalds knows this secret.