*本文原创作者:yangyangwithgnu,本文属FreeBuf原创奖励计划,未经许可禁止转载

摘要:千辛万苦拿到的 webshell 居然无法执行系统命令,怀疑服务端 disable_functions 禁用了命令执行函数,通过环境变量 LD_PRELOAD 劫持系统函数,却又发现目标根本没安装 sendmail,无法执行命令的 webshell 是无意义的,看我如何突破!

半月前逛“已黑网站列表”时复审一小电商网站,“列表”中并未告知漏洞详情,简单浏览了下功能,只有注册、登录、下单、支付等几个而已。登录接口中,找到个 RCE(远程代码执行,非远程命令执行)漏洞:

顺势写入菜刀马后连接:

习惯上,getshell 后我会先了解下该系统配置,虚拟终端中执行 cat /proc/meminfo 但执行报错:

怀疑有 WAF 拦劫了待执行的命令,尝试了空字符串、路径扩展、自定义变量平时常用的几种绕命令执行限制的手法,结果都失败:

无命令执行功能的 webshell 是无意义的,得突破!

通常来说,导致 webshell 不能执行命令的原因大概有三类:一是 php.ini 中用 disable_functions 指示器禁用了 system()、exec() 等等这类命令执行的相关函数;二是 web 进程运行在 rbash 这类受限 shell 环境中;三是 WAF 拦劫。若是一则无法执行任何命令,若是二、三则可以执行少量命令。从当前现象来看,很可能由 disable_functions 所致。为验证,我利用前面的 RCE 漏洞执行 phpinfo(),确认的确如此:

有四种绕过 disable_functions 的手法:第一种,攻击后端组件,寻找存在命令注入的、web 应用常用的后端组件,如,ImageMagick 的魔图漏洞、bash 的破壳漏洞;第二种,寻找未禁用的漏网函数,常见的执行命令的函数有 system()、exec()、shell_exec()、passthru(),偏僻的 popen()、proc_open()、pcntl_exec(),逐一尝试,或许有漏网之鱼;第三种,mod_cgi 模式,尝试修改 .htaccess,调整请求访问路由,绕过 php.ini 中的任何限制;第四种,利用环境变量 LD_PRELOAD 劫持系统函数,让外部程序加载恶意 *.so,达到执行系统命令的效果。

尝试第一种时,我用 phpinfo() 查看 ImageMagick 版本为 v6.9.4-10:

用 searchsploit(exploit-db.com 的本地版)搜索存在命令注入的版本为 v6.9.3-9 或 v7.0.1-0:

显然,当前 ImageMagick 无法利用;尝试第二种时,常见的、不常见的、罕见的(如 dl()),所有可启动进程的函数均被禁用;尝试第三种时,发现并未启用 mod_cgi 模式。所有希望,寄托在 LD_PRELOAD。

设想这样一种思路:利用漏洞控制 web 启动新进程 a.bin(即便进程名无法让我随意指定),a.bin 内部调用系统函数 b(),b() 位于系统共享对象 c.so 中,所以系统为该进程加载共 c.so,我想法在 c.so 前优先加载可控的 c_evil.so,c_evil.so 内含与 b() 同名的恶意函数,由于 c_evil.so 优先级较高,所以,a.bin 将调用到 c_evil.so 内 b() 而非系统的 c.so 内 b(),同时,c_evil.so 可控,达到执行恶意代码的目的。基于这一思路,将突破 disable_functions 限制执行操作系统命令这一目标,大致分解成几步在本地推演:查看进程调用系统函数明细、操作系统环境下劫持系统函数注入代码、找寻内部启动新进程的 PHP 函数、PHP 环境下劫持系统函数注入代码。

查看进程调用系统函数明细。linux 创建新进程的过程较为复杂,我关心进程加载了哪些共享对象、可能调用哪些 API、实际调用了哪些 API。比如,运行 /usr/bin/id,通过 ldd 可查看系统为其加载的共享对象:

由于可执行文件 /usr/bin/id 内含符号表,所以,运行 nm -D /usr/bin/id 2>&1 或 readelf -Ws /usr/bin/id 可查看该程序可能调用的系统 API 明细:

由于程序运行时会根据命令行选项、运行环境作出不同反应,导致真正运行时调用的 API 可能只是 readefl 查看的子集,你可以运行 strace -f /usr/bin/id 2>&1 跟踪实际 API 调用情况,比如,实际调用 open() 的入参、返回值一目了然:

操作系统环境下劫持系统函数注入代码。linux 的环境变量 LD_PRELOAD 是一种类似 win32 API hook 的更优雅的实现,适用于打热补丁、读取进程空间数据、禁止程序调用指定 API、调试程序等等场景,甚至可以在不更改原始可执行文件前提下植入后门(管理员常用的 /bin/ps)。由于被劫持的系统函数得由我们重新实现一次,函数原型必须一致,为减少复杂性,我会选择劫持那些无参数且常用的系统函数,getuid() 就适合,以此为例,完整劫持过程步骤大致如下:首先,用 man 2 getuid 查看函数原型:

然后,编写同原型的 getuid() 函数,保存至 getuid_shadow.c,源码为:

执行 gcc -shared -fPIC getuid_shadow.c -o getuid_shadow.so 将其编译为共享对象:

最后,借助环境变量 LD_PRELOAD 劫持系统函数 getuid(),获取控制权。执行 LD_PRELOAD=/root/getuid_shadow.so /usr/bin/id,注入代码成功执行:

注意,LD_PRELOAD 是进程独占环境变量,类似于命令适配器,它与待执行命令间必须为空白字符,而非命令分隔符(;、&&、||)。

找寻内部启动新进程的 PHP 函数。虽然 LD_PRELOAD 为我提供了劫持系统函数的能力,但前提是我得控制 php 启动外部程序才行(只要有进程启动行为即可,无所谓是谁)。常见的 system() 启动程序方式显然不行,否则就不存在突破 disable_functions 一事了。PHP 脚本中除了调用 system()、exec()、shell_exec() 等等一堆 php 函数外,还有哪种可能启动外部程序呢?php 解释器自身!比如,php 函数 goForward() 实现“前进”的功能,php 函数 goForward() 又由组成 php 解释器的 C 语言模块之一的 move.c 实现,C 模块 move.c 内部又通过调用外部程序 go.bin 实现,那么,我的 php 脚本中调用了函数 goForward(),势必启动外部程序 go.bin。现在,我需要找到类似 goForward() 的真实存在的 PHP 函数。印象中,处理图片、请求网页、发送邮件等三类场景中可能存在我想要的函数,我得逐一验证。处理图片,通常调用 PHP 封装的 ImageMagick 库,新建 image.php,调用 Imagick():

运行 strace -f php image.php 2>&1 | grep -A2 -B2 execve 查看 Imagick() 是否启动新进程:

第一个 execve 是启动 PHP 解释器而已,必须找到第二个 execve,没有则说明并未启动新进程;请求网页,新建 http.php,调用 curl_init():

运行 strace -f php http.php 2>&1 | grep -A2 -B2 execve 查看 curl_init() 是否启动新进程:

仍然不是我要的;发送邮件,新建 mail.php,调用 mail():

运行 strace -f php mail.php 2>&1 | grep -A2 -B2 execve 查看 mail() 是否启动新进程:bingo!mail() 内部启动了 /usr/sbin/sendmail、/usr/sbin/postdrop 两个新进程,它就是我一直苦寻的函数(用相同的测试方式,还找到一个 imap_mail())。

PHP 环境下劫持系统函数注入代码。mail.php 内增加设置 LD_PRELOAD 的代码:

然后将 mail.php 以及内含 mail() 函数的共享对象 getuid_shadow.so 放入 web 目录 /var/www/:

执行 mail.php 之后,找到 getuid_shadow.so 中 mail() 创建的文件 /tmp/evil,成功在 PHP 环境下不借助任何 PHP 命令执行函数执行命令:

有了前面的分析,看我如何在目标站点绕过 disable_functions 执行系统命令。

首先,基于前面的 mail.php 写了个小马 bypass_disablefunc.php:

bypass_disablefunc.php 提供三个 GET 参数。一是 cmd 参数,待执行的系统命令(如 pwd);二是 outpath 参数,保存命令执行输出结果的文件路径(如 /tmp/xx),便于在页面上显示,另外关于该参数,你应注意 web 是否有读写权限、web 是否可跨目录访问、文件将被覆盖和删除等几点;三是 sopath 参数,指定劫持系统函数的共享对象的绝对路径(如 /var/www/bypass_disablefunc_x64.so),另外关于该参数,你应注意 web 是否可跨目录访问到它。此外,bypass_disablefunc.php 拼接命令和输出路径成为完整的命令行,所以你不用在 cmd 参数中重定向了:

$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";

同时,通过环境变量 EVIL_CMDLINE 向 bypass_disablefunc_x64.so 传递具体执行的命令行信息:

putenv("EVIL_CMDLINE=" . $evil_cmdline);

然后,基于 getuid_shadow.c 编写劫持函数的代码 bypass_disablefunc.c。回想下,先前我之所以劫持 getuid(),是因为 sendmail 程序会调用该函数,在真实环境中,存在两方面问题:一是,某些环境中,web 禁止启用 senmail、甚至系统上根本未安装 sendmail,也就谈不上劫持 getuid(),通常的 www-data 权限又不可能去更改 php.ini 配置、去安装 sendmail 软件;二是,即便目标可以启用 sendmail,由于未将主机名(hostname 输出)添加进 hosts 中,导致每次运行 sendmail 都要耗时半分钟等待域名解析超时返回,www-data 也无法将主机名加入 hosts(如,127.0.0.1    lamp、lamp.、lamp.com)。基于这两个原因,我不得不放弃劫持函数 getuid(),必须找个更普适的方法。回到 LD_PRELOAD 本身,系统通过它预先加载共享对象,如果能找到一个方式,在加载时就执行代码,而不用考虑劫持某一系统函数,那我就完全可以不依赖 sendmail 了。这种场景与 C++ 的构造函数简直神似!几经搜索后了解到,GCC 有个 C 语言扩展修饰符 __attribute__((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 __attribute__((constructor)) 修饰的函数。强调下,这一细节非常重要,很多朋友用 LD_PRELOAD 手法突破 disable_functions 无法做到百分百成功,正因为这个原因,我们不要局限于仅劫持某一函数,而应考虑劫持共享对象。bypass_disablefunc.c 源码如下:

从环境变量 EVIL_CMDLINE 中接收 bypass_disablefunc.php 传递过来的待执行的命令行。

接着,用命令 gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.so 将 bypass_disablefunc.c 编译为共享对象 bypass_disablefunc_x64.so:

你要根据目标架构编译成不同版本,在 x64 的环境中编译,若不带编译选项则默认为 x64,若要编译成 x86 架构需要加上 -m32 选项。

最后,用菜刀将 bypass_disablefunc.php 和 bypass_disablefunc_x64.so 传到目标:

指定好命令输出路径、共享对象路径后,在 bypass_disablefunc.php 上再次执行先前失败的命令 cat /proc/meminfo:

啊哈!很酷对不对。

好了,巧用 LD_PRELOAD 突破 disable_functions 的手法就是这样子,唯一条件,PHP 支持putenv()、mail() 即可,甚至无需安装 sendmail。那么,现在的情况是,我知道你很忙,没时间看前面的技术细节,要的只是开箱即用的工具。行,bypass_disablefunc.php、bypass_disablefunc.c、bypass_disablefunc_x64.so 托管在 https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD,自取

无需sendmail:巧用LD_PRELOAD突破disable_functions的更多相关文章

  1. [转+自]disable_functions之巧用LD_PRELOAD突破

    写在前面: 通过知乎的一篇艰难的渗透提权,引发了一些对于disable_funcionts绕过的思考,虽然在暑假日记中记载了四种绕过disable_functions,比如com组件,pcntl_ex ...

  2. 通过LD_PRELOAD绕过disable_functions

    今天做靶场时遇到了一个情形:拿到了webshell,却不能执行任何命令,如图 后来百度知道了disable_functions功能,这类服务器针对命令执行函数做了防范措施 一般绕过思路是利用漏掉的函数 ...

  3. PHP利用pcntl_exec突破disable_functions

    http://fuck.0day5.com/?p=563 PHP突破Disable_functions执行Linux命令 利用dl函数突破disable_functions执行命令 http://ww ...

  4. LD_PRELOAD & putenv() 绕过 disable_functions & open_basedir

    这次TCTF中一道题,给出了一个PHP一句话木马,设置了open_basedir,disable_functions包含所有执行系统命令的函数,然后目标是运行根目录下的/readflag,目标很明确, ...

  5. 刷题记录:[SUCTF 2019]EasyWeb(EasyPHP)

    目录 刷题记录:[SUCTF 2019]EasyWeb(EasyPHP) 一.涉及知识点 1.无数字字母shell 2.利用.htaccess上传文件 3.绕过open_basedir/disable ...

  6. Disable_functions绕过整合

    转载 https://whoamianony.top/2021/03/13/Web安全/Bypass Disable_functions/ https://www.mi1k7ea.com/2019/0 ...

  7. 关于 PHP 开放 enable_dl 函数利用 图片Disable_functions

    三篇引文地址: 利用PHP扩展模块突破Disable_functions执行命令 如何编写PHP扩展 php dl函数(详细解释)

  8. bypass disable_function总结学习

    通常bypass的思路如下 1. 攻击后端组件,寻找存在命令注入的.web 应用常用的后端组件,如,ImageMagick 的魔图漏洞.bash 的破壳漏洞 2. 寻找未禁用的漏网函数,常见的执行命令 ...

  9. PHP绕过disable_function

    PHP绕过disable_function 常规绕过 exec exec执行command命令,但是不会输出全部结果,而是返回结果的最后一行. 想得到全部的结果,可以使用第二个参数,让其输出到一个数组 ...

随机推荐

  1. sqlserver 获取汉字拼音的首字母(大写)函数

    1:创建函数: USE [test] GO /****** 对象: UserDefinedFunction [dbo].[GetFirstChar] 脚本日期: 02/22/2019 16:39:06 ...

  2. C# 登陆验证码工具类VerifyCode

    using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; ...

  3. 树链剖分——线段树区间合并bzoj染色

    线段树区间合并就挺麻烦了,再套个树链就更加鬼畜,不过除了代码量大就没什么其他的了.. 一些细节:线段树每个结点用结构体保存,pushup等合并函数改成返回一个结构体,这样好写一些 struct Seg ...

  4. UNIX哲理名言(中英文对照)

    UNIX 的特点: Everything (including hardware) is a file.所有的事物(甚至硬件本身)都是一个的文件. Configuration data stored ...

  5. 学习日志1 :java 三大框架 了解

    1.spring--------利用它的IOC和AOP来处理控制业务(负责对数据库的操作) https://blog.csdn.net/yu616568/article/details/7073997 ...

  6. TCP和UDP的区别以及使用python服务端客户端简单编程

    一.TCP.UDP区别总结 1.TCP面向连接(如打电话要先拨号建立连接):UDP是无连接的,即发送数据之前不需要建立连接 2.TCP提供可靠的服务,也就是说,通过TCP连接传送的数据,无差错,不丢失 ...

  7. 使用Anaconda操作numpy库和matplotlib图形库

    慢慢来~~~ import numpy as np import matplotlib.pyplot as plt # 生成数据 x = np.arange(0, 6, 0.1) # 以0.1为单位, ...

  8. Win7共享文件夹简单?这个共享问题可以难倒90%的人

    信息化社会,没有哪个公司不用电脑办公了.一个办公室里面的同事相互之间利用系统的共享功能,共享一些文件和软件已经是司空见惯的了,这个不需要多么复杂的操作.我们使用最多的windows7操作系统就能很方便 ...

  9. 局域网IP地址

    A类: 10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址). 127.X.X.X是保留地址,用做循环测试用的. B类: 172.16.0.0---172.31.255 ...

  10. Python 常用Web框架的比较

    转载来自:https://www.cnblogs.com/sunshine-1/p/7372934.html 从GitHub中整理出的15个最受欢迎的Python开源框架.这些框架包括事件I/O,OL ...