Environment Variable and Set-UID Program Lab: 2.1 Task 1: Manipulating Environment Variables
Environment Variable and Set-UID Program Lab: 2.1 Task 1: Manipulating Environment Variables
1 Overview
The learning objective of this lab is for students to understand how environment variables affect program
and system behaviors. Environment variables are a set of dynamic named values that can affect the way
running processes will behave on a computer. They are used by most operating systems, since they were
introduced to Unix in 1979. Although environment variables affect program behaviors, how they achieve
that is not well understood by many programmers. As a result, if a program uses environment variables, but
the programmer does not know that they are used, the program may have vulnerabilities.
In this lab, students will understand how environment variables work, how they are propagated from
parent process to child, and how they affect system/program behaviors. We are particularly interested in how
environment variables affect the behavior of Set-UID programs, which are usually privileged programs.
This lab covers the following topics:
• Environment variables
• Set-UID programs
• Securely invoke external programs
• Capability leaking
• Dynamic loader/linker
Readings and videos. Detailed coverage of the Set-UID mechanism, environment variables, and their
related security problems can be found in the following:
• Chapters 1 and 2 of the SEED Book, Computer & Internet Security: A Hands-on Approach, 2nd
Edition, by Wenliang Du. See details at https://www.handsonsecurity.net.
• Section 2 of the SEED Lecture at Udemy, Computer Security: A Hands-on Approach, by Wenliang
Du. See details at https://www.handsonsecurity.net/video.html.
Lab environment. This lab has been tested on our pre-built Ubuntu 16.04 VM, which can be downloaded
from the SEED website.
2 Lab Tasks
2.1 Task 1: Manipulating Environment Variables
In this task, we study the commands that can be used to set and unset environment variables. We are using
Bash in the seed account. The default shell that a user uses is set in the /etc/passwd file (the last field
of each entry). You can change this to another shell program using the command chsh (please do not do it
for this lab). Please do the following tasks:
SEED Labs – Environment Variable and Set-UID Program Lab 2
• Use printenv or env command to print out the environment variables. If you are interested in
some particular environment variables, such as PWD, you can use "printenv PWD" or "env |
grep PWD".
• Use export and unset to set or unset environment variables. It should be noted that these two
commands are not seperate programs; they are two of the Bash’s internal commands (you will not be
able to find them outside of Bash).
2.2 Task 2: Passing Environment Variables from Parent Process to Child Process
In this task, we study how a child process gets its environment variables from its parent. In Unix, fork()
creates a new process by duplicating the calling process. The new process, referred to as the child, is an
exact duplicate of the calling process, referred to as the parent; however, several things are not inherited by
the child (please see the manual of fork() by typing the following command: man fork). In this task,
we would like to know whether the parent’s environment variables are inherited by the child process or not.
Step 1. Please compile and run the following program, and describe your observation. Because the output
contains many strings, you should save the output into a file, such as using a.out > child (assuming
that a.out is your executable file name).
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void printenv()
{
int i = 0;
while (environ[i] != NULL) {
printf("%s\n", environ[i]);
i++;
}
}
void main()
{
pid_t childPid;
switch(childPid = fork()) {
case 0: /* child process */
printenv(); À
exit(0);
default: /* parent process */
//printenv(); Á
exit(0);
}
}
Step 2. Now comment out the printenv() statement in the child process case (Line À), and uncomment
the printenv() statement in the parent process case (Line Á). Compile and run the code again, and
SEED Labs – Environment Variable and Set-UID Program Lab 3
Step 3. Compare the difference of these two files using the diff command. Please draw your conclusion.
Step 1. Please compile and run the following program, and describe your observation. This program
simply executes a program called /usr/bin/env, which prints out the environment variables of the
current process.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *argv[2];
argv[0] = "/usr/bin/env";
argv[1] = NULL;
return 0 ;
}
Step 2. Change the invocation of execve() in Line À to the following; describe your observation.
execve("/usr/bin/env", argv, environ);
Step 3. Please draw your conclusion regarding how the new program gets its environment variables.
using system(), the environment variables of the calling process is passed to the new program /bin/sh.
Please compile and run the following program to verify this.
#include <stdio.h>
#include <stdlib.h>
int main()
{
system("/usr/bin/env");
return 0 ;
}
Step 1. Write the following program that can print out all the environment variables in the current process.
#include <stdio.h>
#include <stdlib.h>
void main()
{
int i = 0;
while (environ[i] != NULL) {
printf("%s\n", environ[i]);
i++;
}
}
Step 2. Compile the above program, change its ownership to root, and make it a Set-UID program.
// Asssume the program’s name is foo
$ sudo chown root foo
$ sudo chmod 4755 foo
Step 3. In your shell (you need to be in a normal user account, not the root account), use the export
command to set the following environment variables (they may have already exist):
• PATH
SEED Labs – Environment Variable and Set-UID Program Lab 5
• LD LIBRARY PATH
• ANY NAME (this is an environment variable defined by you, so pick whatever name you want).
These environment variables are set in the user’s shell process. Now, run the Set-UID program from
Step 2 in your shell. After you type the name of the program in your shell, the shell forks a child process,
and uses the child process to run the program. Please check whether all the environment variables you set
in the shell process (parent) get into the Set-UID child process. Describe your observation. If there are
surprises to you, describe them.
The Set-UID program below is supposed to execute the /bin/ls command; however, the program-
mer only uses the relative path for the ls command, rather than the absolute path:
int main()
{
system("ls");
return 0;
}
Please compile the above program, and change its owner to root, and make it a Set-UID program.
Can you let this Set-UID program run your code instead of /bin/ls? If you can, is your code running
with the root privilege? Describe and explain your observations.
Note (Ubuntu 16.04 VM only): The system(cmd) function executes the /bin/sh program first, and
then asks this shell program to run the cmd command. In both Ubuntu 12.04 and Ubuntu 16.04 VMs,
/bin/sh is actually a symbolic link pointing to the /bin/dash shell. However, the dash program in
these two VMs have an important difference. The dash shell in Ubuntu 16.04 has a countermeasure that
prevents itself from being executed in a Set-UID process. Basically, if dash detects that it is executed in
a Set-UID process, it immediately changes the effective user ID to the process’s real user ID, essentially
dropping the privilege. The dash program in Ubuntu 12.04 does not have this behavior.
Since our victim program is a Set-UID program, the countermeasure in /bin/dash can prevent our
attack. To see how our attack works without such a countermeasure, we will link /bin/sh to another
shell that does not have such a countermeasure. We have installed a shell program called zsh in our Ubuntu
16.04 VM. We use the following commands to link /bin/sh to zsh (there is no need to do these in Ubuntu
12.04):
$ sudo rm /bin/sh
$ sudo ln -s /bin/zsh /bin/sh
SEED Labs – Environment Variable and Set-UID Program Lab 6
Step 1. First, we will see how these environment variables influence the behavior of dynamic loader/linker
when running a normal program. Please follow these steps:
1. Let us build a dynamic link library. Create the following program, and name it mylib.c. It basically
overrides the sleep() function in libc:
#include <stdio.h>
void sleep (int s)
{
/* If this is invoked by a privileged program,
you can do damages here! */
printf("I am not sleeping!\n");
}
2. We can compile the above program using the following commands (in the -lc argument, the second
character is `):
% export LD_PRELOAD=./libmylib.so.1.0.1
4. Finally, compile the following program myprog, and in the same directory as the above dynamic link
library libmylib.so.1.0.1:
/* myprog.c */
int main()
{
sleep(1);
return 0;
}
SEED Labs – Environment Variable and Set-UID Program Lab 7
Step 2. After you have done the above, please run myprog under the following conditions, and observe
what happens.
• Make myprog a Set-UID root program, export the LD PRELOAD environment variable again in
the root account and run it.
• Make myprog a Set-UID user1 program (i.e., the owner is user1, which is another user account),
export the LD PRELOAD environment variable again in a different user’s account (not-root user) and
run it.
Step 3. You should be able to observe different behaviors in the scenarios described above, even though
you are running the same program. You need to figure out what causes the difference. Environment variables
play a role here. Please design an experiment to figure out the main causes, and explain why the behaviors
in Step 2 are different. (Hint: the child process may not inherit the LD * environment variables).
if(argc < 2) {
printf("Please type a file name.\n");
return 1;
}
return 0 ;
}
Step 1: Compile the above program, make it a root-owned Set-UID program. The program will use
system() to invoke the command. If you were Bob, can you compromise the integrity of the system? For
example, can you remove a file that is not writable to you?
Step 2: Comment out the system(command) statement, and uncomment the execve() statement;
the program will use execve() to invoke the command. Compile the program, and make it a root-owned
Set-UID. Do your attacks in Step 1 still work? Please describe and explain your observations.
void main()
{ int fd;
3 Submission
You need to submit a detailed lab report, with screenshots, to describe what you have done and what you
have observed. You also need to provide explanation to the observations that are interesting or surprising.
Please also list the important code snippets followed by explanation. Simply attaching code without any
explanation will not receive credits.