SEED Project is a series of lab exercises developed for cybersecurity education. This is an open-source project that includes lab exercises that cover various domains of cybersecuirty. The goal of this series of articles is to provide detailed insights into these labs.
INTRODUCTION
- Prerequisites
- Knowledge of Linux Command-Line-Interface
- Lab Setup
- Ubuntu/SEED virtual machine
- Resources
- https://seedsecuritylabs.org/Labs_20.04/Software/Environment_Variable_and_SetUID/
The pre-configured SEED virtual machine will be used in this lab demonstration. Oracle Virtual Box hypervisor is used to host the virtual machine. All the resource materials are downloaded from the SEED website. The following sections will discuss the concepts explored in the lab document.
Task 1: Manipulating Environment Variables
Understanding Environment Variable
Operating systems use environment variables to store information that is frequently used during system operations. This is best demonstrated using an example. An obvious use of these variables is during the execution of any terminal command. Lets consider the “ls” command. The “ls” command is normally used for listing the contents of a directory. It’s operation can be enhanced by providing command line arguments and flags. The complete manual for the “ls” command can be found here.
The “ls” command executes a program that is stored inside the machine. It is not inside the current directory. We can use the “find” command to verify the location of the “ls” executable. Output from the “find” command shows several paths where executables named “ls” were found.
This check can be done for any of the other common commands. This will continue to reveal the paths to the original executables. So, how are these executables being executed?
The answer is in the environment variables. The PATH variable is one of the environment variables that stores locations to directories that contain frequently used applications.
The PATH variable stores several paths, separated by a colon character. The shell program is designed to look for the executable in each one of these folders. If found, the program will be executed. Otherwise an error will inform the user that the command is unknown.
The exercise introduces us to 2 commands that can be used to explore the environment variables, “printenv” and “env”. Additionally, environment variables can be created or modified using the “export” command and deleted using the “unset” command.
Next, we will be exploring the security implications of environment variables.
Task 2: Passing Environment Variables from Parent Process to Child Process
The focus of this task will be the C program titled “myprintenv.c”. The program uses the fork() method to create a child process. In a complicated program this can serve critical requirements. But in this case, the method is used to explore what environment variables are getting inherited by a child process.
Program in its original form prints the environment variables as observed from within the child process. The program is then to be modified to print the environment variables as observed by the parent process. Both outputs can be saved into files using the “>”. Then the “diff” command can be used to find any differences between the two outputs.
The “diff” command output proves that there are no differences between the two outputs. This proves that both the parent and child processes has access to the same environment variables.
Conclusion
The “fork” method allows a child process to inherit the environment variables of the parent process.
Task 3: Environment Variables and execve()
This task focuses on the C program titled “myenv.c”. Notably, this program uses the “execve()” method to execute the “env” command. According to the manual page for “execve()”, it takes 3 arguments. First argument is the path to an executable, second is an array of command-line arguments, and third is an array of key:value pairs to be read in as environment variables.
First, we compile and execute the program as it is. Here, “execve” is running the “env” program, with NULL value for environment variables. The “env” program looks for environment variables, and we have given none. As such, the output is empty.
Next, we update the program to consider the current environment variables when executing the “env” command. Once the program is updates and executed, we can observe all the environment variables as expected.
Conclusion
When “execve” is used to call programs, the environment variables accessible by the program can also be manually defined, giving the user an extra degree of control over the security.
Task 4: Environment Variables and system()
This exercise introduces another method for calling other programs, known as “system”. It also uses the “execve” method to run a program, but uses the current environment variables during execution. The sample source code when compiled proves this.
Task 5: Environment Variables and Set-UID Programs
Set-UID is a Unix permission granted to executables, that allows a program to run with the file owner’s privileges, instead of the user’s privileges. The expectation is to delegate certain privileged tasks to other users. Should the program owner be the Root user, the program will indeed execute with Root privileges. This opens up room for security breaches, which will be explored below.
First we write a program to list out the environment variables. Then it will be compiled, ownership will be changed to Root and and Set-UID permissions are granted.
Now, a custom environment variable is set for testing purposes.
Next , the Set-UID executable is run. Observations are as follows.
The Set-UID program is accessing the user’s environment variables. Presence of the custom environment variable “MY_NAME” proves this.
Next, some ways of this could be abused will be explored.
Task 6: The PATH Environment Variable and Set-UID Programs
As explained in the beginning of this report, when a command is entered into the terminal, it’s actual location is found by the operating system by going through the PATH variable. This is the same when a command is called programmatically, using a method like “system()”. All the paths listed in the PATH variable will be checked, and the first one matching the name will get executed.
Since the “system()” method uses the user’s environment variables, a user could easily inject a custom path into the beginning of the PATH variable. If a malicious executable is renamed to match the command that is being called, and placed in this location, it will get executed.
What makes this even more dangerous is, if the parent program is a Set-UID program owned by Root user, the malicious program will also get executed with Root privileges. In brief, this allows a regular user to create a malicious program and execute it with Root privileges.
Task 7: The LD_PRELOAD Environment Variable and Set-UID Programs
This task explores another similar avenue of exploitation, but through a different environment variable.
LD_PRELOAD is an environment variable that specifies the path to a library file that is to be loaded at the time of the execution. We have compiled a library to replace the “sleep()” command to observe when and where the malicious library gets called.
First the program is owned and executed by the normal user.
Observation
The malicious library was called.
Next the program owner is changed to Root, and made an Set-UID program.
Observation
The malicious library did not get called.
Since the Set-UID program is being executed as the owner, the LD_PRELOAD environment variable that was set by the user seems to have not been inherited by the child process.
Observation
LD_* environment variables are not inherited by the child process.
Task 8: Invoking External Programs Using system() versus execve()
The “catall.c” program is designed to take in any file name as an argument and open it using the “/bin/cat” program. This functionality can be confirmed by reading the contents of the “/etc/shadow” file. But there are no checks to verify if the user input is indeed just a file name.
Step 1: Exploiting catall.c
We can append another command to the file name using the “;” character and trick the program into executing that with the owners privileges. This can be confirmed by appending the “whoami” command to the file name.
The last row states “root” indicating that the “whoami” command was executed as the Root user.
This opens up a vulnerability that can do unlimited amount of damage, way beyond just deleting restricted files!
Step 2: Exploiting catall.c when execve is used
The previous attack is no longer viable, because execve() parses the file name as a input to the “/bin/cat” command. The “cat” command tries to read the modified input as a file name and reports back that there is no such file.
Task 9: Capability Leaking
The program opens the “/etc/zzz” file in append mode and prints the file descriptor value. Then it uses the “setuid()” method to restore the privileges to match the user and opens a bash shell. The notable issue here is, the file was opened with elevated privileges and was never closed. So we can still direct inputs into the file using the file descriptor.
Not closing the file left a privilege leake open for exploitation.
The End
Please leave your feedback below. I am planning to create a video series to accompany these blog posts, that could make it more easy to follow these exercises.