您当前的位置: 首页 > 

合天网安实验室

暂无认证

  • 0浏览

    0关注

    748博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

通过LD_PRELOAD绕过disable_functions

合天网安实验室 发布时间:2019-10-14 10:35:34 ,浏览量:0

0x00 前言

前段时间碰到拿到shell以后限制了basedir并且无法执行命令的情况,解决办法是上传恶意的.so文件,并通过设置LD_PRELOAD,然后调用新进程来加载恶意.so文件,达到绕过的效果。当时做这道题目的时候是跟着别人的题解直接套的(一道疯狂bypass的题目),属于一知半解的状态,比赛结束之后又耽搁了一两天,才有时间总结学习以下这个方式。

0x01 学习

链接

在学习LD_PRELOAD之前需要了解什么是链接。

程序的链接主要有以下三种:

  1. 静态链接:在程序运行之前先将各个目标模块以及所需要的库函数链接成一个完整的可执行程序,之后不再拆开。

  2. 装入时动态链接:源程序编译后所得到的一组目标模块,在装入内存时,边装入边链接。

  3. 运行时动态链接:原程序编译后得到的目标模块,在程序执行过程中需要用到时才对它进行链接。

对于动态链接来说,需要一个动态链接库,其作用在于当动态库中的函数发生变化对于可执行程序来说时透明的,可执行程序无需重新编译,方便程序的发布/维护/更新。但是由于程序是在运行时动态加载,这就存在一个问题,假如程序动态加载的函数是恶意的,就有可能导致disable_function被绕过。

LD_PRELOAD介绍

在UNIX的动态链接库的世界中,LD_PRELOAD就是这样一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入恶意程序,从而达到那不可告人的罪恶的目的。

为什么可以绕过

想要利用LD_PRELOAD环境变量绕过disable_functions需要注意以下几点:

  1. 能够上传自己的.so文件

  2. 能够控制环境变量的值(设置LD_PRELOAD变量),比如putenv函数

  3. 存在可以控制PHP启动外部程序的函数并能执行(因为新进程启动将加载LD_PRELOAD中的.so文件),比如mail()、imap_mail()、mb_send_mail()和error_log()等

首先,我们能够上传恶意.so文件,.so文件由攻击者在本地使用与服务端相近的系统环境进行编译,该库中重写了相关系统函数,重写的系统函数能够被PHP中未被disable_functions禁止的函数所调用。

当我们能够设置环境变量,比如putenv函数未被禁止,我们就可以把LD_PRELOAD变量设置为恶意.so文件的路径,只要启动新的进程就会在新进程运行前优先加载该恶意.so文件,由此,恶意代码就被注入到程序中。

当执行未被禁止的PHP函数,并且该函数调用了恶意库中重写的系统函数,就可以达到任意执行系统命令的效果了,因为重写的系统函数中的内容是我们可控的,对这部分内容进行编程即可。

PHP中某些函数比如mail()函数调用时,就会调用系统中的sendmail函数,由于LD_PRELOAD中指定加载了恶意的.so文件中覆盖了sendmail函数,所以就会执行重写的sendmail函数中的恶意代码,从而绕过disable_functions,达到任意执行系统命令的效果。

0x02 实践

在这么一个环境中,执行命令的相关函数被禁止

假如直接执行,会报错

test.php

在浏览器中访问 webshell 就可以执行我们预期的语句了。按照此理,我们可以执行任意特定权限的命令。

这里我的ubuntu是没有安装sendmail的,但同样执行成功。一开始不知道怎么找原因,后面感谢我舍友的指点才发现。

在.c中加入死循环来判断到底哪个函数调用了geteuid()

test.c

#include 
#include 
#include 
void payload() {
        system("touch /var/www/html/success");
}   
int  geteuid() {
 if (getenv("LD_PRELOAD") == NULL) { return 0; }
 while(1){}
 unsetenv("LD_PRELOAD");
 payload();
}

可以在top中看到当前占用最高的一项是其实是/bin/sh,因为死循环一直卡着。

是由test.php 派生出来的,因此我没有安装sendmail也执行成功的原因是sh同样调用了geteuid函数

0x03 通用化

回到 LD_PRELOAD 本身,系统通过它预先加载共享对象,如果能找到一个方式,在加载时就执行代码,而不用考虑劫持某一系统函数,比如geteuid()。

在GCC 有个 C 语言扩展修饰符 __attribute__((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行__attribute__((constructor)) 修饰的函数。

__attribute__((constructor))
constructor参数让系统执行main()函数之前调用函数(被__attribute__((constructor))修饰的函数)
__attribute__((destructor))
destructor参数让系统在main()函数退出或者调用了exit()之后,(被__attribute__((destructor))修饰的函数)

因此,只要php中设置了LD_PRELOAD,并派生了新的进程,将会执行LD_PRELOAD的文件中__attribute__((constructor))里的函数

test3.c

#include 
#include 
__attribute__((constructor))void payload() {
    unsetenv("LD_PRELOAD");
    const char* cmd = getenv("CMD");
    system(cmd);
}

test2.php


可以看到执行成功。

0x03 参考链接

https://www.anquanke.com/post/id/175403

https://www.smi1e.top/php-bypass-disabled_functions/

别忘了投稿哦

大家有好的技术原创文章

欢迎投稿至邮箱:edu@heetian.com

合天会根据文章的时效、新颖、文笔、实用等多方面评判给予200元-800元不等的稿费哦

有才能的你快来投稿吧!

了解投稿详情点击——重金悬赏 | 合天原创投稿涨稿费啦!

关注
打赏
1665306545
查看更多评论
立即登录/注册

微信扫码登录

0.0443s