一、LD_PRELOAD环境变量
介绍:在UNIX的动态链接库的世界中,LD_PRELOAD是这样一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库
例如:你自己设计了一个动态库文件(demo.so),这个动态链接库中你自已了一个pritnf函数,那么你将这个动态库文件设置到LD_PRELOAD环境变量之后,系统中运行的程序调用printf时就不会去glibc库中调用printf,而是去你自己定义的动态库文件(demo.so)中调用printf
使用方式:
使用时设置LD_PRELOAD环境变量:export LD_PRELOAD=绝对路径/xxx.so
使用完成之后取消LD_PRELOAD环境变量:unset LD_PRILOAD
LD_PRELOAD环境变量设置的两种模式:
①全局模式:设置LD_PRELOAD环境变量为全局模式,所有的程序都会使用LD_PRELOAD中指定的动态库,格式如下:
export LD_PRELOAD=绝对路径/xxx.so
②单程序模式1:单独为一个程序设置LD_PRELOAD环境变量,那么程序只在本次运行时调用LD_PRELOAD中指定的动态库,格式如下(备注:没有export命令了):
LD_PRELOAD=绝对路径/xxx.so 程序名 程序参数
③单程序模式2:使用putenv、setenv函数在程序运行时指定环境变量,此环境变量中含有LD_PRELOAD环境变量
putenv、setenv函数见文章:https://blog.csdn.net/qq_41453285/article/details/88965860
使用的注意事项:
指定的动态库路径必须为绝对路径,否则export LD_PRELOAD时会出错
有的程序可能会有一些安全机制,不调用LD_PRELOAD中指定的库
二、密码劫持演示案例(pass.c)
下面我们有一个pass.c密码认证程序,正确的密码保存在程序中。运行程序时让用户传入一个密码参数,然后程序将用户传入的密码参数与程序中原本存在的正确的密码作比较(使用strcmp函数),如果密码正确/错误都输出一些信息
另外,我们有一个parock.c程序,其中有一个自定义的strcmp函数,这个函数总是返回0,并且打印参数1。我们使用gcc将其编译为一个动态库,然后导入LD_PRELOAD环境变量中
劫持原理:将parock.so动态库导入到LD_PRELOAD环境变量中之后,pass程序去运行时,执行到strcmp函数时,就会去调用parock.so动态库中的strcmp函数,而不是去调用glibc中的strcmp函数。而parock.so中定义的strcmp函数总是返回0,因此pass.c在调用时总是匹配成功,因此pass.c程序总是通过
pass.c程序设计
#include <stdio.h>
#include <string.h>
int main(int argc,char*argv[])
{
char passwd[]="dongshao";
if(argc<2)
{
printf("usage:%s<password>\n",argv[0]);
return -1;
}
if(!strcmp(passwd,argv[1]))
{
printf("Welcome into process!\n");
return -1;
}
printf("Error password!\n");
return 0;
}
parock.c程序设计
#include <stdio.h>
#include <string.h>
int strcmp(const char *s1,const char *s2)
{
printf("password = <%s>\n",s1);
return 0;
}
演示案例:
①使用gcc编译我们的pass.c程序,并且运行时传入一个错误的参数,可以看到程序打印了错误的信息
gcc -g -o pass pass.c
./pass helloworld
②使用ldd命令查看一下我们的pass程序调用了哪些库文件
ldd pass
③使用gcc将我们的parock.c程序编译成动态库,动态库名为parock.so
gcc -fPIC -shared parock.c -o parock.so
④将我们的parock.so动态库导入LD_PRELOAD环境变量(动态库一定要指定绝对路径)
export LD_PRELOAD=/home/dongshao/code3/parock.so
⑤再次运行pass程序,此次传入一个错误的参数,可以看到pass程序还是打印了成功的信息,并且将程序中实际正确的密码打印到了控制台,这就是劫持系统调用的结果
./pass helloworld
⑥使用ldd命令查看一下我们的pass程序调用了哪些库文件,可以看到ldd命令出错了(因为系统命令可能也优先调用了我们自己定义的库,出错了)
ldd pass
⑦并且系统中其它命令都出错了(系统出现了异常)
⑧使用unset命令取消LD_PRELOAD环境变量,上面的异常就消失了
unset LD_PRELOAD
备注:
上面是设置全局的LD_PRELOAD环境变量,不仅pass程序,其他程序调用strcmp函数时,都会调用我们的parock.so动态库中的strcmp函数。下面演示在单次运行程序时指定LD_PRELOAD环境变量,可以看到达到预期效果
LD_PRELOAD=/home/dongshao/code3/parock.so ./pass HelloWorld
三、权限劫持演示案例(rootkitt.c)
geteuid、getuid、getgid函数介绍:https://blog.csdn.net/qq_41453285/article/details/88976168
我们自己改写geteuid、getuid、getgid这些系统调用,使其返回值都为0(管理员权限),这样有程序调用这些函数时,就会返回自己拥有了管理员权限(备注:这些程序并不是真的拥有管理员权限,但是在某些管理员权限值查看方面发现自己变成了管理员)
rootkitt.c程序设计
#include <unistd.h>
#include <sys/types.h>
uid_t geteuid(void){return 0;}
uid_t getuid(void){return 0;}
uid_t getgid(void){return 0;}
演示案例
①先执行id命令,发现自己不是管理员权限
②使用gcc将我们的rootkit.c程序编译成动态库,动态库名为rootkit.so
gcc -fPIC -shared rootkit.c -o rootkit.so
③将我们的rootkit.so动态库导入LD_PRELOAD环境变量(动态库一定要指定绝对路径)
export LD_PRELOAD=/home/dongshao/code3/rootkit.so
④再次执行id命令,发现自己已经管理员权限
⑤使用unset命令取消LD_PRELOAD环境变量
unset LD_PRILOAD
⑥出于某种机制,即使取消了LD_PRELOAD环境变量,id命令还是显示自己为管理员(目前还不知道为什么)
⑦在单次调用id命令中使用LD_PRELOAD环境变量
LD_PRELOAD=/home/dongshao/code3/rootkit.so id