之前大二暑假开发了一款基于终端的webshell-controller;相应的代码开源选择了在中秋节前夕;在后期的比赛当中也展现出相应的风采,比如上海的极客谷杯中相应的数据库操作和反弹shell之类的操作完美施展,没有什么错误;在shell-hack中我个人加入相对比较重要的一些点是支持bypass disable_functions;
下面主要介绍两个bypass的原理实现;
shell-hack有多种bypass disable_functions的方法;我选取一部分的相关的实现原理和我开发的思路在此简要的记录;
先来看相应的bypass流程实现;
为了方便使用,我这直接将其bypass功能融合在一个选择器中;主要的原理其实也就是利用mail或者imap_mail或者error_log去进行相关底层getuid的劫持或者利用gcc拓展修饰符去劫持掉整个进程;因为mail和error_log又或者imap_mail其实现的效果是会去调用linux的sendmail命令,sendmail底层实现是有一步是调用C中的getuid函数进行实现;所以就可劫持;但是为了拓展攻击面,往往采用后者去利用gcc拓展修饰符劫持掉整个进程;
利用putenv函数设置LD_PRELOAD环境变量,加载恶意的拓展库;这里为了持续执行命令我设置的是直接开启新端口;新端口采用-c去指定相应的恶意ini执行;xxx函数的执行优先级高于main;在so被加载的时候会直接执行;又因为新进程最开始的第一步是需要去加载env;当解析到LD_PRELOAD的时候就会去相应的path下加载构造的恶意拓展库;所以xxx函数整体的执行优先级最高;
1 |
|
恶意ini获取的方式为以下两个方法:
1 | def test(self): |
通过php_ini_loaded_file函数拿到相应的ini路径,然后调用我设计的readfile接口去进行读取;有意思的是readfile接口我内部实现了四种读文件的方法;会根据相应的server环境去自动识别绕过一般的disable_function限制去读取;读取完之后将disable_functions配置注释掉即可;然后将其传入相应的server的/tmp下方便去包含启用恶意ini;
这里我之前看过蚁剑底层实现原理,蚁剑底层是直接采取-n不启用相应的ini;我当时开发的时候简单的翻了翻文档发现可以采用-c;去指定相应的运载恶意ini;这里就可达到相应的bypass;但是考虑到新开端口,流量无法传入进去;所以需要上一个流量转发脚本;shell-hack中也写好了一套流量转发可以将请求到s1mple.php的流量转发到相应的恶意server端口;
流量转发脚本的原理就是先使用$_SERVER全局数组去拿到相应的请求信息;然后过滤掉非必要信息,然后根据请求的method去进行流量包的构造;如果是post请求,就会去调用file_get_contents(‘php://input); 去读取到相应的post的数据
1 | $post_data = file_get_contents('php://input'); |
然后再将相应的http请求包加以整合,利用php中fsockopen函数去模仿http请求,将请求包发到指定的恶意port;
最后利用fread进行相关数据的读取;最后呈现到客户端;
众所周知,php代码真正执行的地方是在php-fpm中;当流量达到中间件的时候中间件会调用fast-cgi模块进行解析;用Fast-CGI协议重新封装,然后以相应的规定格式将数据传到后面默认监听在9000端口的php-fpm;php-fpm 据fast-cgi协议将TCP流解析成真正的数据,调用php文件;具体的说php-fpm有两个大进程,一个worker,一个master;master负责相应的监听,接收来自 Web Server 的请求;只有一个进程;
当拿到数据的时候就将相应的数据解析去调用fpm去进行相应的php文件的解析处理。有必要说的是php-fpm并非只是单独的一个。本质上可以将其简单的理解为一个进程池;其中有很多php-fpm进程,每个进程内部都嵌入了一个 PHP 解释器;如果在处理php的时候某个进程发生了崩溃也不会影响到其他的进程;
所以理解了这里相应的攻击思路就已经出来了;可以伪造相应的fastcgi模块解析直接和后面php-fpm进行通信;当然如果细致的划分的话具体还有两种多种利用方式;如果server端相应的设置不当,导致监听127.0.0.1:9000和0.0.0.0:9000混淆,就有可能导致fpm服务接口外漏;就可以直接进行和9000端口交互进行攻击;
P牛之前出过一个python的脚本,直接构造然后利用socket进行send数据进行交互;然后利用recv接受相关的返回数据然后进行解析;
有意思的是看一下p牛脚本中的构造数据属性
1 | params = { |
主要的实现点是在最后两个属性处;PHP_ADMIN_VALUE可以覆盖php.ini中的配置;SCRIPT_FILENAME用于指定一个执行的脚本文件;所以思路已经很清晰了,利用PHP_ADMIN_VALUE覆盖掉原本ini中的配置文件;将其修改为我们需要去加载的恶意点;
1 | 'PHP_ADMIN_VALUE' => "extension_dir = /tmp\nextension = s1mple.so", |
覆盖掉原本ini中的extension_dir配置和extension配置;将其设置为恶意的so库的path;所以导致php-fpm会去加载恶意的so拓展库;再加载恶意的拓展之后就自然开启了恶意的服务;所以配置SCRIPT_FILENAME这里设置值比较随意,但是这个字段是必要的;没有相应的字段会发生报错;所以我使用的时候直接将其赋值为了相应的php脚本文件(/tmp/s1mple.php);
执行完之后就会在恶意的port上开启一个使用相应/tmp/php.ini的server;但是这里我程序并没有上传相应的ini;所以-c为空;所以就不载入ini开启一个server;蚁剑的底层实现原理是利用-n去进行相应的不载入,效果和我载入为空的效果是一样的;贴一下蚁剑底层;
1 | let cmd = `${phpbinary} -n -S 127.0.0.1:${port} -t ${self.top.infodata.phpself}`; |
最后放一下shell-hack攻击的效果;
两种攻击方式简单介绍完毕,至于还有的攻击cgi_module执行cgi脚本之类的,相对不是很难,更包括php7之后的FFI利用方式;允许php中加载C代码;也可变形的绕过以实现执行C而不经过fpm从而绕过disable_functions;当然还有一些基于底层二进制的,偏移啥的23333;