Task1:** Figure out why “passwd”, “chsh”, “su”, and “sudo” commands need to be Set-UID programs.What will happen if they are not? If you are not familiar with these programs, you should first learn what they can do by reading their manuals. Please copy these commands to your own directory; the copies will not be Set-UID programs. Run the copied programs, and observe what happens.
从Set-UID程序的核心概念:任何人执行set-UID程序的时候,该程序将拥有和程序拥有者的权限。而passwd,chsh,sh,sudo等命令都是属于root用户,如果他们不是set-UID程序,那么普通用户就无法执行一些root用户可以执行的功能。当Set-UID程序copy后,复件不再是set-UID程序也无法实现原本的功能。如图所示:
Task2: Run Set-UID shell programs in Linux, and describe and explain your observations.
(a) Login as root, copy /bin/zsh to /tmp, and make it a set-root-uid program with permission 4755. Then login as a normal user, and run /tmp/zsh. Will you get root privilege? Please describe your observation. If you cannot find /bin/zsh in your operating system, please use the following command to install it:
将/tmp/zsh赋予4755 set-UID权限后,使用普通用户执行后,用户依旧是普通用户权限。
这与普遍实验结果不一致(实验机器为manjaro),使用debian系统重新执行,发现运行后用户是有root权限的。可能manjaro对此进行了限制。
(b) Instead of copying /bin/zsh, this time, copy /bin/bash to /tmp, make it a set-root-uid program. Run /tmp/bash as a normal user. Will you get root privilege? Please describe and explain your observation.
可以发现即使bash命令设置为set-UID权限,普通用户执行后并没有root权限。
Task3: (Setup for the rest of the tasks) As you can find out from the previous task, /bin/bash has certain built-in protection that prevent the abuse of the Set-UID mechanism. To see the life before such a protection scheme was implemented, we are going to use a different shell program called /bin/zsh. In some Linux distributions (such as Fedora and Ubuntu), /bin/sh is actually a symbolic link to /bin/bash. To use zsh, we need to link /bin/sh to /bin/zsh. The following instructions describe how to change the default shell to zsh.
su
Password: (enter root password)
cd /bin
rm sh
ln -s zsh sh
Task4: The PATH environment variable.
The system(const char *cmd) library function can be used to execute a command within a program. The way system(cmd) works is to invoke the /bin/sh program, and then let the shell program to execute cmd. Because of the shell program invoked, calling system() within a Set-UID program is extremely dangerous. This is because the actual behavior of the shell
program can be affected by environment variables, such as PATH; these environment variables are under user’s control. By changing these variables, malicious users can control the behavior of the Set-UID program. The Set-UID program below is supposed to execute the /bin/ls command; however, the programmer only uses the relative path for the ls command, rather than the absolute path:
int main()
{
system("ls");
return 0;
}
(a) Can you let this Set-UID program (owned by root) run your code instead of /bin/ls? If you can, is your code running with the root privilege? Describe and explain your observations.
System函数调用shell路径是通过PATH来寻找的,我们可以更改PATH为我们想要的目录,而该目录中存放着我们伪造成ls恶意的shell。利用set-UID程序特性获得root权限。
(b) Now, change /bin/sh so it points back to /bin/bash, and repeat the above attack. Can you still get the root privilege? Describe and explain your observations.
执行后并未获得root权限。
Task5: The difference between system() and execve(). Before you work on this task, please make sure that /bin/sh is pointed to /bin/zsh.
Background: Bob works for an auditing agency, and he needs to investigate a company for a suspected fraud. For the investigation purpose, Bob needs to be able to read all the files in the company’s Unix system; on the other hand, to protect the integrity of the system, Bob should not be able to modify any file. To achieve this goal, Vince, the superuser of the system, wrote a special set-root-uid program (see below), and then gave the executable permission to Bob. This program requires Bob to type a file name at the command line, and then it will run /bin/cat to display the specified file. Since the program is running as a root, it can display any file Bob specifies. However, since the program has no write operations, Vince is very sure that Bob cannot use this special program to modify any file.
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *v[3];
if(argc < 2) {
printf("Please type a file name.\n");
return 1;
}
v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = 0;
/* Set q = 0 for Question a, and q = 1 for Question b */
int q = 0;
if (q == 0){
char *command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
sprintf(command, "%s %s", v[0], v[1]);
system(command);
}
else execve(v[0], v, 0);
return 0 ;
}
(a) Set q = 0 in the program. This way, the program will use system() to invoke the command. Is this program safe? If you were Bob, can you compromise the integrity of the system? For example, can you remove any file that is not writable to you? (Hint: remember that system() actually invokes /bin/sh, and then runs the command within the shell environment. We have tried the environment variable in the previous task; here let us try a different attack. Please pay attention to the special characters used in a normal shell environment).
虽然该程序对于Bob没有写权限,但是根据set-UID程序的特性,该程序在运行过程中拥有root的读写执行权限。此处漏洞类似于代码执行,利用system函数调用shell中分号的分割命令效果来绕过权限认证实现对文件的修改。
(b) Set q = 1 in the program. This way, the program will use execve() to invoke the command. Do your attacks in task (a) still work? Please describe and explain your observations.
当通过execve()函数进行系统调用的时候,该函数将我们参数都当作了文件名,所以无法更改。由于execve进行的是系统内核级的系统调用比system进行调用更加直接。
Task6: The LD PRELOAD environment variable.
To make sure Set-UID programs are safe from the manipulation of the LD PRELOAD environment variable, the runtime linker (ld.so) will ignore this environment variable if the program is a Set-UID root program, except for some conditions. We will figure out what these conditions are in this task.
(a) 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)
{
printf("I am not sleeping!\n");
}
(b) We can compile the above program using the following commands (in the -W1 argument, the third character is one, not ; in the -lc argment, the second character is ):
% gcc -fPIC -g -c mylib.c
% gcc -shared -Wl,-soname,libmylib.so.1 -o libmylib.so.1.0.1 mylib.o -lc
#需要注意的是-Wl的l不是数字1,而是字母l。
(c) Now, set the LD PRELOAD environment variable:
% export LD_PRELOAD=./libmylib.so.1.0.1
(d) Finally, compile the following program myprog (put this program in the same directory as libmylib.so.1.0.1):
/* myprog.c */
int main()
{
sleep(1);
return 0;
}
Please run myprog under the following conditions, and observe what happens. Based on your observations, tell us when the runtime linker will ignore the LD PRELOAD environment variable, and explain why.
• Make myprog a regular program, and run it as a normal user.
普通程序+普通用户,LD_RELOAD环境变量生效。
• Make myprog a Set-UID root program, and run it as a normal user.
SUID程序+普通用户,LD_RELOAD环境变量被忽略。
• Make myprog a Set-UID root program, and run it in the root account.
Root SUID程序+root,LD_RELOAD环境变量生效。
• Make myprog a Set-UID user1 program (i.e., the owner is user1, which is another user account), and run it as a different user (not-root user).
用户1 SUID程序+用户2,LD_RELOAD环境变量被忽略。
综上所述,我们可以得到一个结论,设置了SUID的程序被其他用户执行时,会忽略LD_RELOAD环境变量。而普通程序存在被hook的可能。
Task7: Relinquishing privileges and cleanup.
To be more secure, Set-UID programs usually call setuid() system call to permanently relinquish their root privileges. However, sometimes, this is not enough. Compile the following program, and make the program a set-root-uid program. Run it in a normal user account, and describe what you have observed. Will the file /etc/zzz be modified? Please explain your observation.
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
void main()
{
int fd;
/* Assume that /etc/zzz is an important system file,and it is owned by root with permission 0644 */
fd = open("/etc/zzz", O_RDWR | O_APPEND);
/* Simulate the tasks conducted by the program */
sleep(1);
/* After the task, the root privileges are no longer needed,it’s time to relinquish the root privileges permanently. */
setuid(getuid()); /* getuid() returns the real uid */
if (fork()) { /* In the parent process */
close (fd);
exit(0);
}
else { /* in the child process */
/* Now, assume that the child process is compromised, malicious attackers have injected the following statementsinto this process */
write (fd, "Malicious Data", 14);
close (fd);
}
}
由于不存在zzz文件,所以新建了一个zzz空文件。运行test程序后,/etc/zzz文件的内容被修改。使用setuid永久放弃root权限,子进程uid也变更为当前用户uid。此处子进程依旧能写入zzz文件的原因在于设置uid前,zzz文件就已经被打开了。只要将语句setuid(getuid())移至调用open函数之前,就能避免这个问题。